commit 9c3067633241f04995655448fd286c38494a10a3 Author: CI Date: Mon Jun 24 08:40:34 2024 +0000 Build branch main with version main (d0c648f) Build pipeline: vsh-ci-template-p9886 Source commit: https://github.com/viash-hub/biobox/commit/d0c648fb7eefe067f5b5b3d402a204354bb37198 Source message: Delete src/bgzip directory (#64) It was moved to toolbox diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ca5262bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*.DS_Store +*__pycache__ + +# IDE ignores +.idea/ + +# R specific ignores +.Rhistory +.Rproj.user +*.Rproj + +# viash specific ignores +target/ + +# nextflow specific ignores +.nextflow* +work diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..c89bb9b4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,81 @@ +# biobox unreleased + +## BREAKING CHANGES + +* Change default `multiple_sep` to `;` (PR #25). This aligns with an upcoming breaking change in + Viash 0.9.0 in order to avoid issues with the current default separator `:` unintentionally + splitting up certain file paths. + +## NEW FEATURES + +* `arriba`: Detect gene fusions from RNA-seq data (PR #1). + +* `fastp`: An ultra-fast all-in-one FASTQ preprocessor (PR #3). + +* `busco`: + - `busco/busco_run`: Assess genome assembly and annotation completeness with single copy orthologs (PR #6). + - `busco/busco_list_datasets`: Lists available busco datasets (PR #18). + - `busco/busco_download_datasets`: Download busco datasets (PR #19). + +* `cutadapt`: Remove adapter sequences from high-throughput sequencing reads (PR #7). + +* `featurecounts`: Assign sequence reads to genomic features (PR #11). + +* `bgzip`: Add bgzip functionality to compress and decompress files (PR #13). + +* `pear`: Paired-end read merger (PR #10). + +* `lofreq/call`: Call variants from a BAM file (PR #17). + +* `lofreq/indelqual`: Insert indel qualities into BAM file (PR #17). + +* `multiqc`: Aggregate results from bioinformatics analyses across many samples into a single report (PR #42). + +* `star`: + - `star/star_align_reads`: Align reads to a reference genome (PR #22). + - `star/star_genome_generate`: Generate a genome index for STAR alignment (PR #58). + +* `gffread`: Validate, filter, convert and perform other operations on GFF files (PR #29). + +* `salmon`: + - `salmon/salmon_index`: Create a salmon index for the transcriptome to use Salmon in the mapping-based mode (PR #24). + - `salmon/salmon_quant`: Transcript quantification from RNA-seq data (PR #24). + +* `samtools`: + - `samtools/samtools_flagstat`: Counts the number of alignments in SAM/BAM/CRAM files for each FLAG type (PR #31). + - `samtools/samtools_idxstats`: Reports alignment summary statistics for a SAM/BAM/CRAM file (PR #32). + - `samtools/samtools_index`: Index SAM/BAM/CRAM files (PR #35). + - `samtools/samtools_sort`: Sort SAM/BAM/CRAM files (PR #36). + - `samtools/samtools_stats`: Reports alignment summary statistics for a BAM file (PR #39). + - `samtools/samtools_faidx`: Indexes FASTA files to enable random access to fasta and fastq files (PR #41). + - `samtools/samtools_collate`: Shuffles and groups reads in SAM/BAM/CRAM files together by their names (PR #42). + - `samtools/samtools_view`: Views and converts SAM/BAM/CRAM files (PR #48). + - `samtools/samtools_fastq`: Converts a SAM/BAM/CRAM file to FASTQ (PR #52). + +* `falco`: A C++ drop-in replacement of FastQC to assess the quality of sequence read data (PR #43). + +* `bedtools`: + - `bedtools_getfasta`: extract sequences from a FASTA file for each of the + intervals defined in a BED/GFF/VCF file (PR #59). + +## MINOR CHANGES + +* Uniformize component metadata (PR #23). + +* Update to Viash 0.8.5 (PR #25). + +* Update to Viash 0.9.0-RC3 (PR #51). + +* Update to Viash 0.9.0-RC6 (PR #63). + +* Switch to viash-hub/toolbox actions (PR #64). + +## DOCUMENTATION + +* Update README (PR #64). + +## BUG FIXES + +* Add escaping character before leading hashtag in the description field of the config file (PR #50). + +* Format URL in biobase/bcl_convert description (PR #55). \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..7393bc7e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,383 @@ + +# Contributing guidelines + +We encourage contributions from the community. To contribute: + +1. **Fork the Repository**: Start by forking this repository to your account. +2. **Develop Your Component**: Create your Viash component, ensuring it aligns with our best practices (detailed below). +3. **Submit a Pull Request**: After testing your component, submit a pull request for review. + +## Procedure of adding a component + +### Step 1: Find a component to contribute + +* Find a tool to contribute to this repo. + +* Check whether it is already in the [Project board](https://github.com/orgs/viash-hub/projects/1). + +* Check whether there is a corresponding [Snakemake wrapper](https://github.com/snakemake/snakemake-wrappers/blob/master/bio) or [nf-core module](https://github.com/nf-core/modules/tree/master/modules/nf-core) which we can use as inspiration. + +* Create an issue to show that you are working on this component. + + +### Step 2: Add config template + +Change all occurrences of `xxx` to the name of the component. + +Create a file at `src/xxx/config.vsh.yaml` with contents: + +```yaml +name: xxx +description: xxx +keywords: [tag1, tag2] +links: + homepage: yyy + documentation: yyy + issue_tracker: yyy + repository: yyy +references: + doi: 12345/12345678.yz +license: MIT/Apache-2.0/GPL-3.0/... +argument_groups: + - name: Inputs + arguments: <...> + - name: Outputs + arguments: <...> + - name: Arguments + arguments: <...> +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - <...> +runners: + - type: executable + - type: nextflow +``` + +### Step 3: Fill in the metadata + +Fill in the relevant metadata fields in the config. Here is an example of the metadata of an existing component. + +```yaml +functionality: + name: arriba + description: Detect gene fusions from RNA-Seq data + keywords: [Gene fusion, RNA-Seq] + links: + homepage: https://arriba.readthedocs.io/en/latest/ + documentation: https://arriba.readthedocs.io/en/latest/ + repository: https://github.com/suhrig/arriba + issue_tracker: https://github.com/suhrig/arriba/issues + references: + doi: 10.1101/gr.257246.119 + bibtex: | + @article{ + ... a bibtex entry in case the doi is not available ... + } + license: MIT +``` + +### Step 4: Find a suitable container + +Google `biocontainer ` and find the container that is most suitable. Typically the link will be `https://quay.io/repository/biocontainers/xxx?tab=tags`. + +If no such container is found, you can create a custom container in the next step. + + +### Step 5: Create help file + +To help develop the component, we store the `--help` output of the tool in a file at `src/xxx/help.txt`. + +````bash +cat < src/xxx/help.txt +```sh +xxx --help +``` +EOF + +docker run quay.io/biocontainers/xxx:tag xxx --help >> src/xxx/help.txt +```` + +Notes: + +* This help file has no functional purpose, but it is useful for the developer to see the help output of the tool. + +* Some tools might not have a `--help` argument but instead have a `-h` argument. For example, for `arriba`, the help message is obtained by running `arriba -h`: + + ```bash + docker run quay.io/biocontainers/arriba:2.4.0--h0033a41_2 arriba -h + ``` + + +### Step 6: Create or fetch test data + +To help develop the component, it's interesting to have some test data available. In most cases, we can use the test data from the Snakemake wrappers. + +To make sure we can reproduce the test data in the future, we store the command to fetch the test data in a file at `src/xxx/test_data/script.sh`. + +```bash +cat < src/xxx/test_data/script.sh + +# clone repo +if [ ! -d /tmp/snakemake-wrappers ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers /tmp/snakemake-wrappers +fi + +# copy test data +cp -r /tmp/snakemake-wrappers/bio/xxx/test/* src/xxx/test_data +EOF +``` + +The test data should be suitable for testing this component. Ensure that the test data is small enough: ideally <1KB, preferably <10KB, if need be <100KB. + +### Step 7: Add arguments for the input files + +By looking at the help file, we add the input arguments to the config file. Here is an example of the input arguments of an existing component. + +For instance, in the [arriba help file](src/arriba/help.txt), we see the following: + + Usage: arriba [-c Chimeric.out.sam] -x Aligned.out.bam \ + -g annotation.gtf -a assembly.fa [-b blacklists.tsv] [-k known_fusions.tsv] \ + [-t tags.tsv] [-p protein_domains.gff3] [-d structural_variants_from_WGS.tsv] \ + -o fusions.tsv [-O fusions.discarded.tsv] \ + [OPTIONS] + + -x FILE File in SAM/BAM/CRAM format with main alignments as generated by STAR + (Aligned.out.sam). Arriba extracts candidate reads from this file. + +Based on this information, we can add the following input arguments to the config file. + +```yaml +argument_groups: + - name: Inputs + arguments: + - name: --bam + alternatives: -x + type: file + description: | + File in SAM/BAM/CRAM format with main alignments as generated by STAR + (Aligned.out.sam). Arriba extracts candidate reads from this file. + required: true + example: Aligned.out.bam +``` + +Check the [documentation](https://viash.io/reference/config/functionality/arguments) for more information on the format of input arguments. + +Several notes: + +* Argument names should be formatted in `--snake_case`. This means arguments like `--foo-bar` should be formatted as `--foo_bar`, and short arguments like `-f` should receive a longer name like `--foo`. + +* Input arguments can have `multiple: true` to allow the user to specify multiple files. + + + +### Step 8: Add arguments for the output files + +By looking at the help file, we now also add output arguments to the config file. + +For example, in the [arriba help file](src/arriba/help.txt), we see the following: + + + Usage: arriba [-c Chimeric.out.sam] -x Aligned.out.bam \ + -g annotation.gtf -a assembly.fa [-b blacklists.tsv] [-k known_fusions.tsv] \ + [-t tags.tsv] [-p protein_domains.gff3] [-d structural_variants_from_WGS.tsv] \ + -o fusions.tsv [-O fusions.discarded.tsv] \ + [OPTIONS] + + -o FILE Output file with fusions that have passed all filters. + + -O FILE Output file with fusions that were discarded due to filtering. + +Based on this information, we can add the following output arguments to the config file. + +```yaml +argument_groups: + - name: Outputs + arguments: + - name: --fusions + alternatives: -o + type: file + direction: output + description: | + Output file with fusions that have passed all filters. + required: true + example: fusions.tsv + - name: --fusions_discarded + alternatives: -O + type: file + direction: output + description: | + Output file with fusions that were discarded due to filtering. + required: false + example: fusions.discarded.tsv +``` + +Note: + +* Preferably, these outputs should not be directores but files. For example, if a tool outputs a directory `foo/` containing files `foo/bar.txt` and `foo/baz.txt`, there should be two output arguments `--bar` and `--baz` (as opposed to one output argument which outputs the whole `foo/` directory). + +### Step 9: Add arguments for the other arguments + +Finally, add all other arguments to the config file. There are a few exceptions: + +* Arguments related to specifying CPU and memory requirements are handled separately and should not be added to the config file. + +* Arguments related to printing the information such as printing the version (`-v`, `--version`) or printing the help (`-h`, `--help`) should not be added to the config file. + + +### Step 10: Add a Docker engine + +To ensure reproducibility of components, we require that all components are run in a Docker container. + +```yaml +engines: + - type: docker + image: quay.io/biocontainers/xxx:0.1.0--py_0 +``` + +The container should have your tool installed, as well as `ps`. + +If you didn't find a suitable container in the previous step, you can create a custom container. For example: + +```yaml +engines: + - type: docker + image: python:3.10 + setup: + - type: python + packages: numpy +``` + +For more information on how to do this, see the [documentation](https://viash.io/guide/component/add-dependencies.html#steps-for-creating-a-custom-docker-platform). + +Here is a list of base containers we can recommend: + +* Bash: [`bash`](https://hub.docker.com/_/bash), [`ubuntu`](https://hub.docker.com/_/ubuntu) +* C#: [`ghcr.io/data-intuitive/dotnet-script`](https://github.com/data-intuitive/ghcr-dotnet-script/pkgs/container/dotnet-script) +* JavaScript: [`node`](https://hub.docker.com/_/node) +* Python: [`python`](https://hub.docker.com/_/python), [`nvcr.io/nvidia/pytorch`](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/pytorch) +* R: [`eddelbuettel/r2u`](https://hub.docker.com/r/eddelbuettel/r2u), [`rocker/tidyverse`](https://hub.docker.com/r/rocker/tidyverse) +* Scala: [`sbtscala/scala-sbt`](https://hub.docker.com/r/sbtscala/scala-sbt) + +### Step 11: Write a runner script + +Next, we need to write a runner script that runs the tool with the input arguments. Create a Bash script named `src/xxx/script.sh` which runs the tool with the input arguments. + +```bash +#!/bin/bash + +## VIASH START +## VIASH END + +xxx \ + --input "$par_input" \ + --output "$par_output" \ + $([ "$par_option" = "true" ] && echo "--option") +``` + +When building a Viash component, Viash will automatically replace the `## VIASH START` and `## VIASH END` lines (and anything in between) with environment variables based on the arguments specified in the config. + +As an example, this is what the Bash script for the `arriba` component looks like: + +```bash +#!/bin/bash + +## VIASH START +## VIASH END + +arriba \ + -x "$par_bam" \ + -a "$par_genome" \ + -g "$par_gene_annotation" \ + -o "$par_fusions" \ + ${par_known_fusions:+-k "${par_known_fusions}"} \ + ${par_blacklist:+-b "${par_blacklist}"} \ + ${par_structural_variants:+-d "${par_structural_variants}"} \ + $([ "$par_skip_duplicate_marking" = "true" ] && echo "-u") \ + $([ "$par_extra_information" = "true" ] && echo "-X") \ + $([ "$par_fill_gaps" = "true" ] && echo "-I") +``` + + +### Step 12: Create test script + + +If the unit test requires test resources, these should be provided in the `test_resources` section of the component. + +```yaml +functionality: + # ... + test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +``` + +Create a test script at `src/xxx/test.sh` that runs the component with the test data. This script should run the component (available with `$meta_executable`) with the test data and check if the output is as expected. The script should exit with a non-zero exit code if the output is not as expected. For example: + +```bash +#!/bin/bash + +## VIASH START +## VIASH END + +echo "> Run xxx with test data" +"$meta_executable" \ + --input "$meta_resources_dir/test_data/input.txt" \ + --output "output.txt" \ + --option + +echo ">> Checking output" +[ ! -f "output.txt" ] && echo "Output file output.txt does not exist" && exit 1 +``` + + +For example, this is what the test script for the `arriba` component looks like: + +```bash +#!/bin/bash + +## VIASH START +## VIASH END + +echo "> Run arriba with blacklist" +"$meta_executable" \ + --bam "$meta_resources_dir/test_data/A.bam" \ + --genome "$meta_resources_dir/test_data/genome.fasta" \ + --gene_annotation "$meta_resources_dir/test_data/annotation.gtf" \ + --blacklist "$meta_resources_dir/test_data/blacklist.tsv" \ + --fusions "fusions.tsv" \ + --fusions_discarded "fusions_discarded.tsv" \ + --interesting_contigs "1,2" + +echo ">> Checking output" +[ ! -f "fusions.tsv" ] && echo "Output file fusions.tsv does not exist" && exit 1 +[ ! -f "fusions_discarded.tsv" ] && echo "Output file fusions_discarded.tsv does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "fusions.tsv" ] && echo "Output file fusions.tsv is empty" && exit 1 +[ ! -s "fusions_discarded.tsv" ] && echo "Output file fusions_discarded.tsv is empty" && exit 1 +``` + +### Step 12: Create a `/var/software_versions.txt` file + +For the sake of transparency and reproducibility, we require that the versions of the software used in the component are documented. + +For now, this is managed by creating a file `/var/software_versions.txt` in the `setup` section of the Docker engine. + +```yaml +engines: + - type: docker + image: quay.io/biocontainers/xxx:0.1.0--py_0 + setup: + - type: docker + run: | + echo "xxx: \"0.1.0\"" > /var/software_versions.txt +``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..968d811c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Data Intuitive + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..4b497dcd --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ + + +# 🌱📦 biobox + +[![ViashHub](https://img.shields.io/badge/ViashHub-biobox-7a4baa.png)](https://web.viash-hub.com/packages/biobox) +[![GitHub](https://img.shields.io/badge/GitHub-viash--hub%2Fbiobox-blue.png)](https://github.com/viash-hub/biobox) +[![GitHub +License](https://img.shields.io/github/license/viash-hub/biobox.png)](https://github.com/viash-hub/biobox/blob/main/LICENSE) +[![GitHub +Issues](https://img.shields.io/github/issues/viash-hub/biobox.png)](https://github.com/viash-hub/biobox/issues) +[![Viash +version](https://img.shields.io/badge/Viash-v0.9.0--RC6-blue)](https://viash.io) + +A collection of bioinformatics tools for working with sequence data. + +## Objectives + +- **Reusability**: Facilitating the use of components across various + projects and contexts. +- **Reproducibility**: Ensuring that components are reproducible and can + be easily shared. +- **Best Practices**: Adhering to established standards in software + development and bioinformatics. + +## Contributing + +We encourage contributions from the community. To contribute: + +1. **Fork the Repository**: Start by forking this repository to your + account. +2. **Develop Your Component**: Create your Viash component, ensuring it + aligns with our best practices (detailed below). +3. **Submit a Pull Request**: After testing your component, submit a + pull request for review. + +## Contribution Guidelines + +The contribution guidelines describes which steps you should follow to +contribute a component to this repository. + +1. Find a component to contribute +2. Add config template +3. Fill in the metadata +4. Find a suitable container +5. Create help file +6. Create or fetch test data +7. Add arguments for the input files +8. Add arguments for the output files +9. Add arguments for the other arguments +10. Add a Docker engine +11. Write a runner script +12. Create test script +13. Create a `/var/software_versions.txt` file + +See the +[CONTRIBUTING](https://github.com/viash-hub/biobox/blob/main/CONTRIBUTING.md) +file for more details. + +## Support and Community + +For support, questions, or to join our community: + +- **Issues**: Submit questions or issues via the [GitHub issue + tracker](https://github.com/viash-hub/biobox/issues). +- **Discussions**: Join our discussions via [GitHub + Discussions](https://github.com/viash-hub/biobox/discussions). + +## License + +This repository is licensed under an MIT license. See the +[LICENSE](https://github.com/viash-hub/biobox/blob/main/LICENSE) file +for details. diff --git a/README.qmd b/README.qmd new file mode 100644 index 00000000..7d36430b --- /dev/null +++ b/README.qmd @@ -0,0 +1,62 @@ +--- +format: gfm +--- +```{r setup, include=FALSE} +project <- yaml::read_yaml("_viash.yaml") +license <- paste0(project$links$repository, "/blob/main/LICENSE") +contributing <- paste0(project$links$repository, "/blob/main/CONTRIBUTING.md") +``` +# 🌱📦 `r project$name` + +[![ViashHub](https://img.shields.io/badge/ViashHub-`r project$name`-7a4baa)](https://web.viash-hub.com/packages/`r project$name`) +[![GitHub](https://img.shields.io/badge/GitHub-viash--hub%2F`r project$name`-blue)](`r project$links$repository`) +[![GitHub License](https://img.shields.io/github/license/viash-hub/`r project$name`)](`r license`) +[![GitHub Issues](https://img.shields.io/github/issues/viash-hub/`r project$name`)](`r project$links$issue_tracker`) +[![Viash version](https://img.shields.io/badge/Viash-v`r gsub("-", "--", project$viash_version)`-blue)](https://viash.io) + +`r project$description` + +## Objectives + +- **Reusability**: Facilitating the use of components across various projects and contexts. +- **Reproducibility**: Ensuring that components are reproducible and can be easily shared. +- **Best Practices**: Adhering to established standards in software development and bioinformatics. + +## Contributing + +We encourage contributions from the community. To contribute: + +1. **Fork the Repository**: Start by forking this repository to your account. +2. **Develop Your Component**: Create your Viash component, ensuring it aligns with our best practices (detailed below). +3. **Submit a Pull Request**: After testing your component, submit a pull request for review. + +## Contribution Guidelines + +The contribution guidelines describes which steps you should follow to contribute a component to this repository. + +```{r echo=FALSE} +lines <- readr::read_lines("CONTRIBUTING.md") + +index_start <- grep("^### Step [0-9]*:", lines) + +index_end <- c(index_start[-1] - 1, length(lines)) + +name <- gsub("^### Step [0-9]*: *", "", lines[index_start]) + +knitr::asis_output( + paste(paste0(" 1. ", name, "\n"), collapse = "") +) +``` + +See the [CONTRIBUTING](`r contributing`) file for more details. + + +## Support and Community + +For support, questions, or to join our community: + +- **Issues**: Submit questions or issues via the [GitHub issue tracker](`r project$links$issue_tracker`). +- **Discussions**: Join our discussions via [GitHub Discussions](`r project$links$repository`/discussions). + +## License +This repository is licensed under an MIT license. See the [LICENSE](`r license`) file for details. diff --git a/_viash.yaml b/_viash.yaml new file mode 100644 index 00000000..9a240c24 --- /dev/null +++ b/_viash.yaml @@ -0,0 +1,13 @@ +name: biobox +description: | + A collection of bioinformatics tools for working with sequence data. +license: MIT +keywords: [bioinformatics, modules, sequencing] +links: + issue_tracker: https://github.com/viash-hub/biobox/issues + repository: https://github.com/viash-hub/biobox + +viash_version: 0.9.0-RC6 + +config_mods: | + .requirements.commands := ['ps'] diff --git a/main.nf b/main.nf new file mode 100644 index 00000000..5b3d280c --- /dev/null +++ b/main.nf @@ -0,0 +1,3 @@ +workflow { +print("This is a dummy placeholder for pipeline execution. Please use the corresponding nf files for running pipelines.") +} diff --git a/nextflow.config b/nextflow.config new file mode 100644 index 00000000..b2e1c5e9 --- /dev/null +++ b/nextflow.config @@ -0,0 +1,6 @@ +manifest { + name = "biobox" + version = "main" + defaultBranch = "main" + nextflowVersion = "!>=20.12.1-edge" +} diff --git a/src/arriba/config.vsh.yaml b/src/arriba/config.vsh.yaml new file mode 100644 index 00000000..8d72d7eb --- /dev/null +++ b/src/arriba/config.vsh.yaml @@ -0,0 +1,385 @@ +name: arriba +description: Detect gene fusions from RNA-Seq data +keywords: [Gene fusion, RNA-Seq] +links: + homepage: https://arriba.readthedocs.io/en/latest/ + documentation: https://arriba.readthedocs.io/en/latest/ + repository: https://github.com/suhrig/arriba +references: + doi: 10.1101/gr.257246.119 +license: MIT +requirements: + cpus: 1 + commands: [ arriba ] +argument_groups: + - name: Inputs + arguments: + - name: --bam + alternatives: -x + type: file + description: | + File in SAM/BAM/CRAM format with main alignments as generated by STAR + (Aligned.out.sam). Arriba extracts candidate reads from this file. + required: true + example: Aligned.out.bam + - name: --genome + alternatives: -a + type: file + description: | + FastA file with genome sequence (assembly). The file may be gzip-compressed. An + index with the file extension .fai must exist only if CRAM files are processed. + required: true + example: assembly.fa + - name: --gene_annotation + alternatives: -g + type: file + description: | + GTF file with gene annotation. The file may be gzip-compressed. + required: true + example: annotation.gtf + - name: --known_fusions + alternatives: -k + type: file + description: | + File containing known/recurrent fusions. Some cancer entities are often + characterized by fusions between the same pair of genes. In order to boost + sensitivity, a list of known fusions can be supplied using this parameter. The list + must contain two columns with the names of the fused genes, separated by tabs. + required: false + example: known_fusions.tsv + - name: --blacklist + alternatives: -b + type: file + description: | + File containing blacklisted events (recurrent artifacts and transcripts + observed in healthy tissue). + required: false + example: blacklist.tsv + - name: --structural_variants + alternatives: -d + type: file + description: | + Tab-separated file with coordinates of structural variants found using + whole-genome sequencing data. These coordinates serve to increase sensitivity + towards weakly expressed fusions and to eliminate fusions with low evidence. + required: false + example: structural_variants_from_WGS.tsv + - name: --tags + alternatives: -t + type: file + description: | + Tab-separated file containing fusions to annotate with tags in the 'tags' column. + The first two columns specify the genes; the third column specifies the tag. The + file may be gzip-compressed. + required: false + example: tags.tsv + - name: --protein_domains + alternatives: -p + type: file + description: | + File in GFF3 format containing coordinates of the protein domains of genes. The + protein domains retained in a fusion are listed in the column + 'retained_protein_domains'. The file may be gzip-compressed. + required: false + example: protein_domains.gff3 + - name: Outputs + arguments: + - name: --fusions + alternatives: -o + type: file + direction: output + description: | + Output file with fusions that have passed all filters. + required: true + example: fusions.tsv + - name: --fusions_discarded + alternatives: -O + type: file + direction: output + description: | + Output file with fusions that were discarded due to filtering. + required: false + example: fusions.discarded.tsv + - name: Arguments + arguments: + - name: --max_genomic_breakpoint_distance + alternatives: -D + type: long + description: | + When a file with genomic breakpoints obtained via + whole-genome sequencing is supplied via the --structural_variants + parameter, this parameter determines how far a + genomic breakpoint may be away from a + transcriptomic breakpoint to consider it as a + related event. For events inside genes, the + distance is added to the end of the gene; for + intergenic events, the distance threshold is + applied as is. Default: 100000. + required: false + - name: --strandedness + alternatives: -s + type: string + description: | + Whether a strand-specific protocol was used for library preparation, + and if so, the type of strandedness (auto/yes/no/reverse). When + unstranded data is processed, the strand can sometimes be inferred from + splice-patterns. But in unclear situations, stranded data helps + resolve ambiguities. Default: auto + choices: ["auto", "yes", "no", "reverse"] + required: false + - name: --interesting_contigs + alternatives: -i + type: string + description: | + List of interesting contigs. Fusions between genes + on other contigs are ignored. Contigs can be specified with or without the + prefix "chr". Asterisks (*) are treated as wild-cards. + Default: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 X Y AC_* NC_* + required: false + multiple: true + example: ["1", "2", "AC_*", "NC_*"] + - name: --viral_contigs + alternatives: -v + type: string + description: | + List of viral contigs. Asterisks (*) are treated as + wild-cards. + Default: AC_* NC_* + required: false + multiple: true + example: ["AC_*", "NC_*"] + - name: --disable_filters + alternatives: -f + type: string + description: | + List of filters to disable. By default all filters are + enabled. + choices: [ homologs, low_entropy, isoforms, + top_expressed_viral_contigs, viral_contigs, uninteresting_contigs, + non_coding_neighbors, mismatches, duplicates, no_genomic_support, + genomic_support, intronic, end_to_end, relative_support, + low_coverage_viral_contigs, merge_adjacent, mismappers, multimappers, + same_gene, long_gap, internal_tandem_duplication, small_insert_size, + read_through, inconsistently_clipped, intragenic_exonic, + marginal_read_through, spliced, hairpin, blacklist, min_support, + select_best, in_vitro, short_anchor, known_fusions, no_coverage, + homopolymer, many_spliced ] + required: false + multiple: true + - name: --max_e_value + alternatives: -E + type: double + description: | + Arriba estimates the number of fusions with a given number of supporting + reads which one would expect to see by random chance. If the expected number + of fusions (e-value) is higher than this threshold, the fusion is + discarded by the 'relative_support' filter. Note: Increasing this + threshold can dramatically increase the number of false positives and may + increase the runtime of resource-intensive steps. Fractional values are + possible. Default: 0.300000 + required: false + - name: --min_supporting_reads + alternatives: -S + type: integer + description: | + The 'min_support' filter discards all fusions with fewer than + this many supporting reads (split reads and discordant mates + combined). Default: 2 + required: false + example: 2 + - name: --max_mismappers + alternatives: -m + type: double + description: | + When more than this fraction of supporting reads turns out to be + mismappers, the 'mismappers' filter discards the fusion. Default: + 0.800000 + required: false + example: 0.8 + - name: --max_homolog_identity + alternatives: -L + type: double + description: | + Genes with more than the given fraction of sequence identity are + considered homologs and removed by the 'homologs' filter. + Default: 0.300000 + required: false + example: 0.3 + - name: --homopolymer_length + alternatives: -H + type: integer + description: | + The 'homopolymer' filter removes breakpoints adjacent to + homopolymers of the given length or more. Default: 6 + required: false + example: 6 + - name: --read_through_distance + alternatives: -R + type: integer + description: | + The 'read_through' filter removes read-through fusions + where the breakpoints are less than the given distance away + from each other. Default: 10000 + required: false + example: 10000 + - name : --min_anchor_length + alternatives: -A + type: integer + description: | + Alignment artifacts are often characterized by split reads coming + from only one gene and no discordant mates. Moreover, the split + reads only align to a short stretch in one of the genes. The + 'short_anchor' filter removes these fusions. This parameter sets + the threshold in bp for what the filter considers short. Default: 23 + required: false + example: 23 + - name: --many_spliced_events + alternatives: -M + type: integer + description: | + The 'many_spliced' filter recovers fusions between genes that + have at least this many spliced breakpoints. Default: 4 + required: false + example: 4 + - name: --max_kmer_content + alternatives: -K + type: double + description: | + The 'low_entropy' filter removes reads with repetitive 3-mers. If + the 3-mers make up more than the given fraction of the sequence, then + the read is discarded. Default: 0.600000 + required: false + example: 0.6 + - name: --max_mismatch_pvalue + alternatives: -V + type: double + description: | + The 'mismatches' filter uses a binomial model to calculate a + p-value for observing a given number of mismatches in a read. If + the number of mismatches is too high, the read is discarded. + Default: 0.010000 + required: false + example: 0.05 + - name: --fragment_length + alternatives: -F + type: integer + description: | + When paired-end data is given, the fragment length is estimated + automatically and this parameter has no effect. But when single-end + data is given, the mean fragment length should be specified to + effectively filter fusions that arise from hairpin structures. + Default: 200 + required: false + example: 200 + - name: --max_reads + alternatives: -U + type: integer + description: | + Subsample fusions with more than the given number of supporting reads. This + improves performance without compromising sensitivity, as long as the + threshold is high. Counting of supporting reads beyond the threshold is + inaccurate, obviously. Default: 300 + required: false + example: 300 + - name: --quantile + alternatives: -Q + type: double + description: | + Highly expressed genes are prone to produce artifacts during library + preparation. Genes with an expression above the given quantile are eligible + for filtering by the 'in_vitro' filter. Default: 0.998000 + required: false + example: 0.998 + - name: --exonic_fraction + alternatives: -e + type: double + description: | + The breakpoints of false-positive predictions of intragenic events + are often both in exons. True predictions are more likely to have at + least one breakpoint in an intron, because introns are larger. If the + fraction of exonic sequence between two breakpoints is smaller than + the given fraction, the 'intragenic_exonic' filter discards the + event. Default: 0.330000 + required: false + example: 0.33 + - name: --top_n + alternatives: -T + type: integer + description: | + Only report viral integration sites of the top N most highly expressed viral + contigs. Default: 5 + required: false + example: 5 + - name: --covered_fraction + alternatives: -C + type: double + description: | + Ignore virally associated events if the virus is not fully + expressed, i.e., less than the given fraction of the viral contig is + transcribed. Default: 0.050000 + required: false + example: 0.05 + - name: --max_itd_length + alternatives: -l + type: integer + description: | + Maximum length of internal tandem duplications. Note: Increasing + this value beyond the default can impair performance and lead to many + false positives. Default: 100 + required: false + example: 100 + - name: --min_itd_allele_fraction + alternatives: -z + type: double + description: | + Required fraction of supporting reads to report an internal + tandem duplication. Default: 0.070000 + required: false + example: 0.07 + - name: --min_itd_supporting_reads + alternatives: -Z + type: integer + description: | + Required absolute number of supporting reads to report an + internal tandem duplication. Default: 10 + required: false + example: 10 + - name: --skip_duplicate_marking + alternatives: -u + type: boolean_true + description: | + Instead of performing duplicate marking itself, Arriba relies on duplicate marking by a + preceding program using the BAM_FDUP flag. This makes sense when unique molecular + identifiers (UMI) are used. + - name: --extra_information + alternatives: -X + type: boolean_true + description: | + To reduce the runtime and file size, by default, the columns 'fusion_transcript', + 'peptide_sequence', and 'read_identifiers' are left empty in the file containing + discarded fusion candidates (see parameter -O). When this flag is set, this extra + information is reported in the discarded fusions file. + - name: --fill_gaps + alternatives: -I + type: boolean_true + description: | + If assembly of the fusion transcript sequence from the supporting reads is incomplete + (denoted as '...'), fill the gaps using the assembly sequence wherever possible. +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/arriba:2.4.0--h0033a41_2 + setup: + - type: docker + run: | + arriba -h | grep 'Version:' 2>&1 | sed 's/Version:\s\(.*\)/arriba: "\1"/' > /var/software_versions.txt +runners: + - type: executable + - type: nextflow \ No newline at end of file diff --git a/src/arriba/help.txt b/src/arriba/help.txt new file mode 100644 index 00000000..7d8fc76d --- /dev/null +++ b/src/arriba/help.txt @@ -0,0 +1,198 @@ +```bash +arriba -h +``` + +Arriba gene fusion detector +--------------------------- +Version: 2.4.0 + +Arriba is a fast tool to search for aberrant transcripts such as gene fusions. +It is based on chimeric alignments found by the STAR RNA-Seq aligner. + +Usage: arriba [-c Chimeric.out.sam] -x Aligned.out.bam \ + -g annotation.gtf -a assembly.fa [-b blacklists.tsv] [-k known_fusions.tsv] \ + [-t tags.tsv] [-p protein_domains.gff3] [-d structural_variants_from_WGS.tsv] \ + -o fusions.tsv [-O fusions.discarded.tsv] \ + [OPTIONS] + + -c FILE File in SAM/BAM/CRAM format with chimeric alignments as generated by STAR + (Chimeric.out.sam). This parameter is only required, if STAR was run with the + parameter '--chimOutType SeparateSAMold'. When STAR was run with the parameter + '--chimOutType WithinBAM', it suffices to pass the parameter -x to Arriba and -c + can be omitted. + + -x FILE File in SAM/BAM/CRAM format with main alignments as generated by STAR + (Aligned.out.sam). Arriba extracts candidate reads from this file. + + -g FILE GTF file with gene annotation. The file may be gzip-compressed. + + -G GTF_FEATURES Comma-/space-separated list of names of GTF features. + Default: gene_name=gene_name|gene_id gene_id=gene_id + transcript_id=transcript_id feature_exon=exon feature_CDS=CDS + + -a FILE FastA file with genome sequence (assembly). The file may be gzip-compressed. An + index with the file extension .fai must exist only if CRAM files are processed. + + -b FILE File containing blacklisted events (recurrent artifacts and transcripts + observed in healthy tissue). + + -k FILE File containing known/recurrent fusions. Some cancer entities are often + characterized by fusions between the same pair of genes. In order to boost + sensitivity, a list of known fusions can be supplied using this parameter. The list + must contain two columns with the names of the fused genes, separated by tabs. + + -o FILE Output file with fusions that have passed all filters. + + -O FILE Output file with fusions that were discarded due to filtering. + + -t FILE Tab-separated file containing fusions to annotate with tags in the 'tags' column. + The first two columns specify the genes; the third column specifies the tag. The + file may be gzip-compressed. + + -p FILE File in GFF3 format containing coordinates of the protein domains of genes. The + protein domains retained in a fusion are listed in the column + 'retained_protein_domains'. The file may be gzip-compressed. + + -d FILE Tab-separated file with coordinates of structural variants found using + whole-genome sequencing data. These coordinates serve to increase sensitivity + towards weakly expressed fusions and to eliminate fusions with low evidence. + + -D MAX_GENOMIC_BREAKPOINT_DISTANCE When a file with genomic breakpoints obtained via + whole-genome sequencing is supplied via the -d + parameter, this parameter determines how far a + genomic breakpoint may be away from a + transcriptomic breakpoint to consider it as a + related event. For events inside genes, the + distance is added to the end of the gene; for + intergenic events, the distance threshold is + applied as is. Default: 100000 + + -s STRANDEDNESS Whether a strand-specific protocol was used for library preparation, + and if so, the type of strandedness (auto/yes/no/reverse). When + unstranded data is processed, the strand can sometimes be inferred from + splice-patterns. But in unclear situations, stranded data helps + resolve ambiguities. Default: auto + + -i CONTIGS Comma-/space-separated list of interesting contigs. Fusions between genes + on other contigs are ignored. Cfontigs can be specified with or without the + prefix "chr". Asterisks (*) are treated as wild-cards. + Default: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 X Y AC_* NC_* + + -v CONTIGS Comma-/space-separated list of viral contigs. Asterisks (*) are treated as + wild-cards. + Default: AC_* NC_* + + -f FILTERS Comma-/space-separated list of filters to disable. By default all filters are + enabled. Valid values: homologs, low_entropy, isoforms, + top_expressed_viral_contigs, viral_contigs, uninteresting_contigs, + non_coding_neighbors, mismatches, duplicates, no_genomic_support, + genomic_support, intronic, end_to_end, relative_support, + low_coverage_viral_contigs, merge_adjacent, mismappers, multimappers, + same_gene, long_gap, internal_tandem_duplication, small_insert_size, + read_through, inconsistently_clipped, intragenic_exonic, + marginal_read_through, spliced, hairpin, blacklist, min_support, + select_best, in_vitro, short_anchor, known_fusions, no_coverage, + homopolymer, many_spliced + + -E MAX_E-VALUE Arriba estimates the number of fusions with a given number of supporting + reads which one would expect to see by random chance. If the expected number + of fusions (e-value) is higher than this threshold, the fusion is + discarded by the 'relative_support' filter. Note: Increasing this + threshold can dramatically increase the number of false positives and may + increase the runtime of resource-intensive steps. Fractional values are + possible. Default: 0.300000 + + -S MIN_SUPPORTING_READS The 'min_support' filter discards all fusions with fewer than + this many supporting reads (split reads and discordant mates + combined). Default: 2 + + -m MAX_MISMAPPERS When more than this fraction of supporting reads turns out to be + mismappers, the 'mismappers' filter discards the fusion. Default: + 0.800000 + + -L MAX_HOMOLOG_IDENTITY Genes with more than the given fraction of sequence identity are + considered homologs and removed by the 'homologs' filter. + Default: 0.300000 + + -H HOMOPOLYMER_LENGTH The 'homopolymer' filter removes breakpoints adjacent to + homopolymers of the given length or more. Default: 6 + + -R READ_THROUGH_DISTANCE The 'read_through' filter removes read-through fusions + where the breakpoints are less than the given distance away + from each other. Default: 10000 + + -A MIN_ANCHOR_LENGTH Alignment artifacts are often characterized by split reads coming + from only one gene and no discordant mates. Moreover, the split + reads only align to a short stretch in one of the genes. The + 'short_anchor' filter removes these fusions. This parameter sets + the threshold in bp for what the filter considers short. Default: 23 + + -M MANY_SPLICED_EVENTS The 'many_spliced' filter recovers fusions between genes that + have at least this many spliced breakpoints. Default: 4 + + -K MAX_KMER_CONTENT The 'low_entropy' filter removes reads with repetitive 3-mers. If + the 3-mers make up more than the given fraction of the sequence, then + the read is discarded. Default: 0.600000 + + -V MAX_MISMATCH_PVALUE The 'mismatches' filter uses a binomial model to calculate a + p-value for observing a given number of mismatches in a read. If + the number of mismatches is too high, the read is discarded. + Default: 0.010000 + + -F FRAGMENT_LENGTH When paired-end data is given, the fragment length is estimated + automatically and this parameter has no effect. But when single-end + data is given, the mean fragment length should be specified to + effectively filter fusions that arise from hairpin structures. + Default: 200 + + -U MAX_READS Subsample fusions with more than the given number of supporting reads. This + improves performance without compromising sensitivity, as long as the + threshold is high. Counting of supporting reads beyond the threshold is + inaccurate, obviously. Default: 300 + + -Q QUANTILE Highly expressed genes are prone to produce artifacts during library + preparation. Genes with an expression above the given quantile are eligible + for filtering by the 'in_vitro' filter. Default: 0.998000 + + -e EXONIC_FRACTION The breakpoints of false-positive predictions of intragenic events + are often both in exons. True predictions are more likely to have at + least one breakpoint in an intron, because introns are larger. If the + fraction of exonic sequence between two breakpoints is smaller than + the given fraction, the 'intragenic_exonic' filter discards the + event. Default: 0.330000 + + -T TOP_N Only report viral integration sites of the top N most highly expressed viral + contigs. Default: 5 + + -C COVERED_FRACTION Ignore virally associated events if the virus is not fully + expressed, i.e., less than the given fraction of the viral contig is + transcribed. Default: 0.050000 + + -l MAX_ITD_LENGTH Maximum length of internal tandem duplications. Note: Increasing + this value beyond the default can impair performance and lead to many + false positives. Default: 100 + + -z MIN_ITD_ALLELE_FRACTION Required fraction of supporting reads to report an internal + tandem duplication. Default: 0.070000 + + -Z MIN_ITD_SUPPORTING_READS Required absolute number of supporting reads to report an + internal tandem duplication. Default: 10 + + -u Instead of performing duplicate marking itself, Arriba relies on duplicate marking by a + preceding program using the BAM_FDUP flag. This makes sense when unique molecular + identifiers (UMI) are used. + + -X To reduce the runtime and file size, by default, the columns 'fusion_transcript', + 'peptide_sequence', and 'read_identifiers' are left empty in the file containing + discarded fusion candidates (see parameter -O). When this flag is set, this extra + information is reported in the discarded fusions file. + + -I If assembly of the fusion transcript sequence from the supporting reads is incomplete + (denoted as '...'), fill the gaps using the assembly sequence wherever possible. + + -h Print help and exit. + + Code repository: https://github.com/suhrig/arriba + Get help/report bugs: https://github.com/suhrig/arriba/issues + User manual: https://arriba.readthedocs.io/ + Please cite: https://doi.org/10.1101/gr.257246.119 \ No newline at end of file diff --git a/src/arriba/script.sh b/src/arriba/script.sh new file mode 100644 index 00000000..5892db32 --- /dev/null +++ b/src/arriba/script.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +# unset flags +[[ "$par_skip_duplicate_marking" == "false" ]] && unset par_skip_duplicate_marking +[[ "$par_extra_information" == "false" ]] && unset par_extra_information +[[ "$par_fill_gaps" == "false" ]] && unset par_fill_gaps + +# replace ';' with ',' +par_interesting_contigs=$(echo $par_interesting_contigs | tr ';' ',') +par_viral_contigs=$(echo $par_viral_contigs | tr ';' ',') +par_disable_filters=$(echo $par_disable_filters | tr ';' ',') + +# run arriba +arriba \ + -x "$par_bam" \ + -a "$par_genome" \ + -g "$par_gene_annotation" \ + -o "$par_fusions" \ + ${par_known_fusions:+-k "${par_known_fusions}"} \ + ${par_blacklist:+-b "${par_blacklist}"} \ + ${par_structural_variants:+-d "${par_structural_variants}"} \ + ${par_tags:+-t "${par_tags}"} \ + ${par_protein_domains:+-p "${par_protein_domains}"} \ + ${par_fusions_discarded:+-O "${par_fusions_discarded}"} \ + ${par_max_genomic_breakpoint_distance:+-D "${par_max_genomic_breakpoint_distance}"} \ + ${par_strandedness:+-s "${par_strandedness}"} \ + ${par_interesting_contigs:+-i "${par_interesting_contigs}"} \ + ${par_viral_contigs:+-v "${par_viral_contigs}"} \ + ${par_disable_filters:+-f "${par_disable_filters}"} \ + ${par_max_e_value:+-E "${par_max_e_value}"} \ + ${par_min_supporting_reads:+-S "${par_min_supporting_reads}"} \ + ${par_max_mismappers:+-m "${par_max_mismappers}"} \ + ${par_max_homolog_identity:+-L "${par_max_homolog_identity}"} \ + ${par_homopolymer_length:+-H "${par_homopolymer_length}"} \ + ${par_read_through_distance:+-R "${par_read_through_distance}"} \ + ${par_min_anchor_length:+-A "${par_min_anchor_length}"} \ + ${par_many_spliced_events:+-M "${par_many_spliced_events}"} \ + ${par_max_kmer_content:+-K "${par_max_kmer_content}"} \ + ${par_max_mismatch_pvalue:+-V "${par_max_mismatch_pvalue}"} \ + ${par_fragment_length:+-F "${par_fragment_length}"} \ + ${par_max_reads:+-U "${par_max_reads}"} \ + ${par_quantile:+-Q "${par_quantile}"} \ + ${par_exonic_fraction:+-e "${par_exonic_fraction}"} \ + ${par_top_n:+-T "${par_top_n}"} \ + ${par_covered_fraction:+-C "${par_covered_fraction}"} \ + ${par_max_itd_length:+-l "${par_max_itd_length}"} \ + ${par_min_itd_allele_fraction:+-z "${par_min_itd_allele_fraction}"} \ + ${par_min_itd_supporting_reads:+-Z "${par_min_itd_supporting_reads}"} \ + ${par_skip_duplicate_marking:+-u} \ + ${par_extra_information:+-X} \ + ${par_fill_gaps:+-I} diff --git a/src/arriba/test.sh b/src/arriba/test.sh new file mode 100644 index 00000000..7f1243d2 --- /dev/null +++ b/src/arriba/test.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -e + +dir_in="$meta_resources_dir/test_data" + +echo "> Run arriba with blacklist" +"$meta_executable" \ + --bam "$dir_in/A.bam" \ + --genome "$dir_in/genome.fasta" \ + --gene_annotation "$dir_in/annotation.gtf" \ + --blacklist "$dir_in/blacklist.tsv" \ + --fusions "fusions.tsv" \ + --fusions_discarded "fusions_discarded.tsv" \ + --interesting_contigs "1,2" + +echo ">> Checking output" +[ ! -f "fusions.tsv" ] && echo "Output file fusions.tsv does not exist" && exit 1 +[ ! -f "fusions_discarded.tsv" ] && echo "Output file fusions_discarded.tsv does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "fusions.tsv" ] && echo "Output file fusions.tsv is empty" && exit 1 +[ ! -s "fusions_discarded.tsv" ] && echo "Output file fusions_discarded.tsv is empty" && exit 1 + +rm fusions.tsv fusions_discarded.tsv + +echo "> Run arriba without blacklist" +"$meta_executable" \ + --bam "$dir_in/A.bam" \ + --genome "$dir_in/genome.fasta" \ + --gene_annotation "$dir_in/annotation.gtf" \ + --fusions "fusions.tsv" \ + --fusions_discarded "fusions_discarded.tsv" \ + --interesting_contigs "1,2" \ + --disable_filters blacklist + +echo ">> Checking output" +[ ! -f "fusions.tsv" ] && echo "Output file fusions.tsv does not exist" && exit 1 +[ ! -f "fusions_discarded.tsv" ] && echo "Output file fusions_discarded.tsv does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "fusions.tsv" ] && echo "Output file fusions.tsv is empty" && exit 1 +[ ! -s "fusions_discarded.tsv" ] && echo "Output file fusions_discarded.tsv is empty" && exit 1 + +echo "> Test successful" \ No newline at end of file diff --git a/src/arriba/test_data/A.bam b/src/arriba/test_data/A.bam new file mode 100644 index 00000000..57511ab3 Binary files /dev/null and b/src/arriba/test_data/A.bam differ diff --git a/src/arriba/test_data/annotation.gtf b/src/arriba/test_data/annotation.gtf new file mode 100644 index 00000000..22b3a67a --- /dev/null +++ b/src/arriba/test_data/annotation.gtf @@ -0,0 +1,6 @@ +1 havana gene 1 80 . + . gene_id "ENSG00000000000"; gene_version "5"; gene_name "A"; gene_source "havana"; gene_biotype "gene"; +1 havana transcript 1 80 . + . gene_id "ENSG00000000000"; gene_version "5"; transcript_id "ENST00000000000"; transcript_version "2"; gene_name "A"; gene_source "havana"; gene_biotype "gene"; transcript_name "A-202"; transcript_source "havana"; transcript_biotype "processed_transcript"; tag "basic"; transcript_support_level "1"; +1 havana exon 1 80 . + . gene_id "ENSG00000000000"; gene_version "5"; transcript_id "ENST00000000000"; transcript_version "2"; exon_number "1"; gene_name "A"; gene_source "havana"; gene_biotype "gene"; transcript_name "A-202"; transcript_source "havana"; transcript_biotype "processed_transcript"; exon_id "ENSE00000000000"; exon_version "1"; tag "basic"; transcript_support_level "1"; +2 havana gene 1 80 . + . gene_id "ENSG00000000001"; gene_version "5"; gene_name "B"; gene_source "havana"; gene_biotype "gene"; +2 havana transcript 1 80 . + . gene_id "ENSG00000000001"; gene_version "5"; transcript_id "ENST00000000001"; transcript_version "2"; gene_name "B"; gene_source "havana"; gene_biotype "gene"; transcript_name "B-202"; transcript_source "havana"; transcript_biotype "processed_transcript"; tag "basic"; transcript_support_level "1"; +2 havana exon 1 80 . + . gene_id "ENSG00000000001"; gene_version "5"; transcript_id "ENST00000000001"; transcript_version "2"; exon_number "1"; gene_name "B"; gene_source "havana"; gene_biotype "gene"; transcript_name "B-202"; transcript_source "havana"; transcript_biotype "processed_transcript"; exon_id "ENSE00000000001"; exon_version "1"; tag "basic"; transcript_support_level "1"; diff --git a/src/arriba/test_data/blacklist.tsv b/src/arriba/test_data/blacklist.tsv new file mode 100644 index 00000000..e69de29b diff --git a/src/arriba/test_data/genome.fasta b/src/arriba/test_data/genome.fasta new file mode 100644 index 00000000..91ea0d37 --- /dev/null +++ b/src/arriba/test_data/genome.fasta @@ -0,0 +1,4 @@ +>1 +GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG +>2 +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA diff --git a/src/arriba/test_data/script.sh b/src/arriba/test_data/script.sh new file mode 100755 index 00000000..e5bbcf2c --- /dev/null +++ b/src/arriba/test_data/script.sh @@ -0,0 +1,10 @@ +# arriba test data + +# Test data was obtained from https://github.com/snakemake/snakemake-wrappers/tree/master/bio/arriba/test + +if [ ! -d /tmp/snakemake-wrappers ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers /tmp/snakemake-wrappers +fi + +cp -r /tmp/snakemake-wrappers/bio/arriba/test/* src/arriba/test_data + diff --git a/src/bcl_convert/config.vsh.yaml b/src/bcl_convert/config.vsh.yaml new file mode 100644 index 00000000..657fb1f0 --- /dev/null +++ b/src/bcl_convert/config.vsh.yaml @@ -0,0 +1,159 @@ +name: bcl_convert +description: | + Convert bcl files to fastq files using bcl-convert. + Information about upgrading from bcl2fastq via + [Upgrading from bcl2fastq to BCL Convert](https://emea.support.illumina.com/bulletins/2020/10/upgrading-from-bcl2fastq-to-bcl-convert.html) + and [BCL Convert Compatible Products](https://support.illumina.com/sequencing/sequencing_software/bcl-convert/compatibility.html) +argument_groups: + - name: Input arguments + arguments: + - name: "--bcl_input_directory" + alternatives: ["-i"] + type: file + required: true + description: Input run directory + example: bcl_dir + - name: "--sample_sheet" + alternatives: ["-s"] + type: file + description: Path to SampleSheet.csv file (default searched for in --bcl_input_directory) + example: bcl_dir/sample_sheet.csv + - name: --run_info + type: file + description: Path to RunInfo.xml file (default root of BCL input directory) + example: bcl_dir/RunInfo.xml + + - name: Lane and tile settings + arguments: + - name: "--bcl_only_lane" + type: integer + description: Convert only specified lane number (default all lanes) + example: 1 + - name: --first_tile_only + type: boolean + description: Only convert first tile of input (for testing & debugging) + example: true + - name: --tiles + type: string + description: Process only a subset of tiles by a regular expression + example: "s_[0-9]+_1" + - name: --exclude_tiles + type: string + description: Exclude set of tiles by a regular expression + example: "s_[0-9]+_1" + + - name: Resource arguments + arguments: + - name: --shared_thread_odirect_output + type: boolean + description: Use linux native asynchronous io (io_submit) for file output (Default=false) + example: true + - name: --bcl_num_parallel_tiles + type: integer + description: "\\# of tiles to process in parallel (default 1)" + example: 1 + - name: --bcl_num_conversion_threads + type: integer + description: "\\# of threads for conversion (per tile, default # cpu threads)" + example: 1 + - name: --bcl_num_compression_threads + type: integer + description: "\\# of threads for fastq.gz output compression (per tile, default # cpu threads, or HW+12)" + example: 1 + - name: --bcl_num_decompression_threads + type: integer + description: + "\\# of threads for bcl/cbcl input decompression (per tile, default half # cpu threads, or HW+8). + Only applies when preloading files" + example: 1 + + - name: Run arguments + arguments: + - name: --bcl_only_matched_reads + type: boolean + description: For pure BCL conversion, do not output files for 'Undetermined' [unmatched] reads (output by default) + example: true + - name: --no_lane_splitting + type: boolean + description: Do not split FASTQ file by lane (false by default) + example: true + - name: --num_unknown_barcodes_reported + type: integer + description: "\\# of Top Unknown Barcodes to output (1000 by default)" + example: 1000 + - name: --bcl_validate_sample_sheet_only + type: boolean + description: Only validate RunInfo.xml & SampleSheet files (produce no FASTQ files) + example: true + - name: --strict_mode + type: boolean + description: Abort if any files are missing (false by default) + example: true + - name: --sample_name_column_enabled + type: boolean + description: Use sample sheet 'Sample_Name' column when naming fastq files & subdirectories + example: true + + - name: Output arguments + arguments: + - name: "--output_directory" + alternatives: ["-o"] + type: file + direction: output + required: true + description: Output directory containig fastq files + example: fastq_dir + - name: --bcl_sampleproject_subdirectories + type: boolean + description: Output to subdirectories based upon sample sheet 'Sample_Project' column + example: true + - name: --fastq_gzip_compression_level + type: integer + description: Set fastq output compression level 0-9 (default 1) + example: 1 + - name: "--reports" + type: file + direction: output + required: false + description: Reports directory + example: reports_dir + - name: "--logs" + type: file + direction: output + required: false + description: Reports directory + example: logs_dir + +# bcl-convert arguments not taken into account +# --force +# --output-legacy-stats arg Also output stats in legacy (bcl2fastq2) format (false by default) +# --no-sample-sheet arg Enable legacy no-sample-sheet operation (No demux or trimming. No settings + +resources: + - type: bash_script + path: script.sh + +test_resources: + - type: bash_script + path: test.sh + +engines: + - type: docker + image: debian:trixie-slim + # https://support.illumina.com/sequencing/sequencing_software/bcl-convert/downloads.html + setup: + - type: apt + packages: [wget, gdb, which, hostname, alien, procps] + - type: docker + run: | + wget https://s3.amazonaws.com/webdata.illumina.com/downloads/software/bcl-convert/bcl-convert-4.2.7-2.el8.x86_64.rpm -O /tmp/bcl-convert.rpm && \ + alien -i /tmp/bcl-convert.rpm && \ + rm -rf /var/lib/apt/lists/* && \ + rm /tmp/bcl-convert.rpm + - type: docker + run: | + echo "bcl-convert: \"$(bcl-convert -V 2>&1 >/dev/null | sed -n '/Version/ s/^bcl-convert\ Version //p')\"" > /var/software_versions.txt + +runners: + - type: executable + - type: nextflow diff --git a/src/bcl_convert/help.txt b/src/bcl_convert/help.txt new file mode 100644 index 00000000..edb73faf --- /dev/null +++ b/src/bcl_convert/help.txt @@ -0,0 +1,38 @@ +bcl-convert Version 00.000.000.4.2.7 +Copyright (c) 2014-2022 Illumina, Inc. + +Run BCL Conversion (BCL directory to *.fastq.gz) + bcl-convert --bcl-input-directory --output-directory [options] + +Options: + -h [ --help ] Print this help message + -V [ --version ] Print the version and exit + --output-directory arg Output BCL directory for BCL conversion (must be specified) + -f [ --force ] Force: allow destination diretory to already exist + --bcl-input-directory arg Input BCL directory for BCL conversion (must be specified) + --sample-sheet arg Path to SampleSheet.csv file (default searched for in --bcl-input-directory) + --bcl-only-lane arg Convert only specified lane number (default all lanes) + --strict-mode arg Abort if any files are missing (false by default) + --first-tile-only arg Only convert first tile of input (for testing & debugging) + --tiles arg Process only a subset of tiles by a regular expression + --exclude-tiles arg Exclude set of tiles by a regular expression + --bcl-sampleproject-subdirectories arg Output to subdirectories based upon sample sheet 'Sample_Project' column + --sample-name-column-enabled arg Use sample sheet 'Sample_Name' column when naming fastq files & subdirectories + --fastq-gzip-compression-level arg Set fastq output compression level 0-9 (default 1) + --shared-thread-odirect-output arg Use linux native asynchronous io (io_submit) for file output (Default=false) + --bcl-num-parallel-tiles arg # of tiles to process in parallel (default 1) + --bcl-num-conversion-threads arg # of threads for conversion (per tile, default # cpu threads) + --bcl-num-compression-threads arg # of threads for fastq.gz output compression (per tile, default # cpu threads, + or HW+12) + --bcl-num-decompression-threads arg # of threads for bcl/cbcl input decompression (per tile, default half # cpu + threads, or HW+8. Only applies when preloading files) + --bcl-only-matched-reads arg For pure BCL conversion, do not output files for 'Undetermined' [unmatched] + reads (output by default) + --run-info arg Path to RunInfo.xml file (default root of BCL input directory) + --no-lane-splitting arg Do not split FASTQ file by lane (false by default) + --num-unknown-barcodes-reported arg # of Top Unknown Barcodes to output (1000 by default) + --bcl-validate-sample-sheet-only arg Only validate RunInfo.xml & SampleSheet files (produce no FASTQ files) + --output-legacy-stats arg Also output stats in legacy (bcl2fastq2) format (false by default) + --no-sample-sheet arg Enable legacy no-sample-sheet operation (No demux or trimming. No settings + supported. False by default, not recommended + diff --git a/src/bcl_convert/script.sh b/src/bcl_convert/script.sh new file mode 100644 index 00000000..6a59acd5 --- /dev/null +++ b/src/bcl_convert/script.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +set -eo pipefail + +$(which bcl-convert) \ + --bcl-input-directory "$par_bcl_input_directory" \ + --output-directory "$par_output_directory" \ + ${par_sample_sheet:+ --sample-sheet "$par_sample_sheet"} \ + ${par_run_info:+ --run-info "$par_run_info"} \ + ${par_bcl_only_lane:+ --bcl-only-lane "$par_bcl_only_lane"} \ + ${par_first_tile_only:+ --first-tile-only "$par_first_tile_only"} \ + ${par_tiles:+ --tiles "$par_tiles"} \ + ${par_exclude_tiles:+ --exclude-tiles "$par_exclude_tiles"} \ + ${par_shared_thread_odirect_output:+ --shared-thread-odirect-output "$par_shared_thread_odirect_output"} \ + ${par_bcl_num_parallel_tiles:+ --bcl-num-parallel-tiles "$par_bcl_num_parallel_tiles"} \ + ${par_bcl_num_conversion_threads:+ --bcl-num-conversion-threads "$par_bcl_num_conversion_threads"} \ + ${par_bcl_num_compression_threads:+ --bcl-num-compression-threads "$par_bcl_num_compression_threads"} \ + ${par_bcl_num_decompression_threads:+ --bcl-num-decompression-threads "$par_bcl_num_decompression_threads"} \ + ${par_bcl_only_matched_reads:+ --bcl-only-matched-reads "$par_bcl_only_matched_reads"} \ + ${par_no_lane_splitting:+ --no-lane-splitting "$par_no_lane_splitting"} \ + ${par_num_unknown_barcodes_reported:+ --num-unknown-barcodes-reported "$par_num_unknown_barcodes_reported"} \ + ${par_bcl_validate_sample_sheet_only:+ --bcl-validate-sample-sheet-only "$par_bcl_validate_sample_sheet_only"} \ + ${par_strict_mode:+ --strict-mode "$par_strict_mode"} \ + ${par_sample_name_column_enabled:+ --sample-name-column-enabled "$par_sample_name_column_enabled"} \ + ${par_bcl_sampleproject_subdirectories:+ --bcl-sampleproject-subdirectories "$par_bcl_sampleproject_subdirectories"} \ + ${par_fastq_gzip_compression_level:+ --fastq-gzip-compression-level "$par_fastq_gzip_compression_level"} + +if [ ! -z "$par_reports" ]; then + echo "Moving reports to their own location" + mv "${par_output_directory}/Reports" "$par_reports" +else + echo "Leaving reports alone" +fi + +if [ ! -z "$par_logs" ]; then + echo "Moving logs to their own location" + mv "${par_output_directory}/Logs" "$par_logs" +else + echo "Leaving logs alone" +fi diff --git a/src/bcl_convert/test.sh b/src/bcl_convert/test.sh new file mode 100644 index 00000000..b46bc9fe --- /dev/null +++ b/src/bcl_convert/test.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Tests are sourced from: +# https://www.10xgenomics.com/support/software/cell-ranger/latest/analysis/inputs/cr-direct-demultiplexing-bcl-convert +# Test input files are fetched from: +# https://cf.10xgenomics.com/supp/spatial-exp/demultiplexing/iseq-DI.tar.gz +# https://cf.10xgenomics.com/supp/spatial-exp/demultiplexing/bcl_convert_samplesheet.csv + +set -eo pipefail + +echo ">> Fetching and preparing test data" +data_src="https://cf.10xgenomics.com/supp/spatial-exp/demultiplexing/iseq-DI.tar.gz" +sample_sheet_src="https://cf.10xgenomics.com/supp/spatial-exp/demultiplexing/bcl_convert_samplesheet.csv" +test_data_dir="test_data" + +mkdir $test_data_dir +wget -q $data_src -O $test_data_dir/data.tar.gz +wget -q $sample_sheet_src -O $test_data_dir/sample_sheet.csv +tar xzf $test_data_dir/data.tar.gz -C $test_data_dir +rm $test_data_dir/data.tar.gz + +echo ">> Execute and verify output" + +$meta_executable \ + --bcl_input_directory "$test_data_dir/iseq-DI" \ + --sample_sheet "$test_data_dir/sample_sheet.csv" \ + --output_directory fastq \ + --reports reports \ + --logs logs + +echo ">>> Checking whether the output dir exists" +[[ ! -d fastq ]] && echo "Output dir could not be found!" && exit 1 + +echo ">>> Checking whether output fastq files are created" +[[ ! -f fastq/Undetermined_S0_L001_R1_001.fastq.gz ]] && echo "Output fastq files could not be found!" && exit 1 +[[ ! -f fastq/iseq-DI_S1_L001_R1_001.fastq.gz ]] && echo "Output fastq files could not be found!" && exit 1 + +echo ">>> Checking whether the report dir exists" +[[ ! -d reports ]] && echo "Reports dir could not be found!" && exit 1 + +echo ">>> Checking whether the log dir exists" +[[ ! -d logs ]] && echo "Logs dir could not be found!" && exit 1 + +# print final message +echo ">>> Test finished successfully" + +echo ">> Execute with additional arguments and verify output" + +$meta_executable \ + --bcl_input_directory "$test_data_dir/iseq-DI" \ + --sample_sheet "$test_data_dir/sample_sheet.csv" \ + --output_directory fastq1 \ + --bcl_only_matched_reads true \ + --bcl_num_compression_threads 1 \ + --no_lane_splitting false \ + --fastq_gzip_compression_level 9 + +echo ">> Checking whether the output dir exists" +[[ ! -d fastq1 ]] && echo "Output dir could not be found!" && exit 1 + +echo ">> Checking whether output fastq files are created" +[[ -f fastq1/Undetermined_S0_L001_R1_001.fastq.gz ]] && echo "Undetermined should not be generated!" && exit 1 +[[ ! -f fastq1/iseq-DI_S1_L001_R1_001.fastq.gz ]] && echo "Output fastq files could not be found!" && exit 1 + +# print final message +echo ">> Test finished successfully" + +# do not remove this +# as otherwise your test might exit with a different exit code +exit 0 diff --git a/src/bedtools/bedtools_getfasta/config.vsh.yaml b/src/bedtools/bedtools_getfasta/config.vsh.yaml new file mode 100644 index 00000000..f1f49a87 --- /dev/null +++ b/src/bedtools/bedtools_getfasta/config.vsh.yaml @@ -0,0 +1,103 @@ +name: bedtools_getfasta +namespace: bedtools +description: Extract sequences from a FASTA file for each of the intervals defined in a BED/GFF/VCF file. +keywords: [sequencing, fasta, BED, GFF, VCF] +links: + documentation: https://bedtools.readthedocs.io/en/latest/content/tools/getfasta.html + repository: https://github.com/arq5x/bedtools2 +references: + doi: 10.1093/bioinformatics/btq033 +license: GPL-2.0 +requirements: + commands: [bedtools] + +argument_groups: + - name: Input arguments + arguments: + - name: --input_fasta + type: file + description: | + FASTA file containing sequences for each interval specified in the input BED file. + The headers in the input FASTA file must exactly match the chromosome column in the BED file. + - name: "--input_bed" + type: file + description: | + BED file containing intervals to extract from the FASTA file. + BED files containing a single region require a newline character + at the end of the line, otherwise a blank output file is produced. + - name: --rna + type: boolean_true + description: | + The FASTA is RNA not DNA. Reverse complementation handled accordingly. + + - name: Run arguments + arguments: + - name: "--strandedness" + type: boolean_true + alternatives: ["-s"] + description: | + Force strandedness. If the feature occupies the antisense strand, the output sequence will + be reverse complemented. By default strandedness is not taken into account. + + - name: Output arguments + arguments: + - name: --output + alternatives: [-o] + required: true + type: file + direction: output + description: | + Output file where the output from the 'bedtools getfasta' commend will + be written to. + - name: --tab + type: boolean_true + description: | + Report extract sequences in a tab-delimited format instead of in FASTA format. + - name: --bed_out + type: boolean_true + description: | + Report extract sequences in a tab-delimited BED format instead of in FASTA format. + - name: "--name" + type: boolean_true + description: | + Set the FASTA header for each extracted sequence to be the "name" and coordinate columns from the BED feature. + - name: "--name_only" + type: boolean_true + description: | + Set the FASTA header for each extracted sequence to be the "name" columns from the BED feature. + - name: "--split" + type: boolean_true + description: | + When --input is in BED12 format, create a separate fasta entry for each block in a BED12 record, + blocks being described in the 11th and 12th column of the BED. + - name: "--full_header" + type: boolean_true + description: | + Use full fasta header. By default, only the word before the first space or tab is used. + +# Arguments not taken into account: +# +# -fo [Specify an output file name. By default, output goes to stdout. +# + +resources: + - type: bash_script + path: script.sh + +test_resources: + - type: bash_script + path: test.sh + +engines: + - type: docker + image: debian:stable-slim + setup: + - type: apt + packages: [bedtools, procps] + - type: docker + run: | + echo "bedtools: \"$(bedtools --version | sed -n 's/^bedtools //p')\"" > /var/software_versions.txt + +runners: + - type: executable + - type: nextflow diff --git a/src/bedtools/bedtools_getfasta/script.sh b/src/bedtools/bedtools_getfasta/script.sh new file mode 100644 index 00000000..8e88b318 --- /dev/null +++ b/src/bedtools/bedtools_getfasta/script.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -eo pipefail + +unset_if_false=( par_rna par_strandedness par_tab par_bed_out par_name par_name_only par_split par_full_header ) + +for par in ${unset_if_false[@]}; do + test_val="${!par}" + [[ "$test_val" == "false" ]] && unset $par +done + +bedtools getfasta \ + -fi "$par_input_fasta" \ + -bed "$par_input_bed" \ + ${par_rna:+-rna} \ + ${par_name:+-name} \ + ${par_name_only:+-nameOnly} \ + ${par_tab:+-tab} \ + ${par_bed_out:+-bedOut} \ + ${par_strandedness:+-s} \ + ${par_split:+-split} \ + ${par_full_header:+-fullHeader} > "$par_output" + diff --git a/src/bedtools/bedtools_getfasta/test.sh b/src/bedtools/bedtools_getfasta/test.sh new file mode 100644 index 00000000..a28e3a7e --- /dev/null +++ b/src/bedtools/bedtools_getfasta/test.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash +set -eo pipefail + +TMPDIR=$(mktemp -d) +function clean_up { + [[ -d "$TMPDIR" ]] && rm -r "$TMPDIR" +} +trap clean_up EXIT + +# Create dummy test fasta file +cat > "$TMPDIR/test.fa" <chr1 +AAAAAAAACCCCCCCCCCCCCGCTACTGGGGGGGGGGGGGGGGGG +EOF + +TAB="$(printf '\t')" + +# Create dummy bed file +cat > "$TMPDIR/test.bed" < "$TMPDIR/expected.fasta" <chr1:5-10 +AAACC +EOF + +"$meta_executable" \ + --input_bed "$TMPDIR/test.bed" \ + --input_fasta "$TMPDIR/test.fa" \ + --output "$TMPDIR/output.fasta" + +cmp --silent "$TMPDIR/output.fasta" "$TMPDIR/expected.fasta" || { echo "files are different:"; exit 1; } + + +# Create expected bed file for --name +cat > "$TMPDIR/expected_with_name.fasta" <myseq::chr1:5-10 +AAACC +EOF + +"$meta_executable" \ + --input_bed "$TMPDIR/test.bed" \ + --input_fasta "$TMPDIR/test.fa" \ + --name \ + --output "$TMPDIR/output_with_name.fasta" + + +cmp --silent "$TMPDIR/output_with_name.fasta" "$TMPDIR/expected_with_name.fasta" || { echo "Files when using --name are different."; exit 1; } + +# Create expected bed file for --name_only +cat > "$TMPDIR/expected_with_name_only.fasta" <myseq +AAACC +EOF + +"$meta_executable" \ + --input_bed "$TMPDIR/test.bed" \ + --input_fasta "$TMPDIR/test.fa" \ + --name_only \ + --output "$TMPDIR/output_with_name_only.fasta" + +cmp --silent "$TMPDIR/output_with_name_only.fasta" "$TMPDIR/expected_with_name_only.fasta" || { echo "Files when using --name_only are different."; exit 1; } + + +# Create expected tab-delimited file for --tab +cat > "$TMPDIR/expected_tab.out" < "$TMPDIR/expected.bed" < "$TMPDIR/test_strandedness.bed" < "$TMPDIR/expected_strandedness.fasta" <forward(+) +CGCTA +>reverse(-) +TAGCG +EOF + +"$meta_executable" \ + --input_bed "$TMPDIR/test_strandedness.bed" \ + --input_fasta "$TMPDIR/test.fa" \ + -s \ + --name_only \ + --output "$TMPDIR/output_strandedness.fasta" + + +cmp --silent "$TMPDIR/expected_strandedness.fasta" "$TMPDIR/output_strandedness.fasta" || { echo "Files when using -s are different."; exit 1; } + diff --git a/src/busco/busco_download_datasets/config.vsh.yaml b/src/busco/busco_download_datasets/config.vsh.yaml new file mode 100644 index 00000000..04d76dd6 --- /dev/null +++ b/src/busco/busco_download_datasets/config.vsh.yaml @@ -0,0 +1,47 @@ +name: busco_download_datasets +namespace: busco +description: Downloads available busco datasets +keywords: [lineage datasets] +links: + homepage: https://busco.ezlab.org/ + documentation: https://busco.ezlab.org/busco_userguide.html + repository: https://gitlab.com/ezlab/busco +references: + doi: 10.1007/978-1-4939-9173-0_14 +license: MIT +argument_groups: + - name: Inputs + arguments: + - name: --download + type: string + description: | + Download dataset. Possible values are a specific dataset name, "all", "prokaryota", "eukaryota", or "virus". + The full list of available datasets can be viewed [here](https://busco-data.ezlab.org/v5/data/lineages/) or by running the busco/busco_list_datasets component. + required: true + example: stramenopiles_odb10 + - name: Outputs + arguments: + - name: --download_path + direction: output + type: file + description: | + Local filepath for storing BUSCO dataset downloads + required: false + default: busco_downloads + example: busco_downloads +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh +engines: + - type: docker + image: quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0 + setup: + - type: docker + run: | + busco --version | sed 's/BUSCO\s\(.*\)/busco: "\1"/' > /var/software_versions.txt +runners: + - type: executable + - type: nextflow diff --git a/src/busco/busco_download_datasets/script.sh b/src/busco/busco_download_datasets/script.sh new file mode 100644 index 00000000..6010c01f --- /dev/null +++ b/src/busco/busco_download_datasets/script.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +## VIASH START +## VIASH END + + +if [ ! -d "$par_download_path" ]; then + mkdir -p "$par_download_path" +fi + +busco \ + --download_path "$par_download_path" \ + --download "$par_download" + diff --git a/src/busco/busco_download_datasets/test.sh b/src/busco/busco_download_datasets/test.sh new file mode 100644 index 00000000..c6baecea --- /dev/null +++ b/src/busco/busco_download_datasets/test.sh @@ -0,0 +1,15 @@ +echo "> Downloading busco stramenopiles_odb10 dataset" + +"$meta_executable" \ + --download stramenopiles_odb10 \ + --download_path downloads + +echo ">> Checking output" +[ ! -f "downloads/file_versions.tsv" ] && echo "file_versions.tsv does not exist" && exit 1 +[ ! -f "downloads/lineages/stramenopiles_odb10/dataset.cfg" ] && echo "dataset.cfg does not exist" && exit 1 + +echo ">> Checking if output is empty" +[ ! -s "downloads/file_versions.tsv" ] && echo "file_versions.tsv is empty" && exit 1 +[ ! -s "downloads/lineages/stramenopiles_odb10/dataset.cfg" ] && echo "dataset.cfg is empty" && exit 1 + +rm -r downloads \ No newline at end of file diff --git a/src/busco/busco_list_datasets/config.vsh.yaml b/src/busco/busco_list_datasets/config.vsh.yaml new file mode 100644 index 00000000..6ada7c84 --- /dev/null +++ b/src/busco/busco_list_datasets/config.vsh.yaml @@ -0,0 +1,39 @@ +name: busco_list_datasets +namespace: busco +description: Lists the available busco datasets +keywords: [lineage datasets] +links: + homepage: https://busco.ezlab.org/ + documentation: https://busco.ezlab.org/busco_userguide.html + repository: https://gitlab.com/ezlab/busco +references: + doi: 10.1007/978-1-4939-9173-0_14 +license: MIT +argument_groups: + - name: Outputs + arguments: + - name: --output + alternatives: ["-o"] + direction: output + type: file + description: | + Output file of the available busco datasets + required: false + default: busco_dataset_list.txt + example: file.txt +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh +engines: + - type: docker + image: quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0 + setup: + - type: docker + run: | + busco --version | sed 's/BUSCO\s\(.*\)/busco: "\1"/' > /var/software_versions.txt +runners: + - type: executable + - type: nextflow diff --git a/src/busco/busco_list_datasets/script.sh b/src/busco/busco_list_datasets/script.sh new file mode 100644 index 00000000..6c80725c --- /dev/null +++ b/src/busco/busco_list_datasets/script.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +busco --list-datasets | awk '/^#{40}/{flag=1; next} flag{print}' > $par_output \ No newline at end of file diff --git a/src/busco/busco_list_datasets/test.sh b/src/busco/busco_list_datasets/test.sh new file mode 100644 index 00000000..c303cd77 --- /dev/null +++ b/src/busco/busco_list_datasets/test.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +"$meta_executable" \ + --output datasets.txt + +echo ">> Checking output" +[ ! -f "datasets.txt" ] && echo "datasets.txt does not exist" && exit 1 + +echo ">> Checking if output is empty" +[ ! -s "datasets.txt" ] && echo "datasets.txt is empty" && exit 1 + +rm datasets.txt \ No newline at end of file diff --git a/src/busco/busco_run/config.vsh.yaml b/src/busco/busco_run/config.vsh.yaml new file mode 100644 index 00000000..d79f03f5 --- /dev/null +++ b/src/busco/busco_run/config.vsh.yaml @@ -0,0 +1,214 @@ +name: busco_run +namespace: busco +description: Assessment of genome assembly and annotation completeness with single copy orthologs +keywords: [Genome assembly, quality control] +links: + homepage: https://busco.ezlab.org/ + documentation: https://busco.ezlab.org/busco_userguide.html + repository: https://gitlab.com/ezlab/busco +references: + doi: 10.1007/978-1-4939-9173-0_14 +license: MIT +argument_groups: + - name: Inputs + arguments: + - name: --input + alternatives: ["-i"] + type: file + description: | + Input sequence file in FASTA format. Can be an assembled genome or transcriptome (DNA), or protein sequences from an annotated gene set. Also possible to use a path to a directory containing multiple input files. + required: true + example: file.fasta + - name: --mode + alternatives: ["-m"] + type: string + choices: [genome, geno, transcriptome, tran, proteins, prot] + required: true + description: | + Specify which BUSCO analysis mode to run. There are three valid modes: + - geno or genome, for genome assemblies (DNA) + - tran or transcriptome, for transcriptome assemblies (DNA) + - prot or proteins, for annotated gene sets (protein) + example: proteins + - name: --lineage_dataset + alternatives: ["-l"] + type: string + required: false + description: | + Specify a BUSCO lineage dataset that is most closely related to the assembly or gene set being assessed. + The full list of available datasets can be viewed [here](https://busco-data.ezlab.org/v5/data/lineages/) or by running the busco/busco_list_datasets component. + When unsure, the "--auto_lineage" flag can be set to automatically find the optimal lineage path. + BUSCO will automatically download the requested dataset if it is not already present in the download folder. + You can optionally provide a path to a local dataset instead of a name, e.g. path/to/dataset. + Datasets can be downloaded using the busco/busco_download_dataset component. + example: stramenopiles_odb10 + + - name: Outputs + arguments: + - name: --short_summary_json + required: false + direction: output + type: file + example: short_summary.json + description: | + Output file for short summary in JSON format. + - name: --short_summary_txt + required: false + direction: output + type: file + example: short_summary.txt + description: | + Output file for short summary in TXT format. + - name: --full_table + required: false + direction: output + type: file + example: full_table.tsv + description: | + Full table output in TSV format. + - name: --missing_busco_list + required: false + direction: output + type: file + example: missing_busco_list.tsv + description: | + Missing list output in TSV format. + - name: --output_dir + required: false + direction: output + type: file + example: output_dir/ + description: | + The full output directory, if so desired. + + - name: Resource and Run Settings + arguments: + - name: --force + type: boolean_true + description: | + Force rewriting of existing files. Must be used when output files with the provided name already exist. + - name: --quiet + alternatives: ["-q"] + type: boolean_true + description: | + Disable the info logs, displays only errors. + - name: --restart + alternatives: ["-r"] + type: boolean_true + description: | + Continue a run that had already partially completed. Restarting skips calls to tools that have completed but performs all pre- and post-processing steps. + - name: --tar + type: boolean_true + description: | + Compress some subdirectories with many files to save space. + + - name: Lineage Dataset Settings + arguments: + - name: --auto_lineage + type: boolean_true + description: | + Run auto-lineage pipelilne to automatically determine BUSCO lineage dataset that is most closely related to the assembly or gene set being assessed. + - name: --auto_lineage_euk + type: boolean_true + description: | + Run auto-placement just on eukaryota tree to find optimal lineage path. + - name: --auto_lineage_prok + type: boolean_true + description: | + Run auto_lineage just on prokaryota trees to find optimum lineage path. + - name: --datasets_version + type: string + required: false + description: | + Specify the version of BUSCO datasets + example: odb10 + + - name: Augustus Settings + arguments: + - name: --augustus + type: boolean_true + description: | + Use augustus gene predictor for eukaryote runs. + - name: --augustus_parameters + type: string + required: false + description: | + Additional parameters to be passed to Augustus (see Augustus documentation: https://github.com/Gaius-Augustus/Augustus/blob/master/docs/RUNNING-AUGUSTUS.md). + Parameters should be contained within a single string, without whitespace and seperated by commas. + example: "--PARAM1=VALUE1,--PARAM2=VALUE2" + - name: --augustus_species + type: string + required: false + description: | + Specify the augustus species + - name: --long + type: boolean_true + description: | + Optimize Augustus self-training mode. This adds considerably to the run time, but can improve results for some non-model organisms. + + - name: BBTools Settings + arguments: + - name: --contig_break + type: integer + required: false + description: | + Number of contiguous Ns to signify a break between contigs in BBTools analysis. + - name: --limit + type: integer + required: false + description: | + Number of candidate regions (contig or transcript) from the BLAST output to consider per BUSCO. + This option is only effective in pipelines using BLAST, i.e. the genome pipeline (see --augustus) or the prokaryota transcriptome pipeline. + - name: --scaffold_composition + type: boolean_true + description: | + Writes ACGTN content per scaffold to a file scaffold_composition.txt. + + - name: BLAST Settings + arguments: + - name: --e_value + type: double + required: false + description: | + E-value cutoff for BLAST searches. + + - name: Protein Gene Prediction settings + arguments: + - name: --miniprot + type: boolean_true + description: | + Use Miniprot gene predictor. + + - name: MetaEuk Settings + arguments: + - name: --metaeuk_parameters + type: string + description: | + Pass additional arguments to Metaeuk for the first run (see Metaeuk documentation https://github.com/soedinglab/metaeuk). + All parameters should be contained within a single string with no white space, with each parameter separated by a comma. + example: "--max-overlap=15,--min-exon-aa=15" + - name: --metaeuk_rerun_parameters + type: string + description: | + Pass additional arguments to Metaeuk for the second run (see Metaeuk documentation https://github.com/soedinglab/metaeuk). + All parameters should be contained within a single string with no white space, with each parameter separated by a comma. + example: "--max-overlap=15,--min-exon-aa=15" + +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0 + setup: + - type: docker + run: | + busco --version | sed 's/BUSCO\s\(.*\)/busco: "\1"/' > /var/software_versions.txt +runners: + - type: executable + - type: nextflow diff --git a/src/busco/busco_run/help.txt b/src/busco/busco_run/help.txt new file mode 100644 index 00000000..2cacec4d --- /dev/null +++ b/src/busco/busco_run/help.txt @@ -0,0 +1,60 @@ +```bash +busco -h +``` + +Welcome to BUSCO 5.6.1: the Benchmarking Universal Single-Copy Ortholog assessment tool. +For more detailed usage information, please review the README file provided with this distribution and the BUSCO user guide. Visit this page https://gitlab.com/ezlab/busco#how-to-cite-busco to see how to cite BUSCO + +optional arguments: + -i SEQUENCE_FILE, --in SEQUENCE_FILE + Input sequence file in FASTA format. Can be an assembled genome or transcriptome (DNA), or protein sequences from an annotated gene set. Also possible to use a path to a directory containing multiple input files. + -o OUTPUT, --out OUTPUT + Give your analysis run a recognisable short name. Output folders and files will be labelled with this name. The path to the output folder is set with --out_path. + -m MODE, --mode MODE Specify which BUSCO analysis mode to run. + There are three valid modes: + - geno or genome, for genome assemblies (DNA) + - tran or transcriptome, for transcriptome assemblies (DNA) + - prot or proteins, for annotated gene sets (protein) + -l LINEAGE, --lineage_dataset LINEAGE + Specify the name of the BUSCO lineage to be used. + --augustus Use augustus gene predictor for eukaryote runs + --augustus_parameters --PARAM1=VALUE1,--PARAM2=VALUE2 + Pass additional arguments to Augustus. All arguments should be contained within a single string with no white space, with each argument separated by a comma. + --augustus_species AUGUSTUS_SPECIES + Specify a species for Augustus training. + --auto-lineage Run auto-lineage to find optimum lineage path + --auto-lineage-euk Run auto-placement just on eukaryote tree to find optimum lineage path + --auto-lineage-prok Run auto-lineage just on non-eukaryote trees to find optimum lineage path + -c N, --cpu N Specify the number (N=integer) of threads/cores to use. + --config CONFIG_FILE Provide a config file + --contig_break n Number of contiguous Ns to signify a break between contigs. Default is n=10. + --datasets_version DATASETS_VERSION + Specify the version of BUSCO datasets, e.g. odb10 + --download [dataset [dataset ...]] + Download dataset. Possible values are a specific dataset name, "all", "prokaryota", "eukaryota", or "virus". If used together with other command line arguments, make sure to place this last. + --download_base_url DOWNLOAD_BASE_URL + Set the url to the remote BUSCO dataset location + --download_path DOWNLOAD_PATH + Specify local filepath for storing BUSCO dataset downloads + -e N, --evalue N E-value cutoff for BLAST searches. Allowed formats, 0.001 or 1e-03 (Default: 1e-03) + -f, --force Force rewriting of existing files. Must be used when output files with the provided name already exist. + -h, --help Show this help message and exit + --limit N How many candidate regions (contig or transcript) to consider per BUSCO (default: 3) + --list-datasets Print the list of available BUSCO datasets + --long Optimization Augustus self-training mode (Default: Off); adds considerably to the run time, but can improve results for some non-model organisms + --metaeuk_parameters "--PARAM1=VALUE1,--PARAM2=VALUE2" + Pass additional arguments to Metaeuk for the first run. All arguments should be contained within a single string with no white space, with each argument separated by a comma. + --metaeuk_rerun_parameters "--PARAM1=VALUE1,--PARAM2=VALUE2" + Pass additional arguments to Metaeuk for the second run. All arguments should be contained within a single string with no white space, with each argument separated by a comma. + --miniprot Use miniprot gene predictor + --skip_bbtools Skip BBTools for assembly statistics + --offline To indicate that BUSCO cannot attempt to download files + --opt-out-run-stats Opt out of data collection. Information on the data collected is available in the user guide. + --out_path OUTPUT_PATH + Optional location for results folder, excluding results folder name. Default is current working directory. + -q, --quiet Disable the info logs, displays only errors + -r, --restart Continue a run that had already partially completed. + --scaffold_composition + Writes ACGTN content per scaffold to a file scaffold_composition.txt + --tar Compress some subdirectories with many files to save space + -v, --version Show this version and exit \ No newline at end of file diff --git a/src/busco/busco_run/script.sh b/src/busco/busco_run/script.sh new file mode 100644 index 00000000..5b562f83 --- /dev/null +++ b/src/busco/busco_run/script.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +## VIASH START +## VIASH END + + +[[ "$par_tar" == "false" ]] && unset par_tar +[[ "$par_force" == "false" ]] && unset par_force +[[ "$par_quiet" == "false" ]] && unset par_quiet +[[ "$par_restart" == "false" ]] && unset par_restart +[[ "$par_auto_lineage" == "false" ]] && unset par_auto_lineage +[[ "$par_auto_lineage_euk" == "false" ]] && unset par_auto_lineage_euk +[[ "$par_auto_lineage_prok" == "false" ]] && unset par_auto_lineage_prok +[[ "$par_augustus" == "false" ]] && unset par_augustus +[[ "$par_long" == "false" ]] && unset par_long +[[ "$par_scaffold_composition" == "false" ]] && unset par_scaffold_composition +[[ "$par_miniprot" == "false" ]] && unset par_miniprot + +tmp_dir=$(mktemp -d -p "$meta_temp_dir" busco_XXXXXXXXX) +prefix=$(openssl rand -hex 8) + +busco \ + --in "$par_input" \ + --mode "$par_mode" \ + --out "$prefix" \ + --out_path "$tmp_dir" \ + --opt-out-run-stats \ + ${meta_cpus:+--cpu "${meta_cpus}"} \ + ${par_lineage_dataset:+--lineage_dataset "$par_lineage_dataset"} \ + ${par_augustus:+--augustus} \ + ${par_augustus_parameters:+--augustus_parameters "$par_augustus_parameters"} \ + ${par_augustus_species:+--augustus_species "$par_augustus_species"} \ + ${par_auto_lineage:+--auto-lineage} \ + ${par_auto_lineage_euk:+--auto-lineage-euk} \ + ${par_auto_lineage_prok:+--auto-lineage-prok} \ + ${par_contig_break:+--contig_break $par_contig_break} \ + ${par_datasets_version:+--datasets_version "$par_datasets_version"} \ + ${par_e_value:+--evalue "$par_e_value"} \ + ${par_force:+--force} \ + ${par_limit:+--limit "$par_limit"} \ + ${par_long:+--long} \ + ${par_metaeuk_parameters:+--metaeuk_parameters "$par_metaeuk_parameters"} \ + ${par_metaeuk_rerun_parameters:+--metaeuk_rerun_parameters "$par_metaeuk_rerun_parameters"} \ + ${par_miniprot:+--miniprot} \ + ${par_quiet:+--quiet} \ + ${par_restart:+--restart} \ + ${par_scaffold_composition:+--scaffold_composition} \ + ${par_tar:+--tar} \ + + +out_dir=$(find "$tmp_dir/$prefix" -maxdepth 1 -name 'run_*') + +if [[ -n "$par_short_summary_json" ]]; then + cp "$out_dir/short_summary.json" "$par_short_summary_json" +fi +if [[ -n "$par_short_summary_txt" ]]; then + cp "$out_dir/short_summary.txt" "$par_short_summary_txt" +fi +if [[ -n "$par_full_table" ]]; then + cp "$out_dir/full_table.tsv" "$par_full_table" +fi +if [[ -n "$par_missing_busco_list" ]]; then + cp "$out_dir/missing_busco_list.tsv" "$par_missing_busco_list" +fi +if [[ -n "$par_output_dir" ]]; then + if [[ -d "$par_output_dir" ]]; then + rm -r "$par_output_dir" + fi + cp -r -L "$out_dir" "$par_output_dir" +fi + diff --git a/src/busco/busco_run/test.sh b/src/busco/busco_run/test.sh new file mode 100644 index 00000000..12745a01 --- /dev/null +++ b/src/busco/busco_run/test.sh @@ -0,0 +1,88 @@ +test_dir="$meta_resources_dir/test_data" + +mkdir "run_prot_stramenopiles" +cd "run_prot_stramenopiles" + +echo "> Running busco with lineage dataset" + +"$meta_executable" \ + --input $test_dir/protein.fasta \ + --mode proteins \ + --lineage_dataset stramenopiles_odb10 \ + --output_dir output \ + --short_summary_json short_summary.json \ + --short_summary_txt short_summary.txt \ + --full_table full_table.tsv \ + --missing_busco_list missing_busco_list.tsv + +echo ">> Checking output" +[ ! -f "output/full_table.tsv" ] && echo "full_table.tsv does not exist" && exit 1 +[ ! -f "output/missing_busco_list.tsv" ] && echo "missing_busco_list.tsv does not exist" && exit 1 +[ ! -f "output/short_summary.json" ] && echo "short_summary.json does not exist" && exit 1 +[ ! -f "output/short_summary.txt" ] && echo "short_summary.txt does not exist" && exit 1 +[ ! -f "full_table.tsv" ] && echo "full_table.tsv does not exist" && exit 1 +[ ! -f "missing_busco_list.tsv" ] && echo "missing_busco_list.tsv does not exist" && exit 1 +[ ! -f "short_summary.json" ] && echo "short_summary.json does not exist" && exit 1 +[ ! -f "short_summary.txt" ] && echo "short_summary.txt does not exist" && exit 1 + +echo ">> Checking if output is empty" +[ ! -s "output/full_table.tsv" ] && echo "full_table.tsv is empty" && exit 1 +[ ! -s "output/missing_busco_list.tsv" ] && echo "missing_busco_list.tsv is empty" && exit 1 +[ ! -s "output/short_summary.json" ] && echo "short_summary.json is empty" && exit 1 +[ ! -s "output/short_summary.txt" ] && echo "short_summary.txt is empty" && exit 1 +[ ! -s "full_table.tsv" ] && echo "full_table.tsv is empty" && exit 1 +[ ! -s "missing_busco_list.tsv" ] && echo "missing_busco_list.tsv is empty" && exit 1 +[ ! -s "short_summary.json" ] && echo "short_summary.json is empty" && exit 1 +[ ! -s "short_summary.txt" ] && echo "short_summary.txt is empty" && exit 1 + +cd .. +mkdir "run_prot_autolineage" +cd "run_prot_autolineage" + +echo "> Running busco with auto lineage" + +"$meta_executable" \ + --input $test_dir/protein.fasta \ + --mode proteins \ + --auto_lineage \ + --output_dir output + +echo ">> Checking output" +[ ! -f "output/full_table.tsv" ] && echo "full_table.tsv does not exist in output folder" && exit 1 +[ ! -f "output/missing_busco_list.tsv" ] && echo "missing_busco_list.tsv does not exist in output folder" && exit 1 +[ ! -f "output/short_summary.json" ] && echo "short_summary.json does not exist in output folder" && exit 1 +[ ! -f "output/short_summary.txt" ] && echo "short_summary.txt does not exist in output folder" && exit 1 + +echo ">> Checking if output is empty" +[ ! -s "output/full_table.tsv" ] && echo "full_table.tsv in output folder is empty" && exit 1 +[ ! -s "output/missing_busco_list.tsv" ] && echo "missing_busco_list.tsv in output folder is empty" && exit 1 +[ ! -s "output/short_summary.json" ] && echo "short_summary.json in output folder is empty" && exit 1 +[ ! -s "output/short_summary.txt" ] && echo "short_summary.txt in output folder is empty" && exit 1 + +rm -r output/ + +cd .. +mkdir "run_genome" +cd "run_genome" + +echo "> Running busco with genome data" + +"$meta_executable" \ + --input $test_dir/genome.fna \ + --mode genome \ + --lineage_dataset saccharomycetes_odb10 \ + --output_dir output + +echo ">> Checking output" +[ ! -f "output/full_table.tsv" ] && echo "full_table.tsv does not exist in output folder" && exit 1 +[ ! -f "output/missing_busco_list.tsv" ] && echo "missing_busco_list.tsv does not exist in output folder" && exit 1 +[ ! -f "output/short_summary.json" ] && echo "short_summary.json does not exist in output folder" && exit 1 +[ ! -f "output/short_summary.txt" ] && echo "short_summary.txt does not exist in output folder" && exit 1 + +echo ">> Checking if output is empty" +[ ! -s "output/full_table.tsv" ] && echo "full_table.tsv in output folder is empty" && exit 1 +[ ! -s "output/missing_busco_list.tsv" ] && echo "missing_busco_list.tsv in output folder is empty" && exit 1 +[ ! -s "output/short_summary.json" ] && echo "short_summary.json in output folder is empty" && exit 1 +[ ! -s "output/short_summary.txt" ] && echo "short_summary.txt in output folder is empty" && exit 1 + +rm -r output/ \ No newline at end of file diff --git a/src/busco/busco_run/test_data/genome.fna b/src/busco/busco_run/test_data/genome.fna new file mode 100644 index 00000000..f0299314 --- /dev/null +++ b/src/busco/busco_run/test_data/genome.fna @@ -0,0 +1,10000 @@ +>NC_007795.1 Staphylococcus aureus subsp. aureus NCTC 8325 chromosome, complete genome +CGATTAAAGATAGAAATACACGATGCGAGCAATCAAATTTCATAACATCACCATGAGTTTGGTCCGAAGCATGAGTGTTT +ACAATGTTCGAACACCTTATACAGTTCTTATACATACTTTATAAATTATTTCCCAAACTGTTTTGATACACTCACTAACA +GATACTCTATAGAAGGAAAAGTTATCCACTTATGCACATTTATAGTTTTCAGAATTGTGGATAATTAGAAATTACACACA +AAGTTATACTATTTTTAGCAACATATTCACAGGTATTTGACATATAGAGAACTGAAAAAGTATAATTGTGTGGATAAGTC +GTCCAACTCATGATTTTATAAGGATTTATTTATTGATTTTTACATAAAAATACTGTGCATAACTAATAAGCAAGATAAAG +TTATCCACCGATTGTTATTAACTTGTGGATAATTATTAACATGGTGTGTTTAGAAGTTATCCACGGCTGTTATTTTTGTG +TATAACTTAAAAATTTAAGAAAGATGGAGTAAATTTATGTCGGAAAAAGAAATTTGGGAAAAAGTGCTTGAAATTGCTCA +AGAAAAATTATCAGCTGTAAGTTACTCAACTTTCCTAAAAGATACTGAGCTTTACACGATTAAAGATGGTGAAGCTATCG +TATTATCGAGTATTCCTTTTAATGCAAATTGGTTAAATCAACAATATGCTGAAATTATCCAAGCAATCTTATTTGATGTT +GTAGGCTATGAAGTTAAACCTCACTTTATTACTACTGAAGAATTAGCAAATTATAGTAATAATGAAACTGCTACTCCAAA +AGAAACAACAAAACCTTCTACTGAAACAACTGAGGATAATCATGTGCTTGGTAGAGAGCAATTCAATGCCCATAACACAT +TTGACACTTTTGTAATCGGACCCGGTAACCGCTTTCCACATGCAGCGAGTTTAGCTGTGGCCGAAGCACCAGCCAAAGCG +TACAATCCATTATTTATCTATGGAGGTGTTGGTTTAGGAAAAACCCATTTAATGCATGCCATTGGTCATCATGTTTTAGA +TAATAATCCAGATGCCAAAGTGATTTACACATCAAGTGAAAAATTCACAAATGAATTTATTAAATCAATTCGTGATAACG +AAGGTGAAGCTTTCAGAGAAAGATATCGTAATATCGACGTCTTATTAATCGATGATATTCAGTTCATACAAAACAAGGTA +CAAACACAAGAAGAATTTTTCTATACTTTTAATGAATTGCATCAGAATAACAAGCAAATAGTTATTTCGAGTGATCGACC +ACCAAAGGAAATTGCACAATTAGAAGACCGATTACGTTCACGCTTTGAATGGGGGCTAATTGTTGATATTACGCCACCAG +ATTATGAAACTCGAATGGCAATTTTGCAGAAGAAAATTGAAGAAGAAAAATTAGATATTCCACCAGAAGCTTTAAATTAT +ATAGCAAATCAAATTCAATCTAATATTCGTGAATTAGAAGGTGCATTAACACGTTTACTTGCATATTCACAATTATTAGG +AAAACCAATTACAACTGAATTAACTGCTGAAGCTTTAAAAGATATCATTCAAGCACCAAAATCTAAAAAGATTACCATCC +AAGATATTCAAAAAATTGTAGGCCAGTACTATAATGTTAGAATTGAAGATTTCAGTGCAAAAAAACGTACAAAGTCAATT +GCATATCCGCGTCAAATAGCTATGTACTTGTCTAGAGAGCTTACAGATTTCTCATTACCTAAAATTGGTGAAGAATTTGG +TGGGCGTGATCATACGACCGTCATTCATGCTCATGAAAAAATATCTAAAGATTTAAAAGAAGATCCTATTTTTAAACAAG +AAGTAGAGAATCTTGAAAAAGAAATAAGAAATGTATAAGTAGGAAACTTTGGGAAATGTAATCTGTTATATAACAGCACT +AATGATAACAATCATTTTTTACATTTCTATATGCTAATGTGGCAAGATGAGCAAAACTCATTTTGTGGATAATGTTTAAA +AGTCATACACACCATACACAAGTTATCAACATGTGTATAACTTCGCCAAATCTATGTTTTTAAGACTTATCCACCAATCC +ACAGCACCTACTACTATTACTAAGAACTTAAAACCTATATAATTATATATAAACGACTGGAAGGAGTTTTAATTAATGAT +GGAATTCACTATTAAAAGAGATTATTTTATTACACAATTAAATGACACATTAAAAGCTATTTCACCAAGAACAACATTAC +CTATATTAACTGGTATCAAAATCGATGCGAAAGAACATGAAGTTATATTAACTGGTTCAGACTCTGAAATTTCAATAGAA +ATCACTATTCCTAAAACTGTAGATGGCGAAGATATTGTCAATATTTCAGAAACAGGCTCAGTAGTACTTCCTGGACGATT +CTTTGTTGATATTATAAAAAAATTACCTGGTAAAGATGTTAAATTATCTACAAATGAACAATTCCAGACATTAATTACAT +CAGGTCATTCTGAATTTAATTTAAGTGGCTTAGATCCAGATCAATATCCTTTATTACCTCAAGTTTCTAGAGATGACGCA +ATTCAATTGTCGGTAAAAGTGCTTAAAAACGTGATTGCACAAACAAATTTTGCAGTGTCCACCTCAGAAACACGCCCAGT +ACTAACTGGTGTGAACTGGCTTATACAAGAAAATGAATTAATATGCACAGCGACTGACTCACACCGCTTGGCTGTAAGAA +AGTTGCAGTTAGAAGATGTTTCTGAAAACAAAAATGTCATCATTCCAGGTAAGGCTTTAGCTGAATTAAATAAAATTATG +TCTGACAATGAAGAAGACATTGATATCTTCTTTGCTTCAAACCAAGTTTTATTTAAAGTTGGAAATGTGAACTTTATTTC +TCGATTATTAGAAGGACATTATCCTGATACAACACGTTTATTCCCTGAAAACTATGAAATTAAATTAAGTATAGACAATG +GGGAGTTTTATCATGCGATTGATCGTGCCTCTTTATTAGCGCGTGAAGGTGGTAATAACGTTATTAAATTAAGTACAGGT +GATGACGTTGTTGAATTGTCTTCTACATCACCAGAAATTGGTACTGTAAAAGAAGAAGTTGATGCAAACGATGTTGAAGG +TGGTAGCCTGAAAATTTCATTCAACTCTAAATATATGATGGATGCTTTAAAAGCAATCGATAATGATGAGGTTGAAGTTG +AATTCTTCGGTACAATGAAACCATTTATTCTAAAACCAAAAGGTGACGACTCGGTAACGCAATTAATTTTACCAATCAGA +ACTTACTAAAAATAAATATAAATAAAGGATGACGTGATTAATTAAAACGTCATCCTTTATTTTTTGGCAAAAATAATTCT +AGGTGCGTATGTAAAATAAATTTGGCAGCATTTTAAACAGCAAATAAAAGACGCCAATTAAATTTATGACAAATGTATCC +AAAATTTAATAAGTGTGCTTATATGCCCTTTAAATTTAAAATTTTAATAGTCAATAACAAGTTGAATATAAAAGTTAAAC +GCCGTTAAATAGCGTTAAAAAATTGAAAATGACAGTATTGCCAAAAAATAAGAATTAATTATTTATATGTAAACGGTTTC +TACCTCTATTTTAAATGAAATTTGTGACAAAAAAAGGTATAATATATTAATGACATACAAAGAAATGGAGTGATTATTTT +GGTTCAAGAAGTTGTAGTAGAAGGAGACATTAATTTAGGTCAATTTCTAAAAACAGAAGGGATTATTGAATCTGGTGGTC +AAGCAAAATGGTTCTTGCAAGACGTTGAAGTATTAATTAATGGAGTGCGTGAAACACGTCGCGGTAAAAAGTTAGAACAT +CAAGATCGTATAGATATCCCAGAATTACCTGAAGATGCTGGTTCTTTCTTAATCATTCATCAAGGTGAACAATGAAGTTA +AATACACTCCAATTAGAAAATTATCGTAACTATGATGAGGTTACGTTGAAATGTCATCCTGACGTGAATATCCTCATTGG +AGAAAATGCACAAGGAAAGACAAATTTACTTGAATCAATTTATACCTTAGCTTTAGCAAAAAGTCATAGAACGAGTAATG +ATAAGGAACTCATACGTTTTAATGCTGATTATGCTAAAATAGAAGGTGAGCTTAGTTATAGACACGGCACGATGCCATTA +ACAATGTTTATAACTAAAAAAGGTAAACAAGTCAAAGTGAATCACTTAGAGCAAAGTCGTCTAACTCAATATATTGGACA +CCTCAATGTGGTTCTATTTGCGCCAGAAGATTTGAATATTGTAAAAGGCTCTCCTCAAATAAGACGACGCTTTATAGATA +TGGAGTTGGGCCAAATTTCTGCTGTTTACTTAAATGATTTAGCTCAATACCAACGTATTTTAAAGCAAAAGAATAATTAC +TTAAAGCAGTTACAATTAGGCCAAAAAAAGGACTTAACAATGTTGGAAGTATTAAATCAGCAGTTTGCTGAATATGCAAT +GAAAGTAACTGATAAACGTGCACATTTTATTCAAGAGCTAGAGTCGTTAGCTAAACCGATTCATGCTGGTATCACAAATG +ATAAAGAAGCGTTGTCGCTGAATTATTTACCTAGTCTTAAATTTGATTATGCTCAAAATGAAGCGGCACGACTTGAAGAA +ATTATGTCTATTCTTAGCGATAATATGCAAAGAGAAAAAGAACGAGGCATTAGCTTATTCGGACCACATCGAGATGATAT +AAGTTTTGATGTGAATGGCATGGATGCTCAAACATATGGTTCTCAAGGACAGCAACGTACAACGGCTTTGTCCATTAAAT +TAGCTGAAATTGAGTTAATGAATATCGAAGTTGGGGAATATCCCATCTTATTATTAGACGATGTACTCAGTGAATTAGAT +GATTCGCGTCAAACGCATTTATTAAGTACGATTCAGCATAAAGTACAAACATTTGTCACTACGACATCTGTAGATGGTAT +TGATCATGAAATCATGAATAACGCTAAATTGTATCGTATTAATCAAGGTGAAATTATAAAGTAACAGAAAGCGATGGTGA +CTGCATTGTCAGATGTAAACAACACGGATAATTATGGTGCTGGGCAAATACAAGTATTAGAAGGTTTAGAAGCAGTACGT +AAAAGACCAGGTATGTATATAGGATCGACTTCAGAGAGAGGTTTGCACCATTTAGTGTGGGAAATTGTCGATAATAGTAT +CGATGAAGCATTAGCTGGTTATGCAAATCAAATTGAAGTTGTTATTGAAAAAGATAACTGGATTAAAGTAACGGATAACG +GACGTGGTATCCCAGTTGATATTCAAGAAAAAATGGGACGTCCAGCTGTCGAAGTTATTTTAACTGTTTTACATGCTGGT +GGTAAATTTGGCGGTGGCGGATACAAAGTATCTGGTGGTTTACATGGTGTTGGTTCATCAGTTGTAAACGCATTGTCACA +AGACTTAGAAGTATATGTACACAGAAATGAGACTATATATCATCAAGCATATAAAAAAGGTGTACCTCAATTTGACTTAA +AAGAAGTTGGCACAACTGATAAGACAGGTACTGTCATTCGTTTTAAAGCAGATGGAGAAATCTTCACAGAGACAACTGTA +TACAACTATGAAACATTACAGCAGCGTATTAGAGAGCTTGCTTTCTTAAACAAAGGAATTCAAATCACATTAAGAGATGA +ACGTGATGAAGAAAACGTTAGAGAAGACTCCTATCACTATGAGGGCGGTATTAAATCGTACGTTGAGTTATTGAACGAAA +ATAAAGAACCTATTCATGATGAGCCAATTTATATTCATCAATCTAAAGATGATATTGAAGTAGAAATTGCGATTCAATAT +AACTCAGGATATGCCACAAATCTTTTAACTTACGCAAATAACATTCATACGTATGAAGGTGGTACGCATGAAGACGGATT +CAAACGTGCATTAACGCGTGTCTTAAATAGTTATGGTTTAAGTAGCAAGATTATGAAAGAAGAAAAAGATAGACTTTCTG +GTGAAGATACACGTGAAGGTATGACAGCAATTATATCTATCAAACATGGTGATCCTCAATTCGAAGGTCAAACGAAGACA +AAATTAGGTAATTCTGAAGTGCGTCAAGTTGTAGATAAATTATTCTCAGAGCACTTTGAACGATTTTTATATGAAAATCC +ACAAGTCGCACGTACAGTGGTTGAAAAAGGTATTATGGCGGCACGTGCACGTGTTGCTGCGAAAAAAGCGCGTGAAGTAA +CACGTCGTAAATCAGCGTTAGATGTAGCAAGCCTTCCAGGTAAATTAGCCGATTGCTCTAGTAAAAGTCCTGAAGAATGT +GAGATTTTCTTAGTCGAAGGGGACTCTGCCGGGGGGTCTACAAAATCTGGTCGTGACTCTAGAACGCAGGCGATTTTACC +ATTACGAGGTAAGATATTAAATGTTGAAAAAGCACGATTAGATAGAATTTTGAATAACAATGAAATTCGTCAAATGATCA +CAGCATTTGGTACAGGAATCGGTGGCGACTTTGATCTAGCGAAAGCAAGATATCACAAAATCGTCATTATGACTGATGCC +GATGTGGATGGAGCGCATATTAGAACATTGTTATTAACATTCTTCTATCGATTTATGAGACCGTTAATTGAAGCAGGCTA +TGTGTATATTGCACAGCCACCGTTGTATAAACTGACACAAGGTAAACAAAAGTATTATGTATACAATGATAGGGAACTTG +ATAAACTTAAATCTGAATTGAATCCAACACCAAAATGGTCTATTGCACGATACAAAGGTCTTGGAGAAATGAATGCAGAT +CAATTATGGGAAACAACAATGAACCCTGAGCACCGCGCTCTTTTACAAGTAAAACTTGAAGATGCGATTGAAGCGGACCA +AACATTTGAAATGTTAATGGGTGACGTTGTAGAAAACCGTAGACAATTTATAGAAGATAATGCAGTTTATGCAAACTTAG +ACTTCTAAGCGCTGTGAACTGAACTTTTGAAGGAGGAACTCTTGATGGCTGAATTACCTCAATCAAGAATAAATGAACGA +AATATTACCAGTGAAATGCGTGAATCATTTTTAGATTATGCGATGAGTGTTATCGTTGCTCGTGCATTGCCAGATGTTCG +TGACGGTTTAAAACCAGTACATCGTCGTATACTATATGGATTAAATGAACAAGGTATGACACCGGATAAATCATATAAAA +AATCAGCACGTATCGTTGGTGACGTAATGGGTAAATATCACCCTCATGGTGACTCATCTATTTATGAAGCAATGGTACGT +ATGGCTCAAGATTTCAGTTATCGTTATCCGCTTGTTGATGGCCAAGGTAACTTTGGTTCAATGGATGGAGATGGCGCAGC +AGCAATGCGTTATACTGAAGCGCGTATGACTAAAATCACACTTGAACTGTTACGTGATATTAATAAAGATACAATAGATT +TTATCGATAACTATGATGGTAATGAAAGAGAGCCGTCAGTCTTACCTGCTCGATTCCCTAACTTATTAGCCAATGGTGCA +TCAGGTATCGCGGTAGGTATGGCAACGAATATTCCACCACATAACTTAACAGAATTAATCAATGGTGTACTTAGCTTAAG +TAAGAACCCTGATATTTCAATTGCTGAGTTAATGGAGGATATTGAAGGTCCTGATTTCCCAACTGCTGGACTTATTTTAG +GTAAGAGTGGTATTAGACGTGCATATGAAACAGGTCGTGGTTCAATTCAAATGCGTTCTCGTGCAGTTATTGAAGAACGT +GGAGGCGGACGTCAACGTATTGTTGTCACTGAAATTCCTTTCCAAGTGAATAAGGCTCGTATGATTGAAAAAATTGCAGA +GCTCGTTCGTGACAAGAAAATTGACGGTATCACTGATTTACGTGATGAAACAAGTTTACGTACTGGTGTGCGTGTCGTTA +TTGATGTGCGTAAGGATGCAAATGCTAGTGTCATTTTAAATAACTTATACAAACAAACACCTCTTCAAACATCATTTGGT +GTGAATATGATTGCACTTGTAAATGGTAGACCGAAGCTTATTAATTTAAAAGAAGCGTTGGTACATTATTTAGAGCATCA +AAAGACAGTTGTTAGAAGACGTACGCAATACAACTTACGTAAAGCTAAAGATCGTGCCCACATTTTAGAAGGATTACGTA +TCGCACTTGACCATATCGATGAAATTATTTCAACGATTCGTGAGTCAGATACAGATAAAGTTGCAATGGAAAGCTTGCAA +CAACGCTTCAAACTTTCTGAAAAACAAGCTCAAGCTATTTTAGACATGCGTTTAAGACGTCTAACAGGTTTAGAGAGAGA +CAAAATTGAAGCTGAATATAATGAGTTATTAAATTATATTAGTGAATTAGAAGCAATCTTAGCTGATGAAGAAGTGTTAT +TACAGTTAGTTAGAGATGAATTGACTGAAATTAGAGATCGTTTCGGTGATGATCGTCGTACAGAAATTCAATTAGGTGGA +TTTGAAGACTTAGAGGACGAAGACTTAATTCCAGAAGAACAAATAGTAATTACACTAAGCCATAATAACTACATTAAACG +TTTGCCGGTATCTACATATCGTGCTCAAAACCGTGGTGGTCGTGGTGTTCAAGGTATGAATACATTGGAAGAAGATTTTG +TCAGTCAATTGGTAACTTTAAGTACACATGACCATGTATTGTTCTTTACTAACAAAGGTCGTGTATACAAACTTAAAGGT +TACGAAGTGCCTGAGTTATCAAGACAGTCTAAAGGTATTCCTGTAGTGAATGCTATTGAACTTGAAAATGATGAAGTCAT +TAGTACAATGATTGCTGTTAAAGACCTTGAAAGTGAAGACAACTTCTTAGTGTTTGCAACTAAACGTGGTGTCGTTAAAC +GTTCAGCATTAAGTAACTTCTCAAGAATAAATAGAAATGGTAAGATTGCGATTTCGTTCAGAGAAGATGATGAGTTAATT +GCAGTTCGCTTAACAAGTGGTCAAGAAGATATCTTGATTGGTACATCACATGCATCATTAATTCGATTCCCTGAATCAAC +ATTACGTCCTTTAGGCCGTACAGCAACGGGTGTGAAAGGTATTACACTTCGTGAAGGTGACGAAGTTGTAGGGCTTGATG +TAGCTCATGCAAACAGTGTTGATGAAGTATTAGTAGTTACTGAAAATGGTTATGGTAAACGTACGCCAGTTAATGACTAT +CGCTTATCAAATCGTGGTGGTAAAGGTATTAAAACAGCTACGATTACTGAGCGTAATGGTAATGTTGTATGTATCACTAC +AGTAACTGGTGAAGAAGATTTAATGATTGTTACTAATGCAGGTGTCATTATTCGACTAGATGTTGCAGATATTTCTCAAA +ATGGTCGTGCAGCACAAGGTGTTCGCTTAATTCGCTTAGGTGATGATCAATTTGTTTCAACGGTTGCTAAAGTAAAAGAA +GATGCAGAAGATGAAACGAATGAAGATGAGCAATCTACTTCAACTGTATCTGAAGATGGTACTGAACAACAACGTGAAGC +GGTTGTAAATGATGAAACACCAGGAAATGCAATTCATACTGAAGTGATTGATTCAGAAGAAAATGATGAAGATGGACGTA +TTGAAGTAAGACAAGATTTCATGGATCGTGTTGAAGAAGATATACAACAATCATCAGATGAAGAATAATAAAAAATAAGA +CTTCCCTATATGTAGGGGAGTCTTATTTTTATGCTAGAAAGTAATGCTTTACTATATTCAATGATTAGTAATGACTAACT +TTCTAATTGTTTCATTGCGTAAGGTATTTCATTGATAAGTCTTGATGGTGGCACCACATACATATCTTTTGCAAGGTTTT +CGCCAATAAAACTATGTGTATATGTGGCACTCATAACCGCTTCTTTTAAGTTATCAAATTGACCGACAAAGCTTGTAATC +ATACCAGCAAGTGTATCGCCCATACCACCAGTCGCCATTGCTGGGCTACCGATTGTCAATTTAAAGTCTTCATCTTTAAA +GAAAATTTCAGTACCATGTTTTTTAAGTACAACAGTTGCACCTAAACGATCAACTGCTTCACGATTACGCTCATATGTCT +GTTCCTCAATAGGAATACCACTTAATCGTTCCCATTCTTTGAGGTGTGGTGTAAAGATCACACGACATGTAGGTAATTGC +GGTTTCAGTTTACTAAAGATTGTAATCGCATCGCCGTCTACGATTAAATTTTGATGCGGTTGTATATTTTGTAGTAGGAA +TGTAATGGCATTATTTCCTTTGAAATCAACGCCAAGACCTGGACCAATTAGTATACTGTCAGTCATTTCAATCATTTTCG +TCAACATTTTCGTATCATTAATATCAATAACCATCGCTTCTGGGCAACGAGAATGTAATGCTGAATGATTTGTTGGATGT +GTAGCTACAGTGATTAAACCACTACCGCTAAATACACATGCACGAGCCGCTAACATAATGGCACCACCTAAGTTAGCAGA +TCCACCAATTAATAAAATTTTGCCATAATCACCTTTATGTGAATCTTCTTTACGCTTAGGAATGTTAATAGAATTTAACG +TTTCCATAGTGATATAACCTCCCATGTAAAAGCCTTTTCCGAATTTATTCAATTTTAAAAATATATAGTAACTTTTAACA +AAATGTATTATAAATTTCTGAATTCATTATTATTTGTCGTTAAATACAATAGAAAATACTATACCTGTATATGCAATTCG +TCAATAGATAAATTATTAAATATGCTTACAACAATCTTAATATCCTTTAACGCACTACAATAGTGCTCTGATAATAGGTT +ATAAATGTACGTAAAACCATTGTTTCAATAAAAATGAAAACGTATACTTCAAGAAGGATGGGTTACTTAATATAAACAAG +GGGGTAACATATATGACTTTATATTTAGATGGTGAAACACTAACAATTGAGGATATTAAATCATTTTTACAACAACAATC +AAAGATTGAAATTATTGATGATGCGTTAGAACGTGTCAAAAAAAGTAGAGCGGTAGTTGAACGTATTATTGAAAATGAGG +AAACGGTTTACGGTATTACTACAGGTTTTGGGTTATTTAGTGATGTACGTATAGACCCGACGCAATATAATGAATTACAA +GTGAATCTGATACGCTCACATGCCTGTGGACTAGGTGAGCCATTTTCAAAAGAAGTAGCATTAGTCATGATGATTTTACG +ATTGAATACATTATTAAAAGGTCATTCAGGTGCCACTTTAGAATTAGTGAGACAATTACAATTTTTTATAAATGAACGTA +TTATACCGATAATCCCACAGCAAGGCTCTCTCGGTGCATCAGGAGATTTAGCGCCATTATCACATTTAGCATTAGCATTA +ATTGGTGAAGGGAAAGTATTGTACAGAGGGGAAGAAAAGGATAGTGACGATGTATTAAGAGAATTAAATAGACAACCTTT +GAACCTTCAGGCTAAAGAAGGTTTAGCATTGATTAATGGTACGCAAGCTATGACAGCTCAAGGTGTCATTAGTTATATAG +AAGCAGAAGATTTAGGTTACCAATCTGAATGGATTGCTGCATTAACGCATCAGTCTCTTAATGGCATTATAGATGCATAT +CGACATGATGTGCACGCAGTTCGTAATTTTCAAGAACAGATTAATGTGGCAGCGCGTATGCGTGATTGGTTAGAAGGATC +AACATTAACGACGCGACAATCAGAAATACGTGTACAAGATGCATATACGTTGCGTTGTATACCACAAATCCATGGCGCGA +GTTTTCAAGTATTCAATTATGTTAAACAGCAATTAGAATTTGAAATGAATGCGGCTAATGATAATCCACTTATATTTGAG +GAAGCAAATGAAACGTTTGTTATTTCAGGTGGTAACTTCCATGGACAACCTATTGCTTTTGCATTAGATCATCTTAAATT +AGGTGTAAGTGAATTAGCAAACGTATCGGAACGTCGTCTAGAGCGACTAGTAAATCCTCAATTAAATGGTGATTTACCAG +CATTTCTTAGTCCAGAGCCAGGATTGCAAAGTGGCGCGATGATTATGCAATATGCTGCTGCAAGTCTCGTTTCTGAAAAT +AAAACTTTAGCGCATCCAGCGAGTGTTGATTCTATCACTTCATCTGCGAACCAAGAAGATCACGTATCTATGGGAACTAC +AGCTGCTAGACATGGTTATCAAATTATTGAAAATGCAAGACGTGTGCTGGCAATCGAATGTGTTATTGCATTACAAGCAG +CAGAGTTAAAAGGTGTTGAAGGATTATCACCAAAAACACGTCGCAAGTATGATGAGTTTCGAAGTATCGTGCCATCCATT +ACACATGATCGTCAATTTCATAAAGATATTGAAGCGGTTGCACAGTATTTAAAGCAATCAATTTATCAAACGACTGCATG +TCACTAAATCGACATGAGTGATGTTTGAAATCGTTACTTGAAAAAGCAAATATAGTTTGCTATATTAAAAATAACTTAAT +AAGACATTGTTCGTAGGACAAGTAATATATAGTGTTCGATATCAGAGAGCTTGTGGTTAGTGTGAACAAGAATCAACATA +TATATGAATCTACCTACTTAATTTAAAAGAACAATCGGTGATAACCGTTATTTTAGTGAAGTGCAATTTAGGTTTAGTGT +ATCTTTATAACTTAAATTGTTAAATAGGGTGGCAACGCGTAGACCACGTCCCTTGTAGGGATGTGGTCTTTTTTTATTTT +CTAAAAAATAAAGACGTACGTCATTCCATAATAAATGATAATTATTTGGGAAAGGATGAAGGTTAATGTTAGACATTAGA +TTATTCAGAAATGAGCCTGACACAGTTAAGAGCAAAATTGAATTACGTGGAGATGATCCAAAAGTTGTAGATGAAATTTT +AGAATTGGATGAGCAACGACGTAAATTAATTAGTGCAACAGAAGAAATGAAAGCACGTCGTAATAAAGTAAGCGAAGAAA +TCGCATTAAAAAAACGTAATAAAGAAAATGCTGATGATGTGATTGCTGAAATGCGCACATTAGGTGACGATATTAAAGAA +AAAGATAGTCAATTAAATGAAATTGATAATAAAATGACAGGTATCCTTTGTCGTATTCCAAATTTAATAAGTGATGATGT +ACCTCAAGGTGAATCTGATGAAGATAACGTTGAAGTTAAAAAGTGGGGTACACCACGTGAGTTTTCATTTGAACCCAAAG +CACATTGGGATATTGTAGAAGAATTGAAAATGGCTGATTTTGATCGTGCAGCAAAAGTTTCAGGTGCGCGTTTTGTATAT +TTAACAAATGAAGGTGCGCAATTAGAGCGTGCTTTAATGAACTATATGATTACAAAACATACAACACAACATGGTTATAC +AGAAATGATGGTACCACAGCTTGTGAACGCAGATACAATGTATGGTACAGGTCAATTACCTAAATTTGAAGAAGATTTAT +TTAAAGTAGAAAAAGAAGGATTATATACAATTCCAACTGCTGAAGTACCATTAACGAATTTCTACCGTAATGAAATTATT +CAACCAGGTGTACTTCCTGAAAAATTCACTGGTCAATCTGCATGTTTCCGTAGTGAAGCAGGATCAGCAGGTAGAGATAC +AAGAGGATTAATTCGTTTACATCAATTCGATAAAGTGGAAATGGTACGTTTTGAACAACCTGAAGATTCATGGAATGCTT +TAGAAGAAATGACAACAAACGCAGAAGCAATTCTAGAAGAGTTAGGTTTACCATACCGTCGTGTTATTTTATGTACAGGT +GATATTGGATTTAGTGCAAGCAAAACATATGATTTAGAAGTTTGGTTACCAAGCTACAATGATTATAAAGAAATTAGTTC +ATGCTCAAACTGTACGGATTTCCAAGCGCGTCGTGCTAACATCCGCTTCAAGCGTGACAAAGCAGCTAAACCAGAATTAG +CACATACATTAAATGGTAGTGGTTTAGCAGTTGGACGTACATTTGCTGCTATTGTTGAAAATTACCAAAATGAAGATGGA +ACAGTAACAATTCCAGAAGCATTAGTACCATTTATGGGTGGTAAAACACAAATTTCAAAACCAGTTAAATAAAGGCTTTA +GCTACAAGCTTTAAAAAGTATATATCTACGTATACTTAAAGCAAGGGCAAGATACTTTAAATAATATTTTAAAAAGTGGT +GACGAAGCTGTCGCCACTTTTTTTGTGCTGTAAAAATATAATAGTGAGCCAATGCACATAACAACAATAAATTAAGTTTG +TGGTTTAATGGGGTGAACGCATTTCATTATAGCAACAATACGGGATAATTATGATGAACTAAAACAATCTAAAACGTAAC +AAGTTTGAGCATCACTAATATAGGAAAGGAAGCGATAAAATACTGATTTCGTTGATATGTAGTATGAGTTATATCGATGG +AGTAGGGTAGGGGGAGGGGATGATTATAAGGGAGTGGTACATGAATCAATATCCCAGACTCATCATCAGATATAAAAATT +TATAAAATTGATACTTAAAAACAACTACAAATCCATAGAAAATATGGAGGTAGTCTTAAATAAAAAATTGAAAATTCTCA +AAAATAAAAAGTTAATATGAAGCTGACTAAAGACTCCGGAATGTCTAACCTCAGACAAACTGATGTCTAATGTTATTGCT +TAGGGTATAGAACTGTATTAGACTAGGTATATTATTTTTTCGTAATTATATAAATATAAAGTGGCAAAGGAGGTAATTGA +GATGACAACACATTTAAGTTTTAGACAAGGCGTGCAAGAGTGTATCCCAACATTATTGGGTTATGCCGGTGTTGGTATTT +CATTTGGTATTGTGGCTTCGTCTCAAAACTTTAGTATTTTAGAAATTGTCTTGTTATGTCTTGTTATATATGCCGGTGCT +GCGCAATTTATTATGTGCGCGTTGTTTATAGCAGGTACACCTATATCAGCGATTGTACTAACTGTATTTATCGTAAATTC +AAGAATGTTCCTTTTAAGTATGTCGCTTGCACCAAACTTCAAGACATATGGGTTTTGGAACCGTGTTGGATTAGGTTCAT +TAGTAACTGACGAAACGTTTGGCGTCGCCATTACACCTTATTTAAAAGGAGAAGCTATCAATGATCGTTGGATGCATGGT +CTTAACATCACAGCATATTTATTTTGGGCAATTTCATGTGTAGCTGGGGCTTTATTTGGCGAATATATCTCAAATCCGCA +AACGCTAGGGTTAGATTTTGCTATCACGGCTATGTTTATCTTTTTGGCCATTGCGCAATTTGAATCAATTACTAAATCGC +GATTAAGAATTTACATAGTACTCATTATTGCCGTCATAGTAATGATGTTATCGCTAAGTATGTTTATGCCTTCATATCTA +GCAATATTAATTGCAGCCACAATTTCAGCAGCGTTAGGAGTGATGATGGAACAATGATAACTCATATGAACATGTTAATA +CTTATTTTATTGTGTGGTATCGTAACGCTATTAATTCGAATTATACCTTTTATCATGATTTCAAAAGTGCAATTGCCTGA +TGTCGTGGTTCGATGGCTATCATTTATCCCAATCACACTATTTACGGCACTTGTCATTGACAGCATTATTCAACAGACGC +CTCATGGTGAGGGGTATACATTAAACATCCCTTACATTATCGCGCTCATTCCGACGGTTATTTTATCTATAATCACGCGT +AGTTTAACTATTACAATTATTAGTGGGATTGTTATCATGGCAGCATTACGATTTTTCTTTTAAAAATAACTGAAAATCAT +TGAACAGATATTTAAACTTAGGTAAACTGTTAGTAATCAGAAAATTCTGATATACAAATCGTCTATGTTAAGATTGGGAA +TCGTGTATACGTAAGCGATTTGGCTAACGCATGACAATGATAACAACATTTTAAATGATAAAAGTAATTCATCACTGAAT +CTCAACTAACACATAACAATTTCATATTTCTTATTGTGAGAAGTTGAGGGACTTGGCCCTGTGATACTTCAGCAACCGAC +TTTATAGCACGGTGCTAAAACCAACGAGTTACTCGAATGATAAGTATAAAGACTTCTTACTTTTCAATAGGGTGAGAAGT +TTTTTTGTTTAAGGAGGAAAGAACAATGACAAATTACACAGTAGATACTTTAAATTTAGGGGAATTTATTACAGAATCTG +GGGAAGTCATAGATAACTTGCGTTTGAGATATGAGCATGTCGGTTATCATGGACAACCATTAGTTGTAGTTTGTCATGCA +TTAACTGGCAATCATTTAACATATGGAACAGATGATTATCCGGGTTGGTGGCGAGAAATTATTGATGGGGGATATATACC +CATTCACGATTATCAATTTTTAACATTTGATGTTATTGGTAGTCCTTTCGGTTCAAGTTCACCTTTAAACGACCCTCATT +TTCCTAAAAAATTAACATTAAGAGATATAGTCAGAGCGAATGAACGAGGTATACAAGCCCTTGGTTATGATAAGATTAAT +ATTTTAATAGGGGGAAGTCTTGGAGGTATGCAAGCAATGGAACTACTCTACAATCAACAGTTTGAAGTAGATAAAGCCAT +TATCCTTGCTGCAACAAGTCGAACATCATCTTATAGTAGAGCTTTCAATGAAATTGCAAGGCAAGCCATTCATCTTGGTG +GTAAGGAAGGTCTAAGTATTGCACGCCAATTAGGTTTTTTGACATATCGATCATCAAAAAGTTATGATGAACGTTTTACG +CCGGATGAAGTAGTCGCATACCAACAACATCAAGGTAATAAATTTAAAGAACATTTTGATTTGAATTGTTATCTGACACT +GCTAGATGTATTGGATAGTCACAACATTGACCGAGGTCGCACAGACGTAACGCATGTTTTTAAAAATTTAGAAACAAAAG +TGTTAACGATGGGGTTCATAGATGATTTGCTATATCCGGATGATCAAGTTCGTGCATTAGGTGAACGTTTTAAATATCAT +CGTCATTTCTTCGTGCCTGATAATGTTGGGCATGATGGATTCCTACTAAACTTTAGTACCTGGGCACCTAACTTATATCA +TTTCTTAAATTTAAAGCATTTTAAGCGTAAGTAATAATATATATTTTCAAAGAATGAAGCCACTAAATCACAGTTATCAT +ATTCAAAAACATCTAGAGTACATGAGAGTGGAAATGTATTGTAAATCTAAATCAAGCTTGATTGCTATGTAGTGGGATTA +AATAATCATGTTGAAAATGAAAGCACACAAAAGCGATTCAAATGAGTATGTTAATTACTTATAAAACATGAAAGTTATTA +TTCAATTAAATGAAATAGAAGCATACAATCTATAAATTGTTAATTTTCATTAAAGAGGTTAAAATAATAGCTATAGTTAA +AAATATGGGAGTGGCTTATGTGTTTTCAAAAATACAACCTAAAGCAACAATAATTGCAACGATTGCGTTGGTATTTGTCG +CTTTAGCTTTATATCTAGTGCCTGGTTTAGGACTAATATTTGCATTATTTGCAACCATACCAGGTATCGTTTTATGGAAT +AAATCAATACAATCTTTCGGGATTAGTGCACTTATTACAGTAATTATAACAACTGTTTTAGGTAATACTTTCGTTTTAAG +TGCCATCATATTAGTCTTAATTGCAAGTTTAATTATTGGTCAATTGCTCAAAGAAAGAACGTCTAAAGAAAGAATATTAT +ACGTAACAACAGTAGCGATGAGCTTAATTTCATTAATCGCTTTTATGTTACTACAAACATTCGGAAGGATTCCACCATCA +GCGAGCATAGTAAAACCTTTCAAGCAAACATTACATGAAGCGATTACGATGAGCGGTGCCGATGCGAATATGACCCAAAT +ATTAGAAGAAGGGTTTAGACAAGCGACCGTTCAATTACCAGGTTTCATCATTATCATTACATTTTTAATCGTCTTAATTA +ACTTAATCGTTACATTTCCGATTTTACGAAAATTTAAAATCGCTACACCTGTATTTAAGCCACTTTTCGCGTGGCAAATG +AGCGGTATTTTATTATGGATATACATTATTGTTATCATATGTTTATTATTTACAGGTCAACCGAGTGTGTTCCAGAGCAT +TCTTTTAAACTTCCAACTTGTGTTATCATTAGTAATGTATATTCAAGGTTTAAGTGTTATTCATTTCTTTGGTAAAGCGA +AAGGTTTGCCGAATGCAGTAACGATTTTACTATTGGTTATCGGTACAATACTGACACCTACGACACATATTGTAGGACTA +CTTGGTGTTATCGATTTAAGTTTGAATTTGAAGCGAATCATGAAAAATAATTCTAAAAAGTGAATAGAGGTGGAATAATG +AATCGGCAGTCCACTAAGAAAGCTTTACTAATACCATTTGTCATCATGATCATCACAGCAATTGTTTTAATGGGTGTATG +GTTTATCTTTAATAGTCTTATAGCACTAATTGCATCTATCGTTCTTGTCGTGATGATTATTGTTAGCATCATTTTATTCA +GACAAGCTTTAATGAAAATGGATAGTTATGTAGATGGTTTGAGTGCTCAAATTTCAACAACAAATAATAAAGCAATCAAA +CATTTACCAATTGGTATCATTGTTTTAGATGAAAATGATCACATCGAATGGGTTAACCAATTTATGACAGATCATATGGA +AGCGAATGTCATTTCTGAATCTGTAAATGAAGTATTTCCAAACATTTTAAAGCAATTAGATAGAGTGAAATCCGTTGAAA +TAGAATATAATCAGTATCATTTCCAAGTACGTTATTCTGAGAATGATCACTGCCTCTATTTCTTTGATATAACTGAACAA +GTACAAACAAATGAACTATATGAAAATTCTAAACCAATCATTGCGACATTATTTTTAGATAACTACGATGAGATTACGCA +AAATATGAATGATACGCAGCGTTCGGAAATCAACTCAATGGTAACGCGTGTCATTAGTCGATGGGCAACTGAGTATAATA +TATTTTTCAAAAGATACAGTTCCGATCAATTCGTAGCCTATTTAAATCAAAAAATATTAGCTGACTTAGAAGAATCTAAA +TTTGATATCTTGAGTCAATTACGTGAAAAAAGTGTTGGTTATCGTGCCCAATTAACATTAAGTATCGGTGTTGGTGAAGG +TACTGAAAATTTAATCGACTTAGGTGAATTATCACAATCAGGCCTAGACTTAGCATTAGGACGCGGTGGCGACCAAGTTG +CAATTAAAAGTATTAATGGTAATGTGCGTTTCTATGGCGGTAAGACTGACCCGATGGAGAAACGTACTCGTGTAAGAGCA +CGTGTGATCTCACATGCGTTAAAAGATATCCTTGCAGAGGGTGACAAAGTCATTATCATGGGACATAAACGTCCTGACTT +AGATGCAATTGGTGCAGCAATCGGTGTGTCTAGATTTGCAATGATGAATAATTTAGAAGCATACATCGTATTAAATGAGA +CTGACATTGATCCAACATTACGACGCGTGATGAACGAAATAGATAAAAAGCCAGAGTTAAGAGAGCGATTTATTACATCA +GATGATGCTTGGGATATGATGACATCTAAGACAACCGTAGTGATTGTTGATACGCATAAACCGGAACTGGTTTTAGATGA +AAATGTCTTAAATAAAGCAAACCGTAAAGTTGTTATCGATCATCATAGACGTGGTGAAAGCTTCATCTCTAATCCATTGT +TGATATATATGGAACCATACGCAAGTTCGACAGCTGAATTGGTAACAGAGTTACTGGAATATCAACCAACAGAACAACGT +TTAACACGTCTTGAATCAACAGTGATGTATGCAGGTATTATTGTAGATACAAGAAACTTTACATTACGAACAGGATCAAG +AACATTCGATGCAGCGAGTTATTTACGTGCACATGGTGCAGATACGATTTTAACGCAACATTTCTTAAAAGATGATGTGG +ATACTTACATTAATCGATCTGAATTAATTCGAACTGTAAAAGTTGAAGATAATGGCATAGCCATTGCGCATGGTTCAGAC +GATAAAATTTATCATCCAGTAACAGTTGCACAAGCAGCAGATGAACTGTTAAGTTTAGAAGGTATTGAAGCATCATATGT +TGTTGCGAGACGTGAAGATAATCTGATTGGTATATCTGCGCGTTCACTCGGTTCAGTAAATGTCCAGTTAACAATGGAAG +CACTTGGTGGCGGTGGACATTTAACCAATGCGGCAACACAACTTAAAGGTGTGACAGTCGAAGAGGCGATAGCACAATTA +CAACAAGCAATTACAGAACAATTAAGTAGGAGTGAAGATGCATGAAAGTAATTTTTACACAAGATGTTAAAGGTAAAGGT +AAAGGTAAAAAAGGTGAAGTTAAAGAAGTACCAGTAGGTTATGCAAATAACTTCTTATTGAAAAAGAATTATGCTGTAGA +AGCAACACCAGGTAACCTTAAACAATTAGAGTTACAGAAAAAACGTGCAAAACAAGAACGCCAACAAGAAATTGAAGATG +CTAAAGCATTAAAAGAAACGTTATCAAACATTGAAGTTGAAGTATCAGCAAAAACTGGTGAAGGTGGTAAATTGTTTGGG +TCAGTAAGTACAAAACAAATTGCCGAAGCACTAAAAGCACAACATGATATTAAAATTGATAAACGTAAAATGGATTTACC +AAATGGAATTCATTCCCTAGGATATACGAATGTACCTGTTAAATTAGATAAAGAAGTTGAAGGTACAATTCGCGTACACA +CAGTTGAACAATAAAGTTGGATTGAAATAAGAGGTGTAACCATTCATGGATAGAATGTATGAGCAAAATCAAATGCCGCA +TAACAATGAAGCTGAACAGTCTGTCTTAGGTTCAATTATTATAGATCCAGAATTGATTAATACTACTCAGGAAGTTTTGC +TTCCTGAGTCGTTTTATAGGGGTGCCCATCAACATATTTTCCGTGCAATGATGCACTTAAATGAAGATAATAAAGAAATT +GATGTTGTAACATTGATGGATCAATTATCGACGGAAGGTACGTTGAATGAAGCGGGTGGCCCGCAATATCTTGCAGAGTT +ATCTACAAATGTACCAACGACGCGAAATGTTCAGTATTATACTGATATCGTTTCTAAGCATGCATTAAAACGTAGATTGA +TTCAAACTGCAGATAGTATTGCCAATGATGGATATAATGATGAACTTGAACTAGATGCGATTTTAAGTGATGCAGAACGT +CGAATTTTAGAGCTATCATCTTCTCGTGAAAGCGATGGCTTTAAAGACATTCGAGACGTCTTAGGACAAGTGTATGAAAC +AGCTGAAGAGCTTGATCAAAATAGTGGTCAAACACCAGGTATACCTACAGGATATCGAGATTTAGACCAAATGACAGCAG +GGTTCAACCGAAATGATTTAATTATCCTTGCAGCGCGTCCATCTGTAGGTAAGACTGCGTTCGCACTTAATATTGCACAA +AAAGTTGCAACGCATGAAGATATGTATACAGTTGGTATTTTCTCGCTAGAGATGGGTGCTGATCAGTTAGCCACACGTAT +GATTTGTAGTTCTGGAAATGTTGACTCAAACCGCTTAAGAACGGGTACTATGACTGAGGAAGATTGGAGTCGTTTTACTA +TAGCGGTAGGTAAATTATCACGTACGAAGATTTTTATTGATGATACACCGGGTATTCGAATTAATGATTTACGTTCTAAA +TGTCGTCGATTAAAGCAAGAACATGGCTTAGACATGATTGTGATTGACTACTTACAGTTGATTCAAGGTAGTGGTTCACG +TGCGTCCGATAACAGACAACAGGAAGTTTCTGAAATCTCTCGTACATTAAAAGCATTAGCCCGTGAATTAAAATGTCCAG +TTATCGCATTAAGTCAGTTATCTCGTGGTGTTGAACAACGACAAGATAAACGTCCAATGATGAGTGATATTCGTGAATCT +GGTTCGATTGAGCAAGATGCCGATATCGTTGCATTCTTATACCGTGATGATTACTATAACCGTGGCGGCGATGAAGATGA +TGACGATGATGGTGGTTTCGAGCCACAAACGAATGATGAAAACGGTGAAATTGAAATTATCATTGCTAAGCAACGTAACG +GTCCAACAGGCACAGTTAAGTTACATTTTATGAAACAATATAATAAATTTACCGATATCGATTATGCACATGCAGATATG +ATGTAAAAAAGTTTTTCCGTCCAATAATCATTAAGATGATAAAATTGTACGGTTTTTATTTTGTTCTGAACGGGTTGATA +TATGTTAAGTTTGTGTATTGAAAGTGATAAATTAGTACTGTCAACGCCTCTGTTAAAGGGTTTTTAGGACGTTGAAAACG +ATTTGTTAAAATGATTTTTCTTTTAAAAAGGCCGAAAATCAATGTTCGATTTTTATTTGCATTATGGTCTCGATATTGGT +AGAATATCAAATGGTTAAATGAGAAAAACTTGGAGGTGCTCACATGTCATCAATCGTAGTAGTTGGGACACAATGGGGAG +ACGAAGGAAAAGGAAAAATAACGGATTTCTTGGCAGAACAGTCAGATGTTATCGCGCGTTTTTCAGGTGGTAATAATGCA +GGCCATACCATTCAATTTGGCGGAGAAACATATAAATTACATTTAGTACCATCTGGTATCTTTTACAAAGACAAATTAGC +GGTAATCGGTAACGGAGTCGTTGTTGATCCAGTTGCACTATTGAAAGAATTAGACGGATTAAATGAACGTGGCATTCCTA +CAAGTAATTTACGTATATCTAATCGTGCGCAAGTGATTTTACCATATCACTTAGCACAAGATGAATATGAAGAACGTTTA +CGTGGTGACAATAAGATTGGTACAACTAAAAAAGGTATCGGTCCAGCATATGTAGACAAAGTTCAACGTATCGGTATTCG +TATGGCAGATTTACTTGAAAAAGAAACATTCGAAAGATTATTAAAATCAAACATTGAATATAAACAAGCATATTTCAAAG +GTATGTTTAACGAAACATGTCCATCATTTGATGATATCTTTGAAGAATATTATGCAGCAGGTCAACGTCTAAAAGAATTT +GTAACAGACACATCAAAAATCTTAGACGATGCATTTGTAGCAGATGAAAAGGTACTTTTCGAAGGTGCGCAAGGTGTAAT +GTTAGATATCGACCATGGTACATATCCATTCGTTACATCAAGTAATCCAATTGCAGGTAACGTTACTGTTGGTACAGGTG +TAGGTCCTACATTCGTTTCAAAGGTAATTGGTGTATGTAAAGCTTATACATCACGTGTTGGTGATGGTCCATTCCCTACT +GAATTATTCGATGAAGATGGACATCATATTAGAGAGGTTGGTCGTGAATACGGTACAACAACAGGACGTCCACGTCGTGT +AGGTTGGTTTGATTCAGTTGTATTACGTCACTCTCGTCGTGTAAGTGGTATTACAGATTTATCTATTAACTCAATCGATG +TTTTAACAGGCCTAGACACAGTGAAAATCTGTACAGCTTATGAATTAGACGGTAAAGAAATTACTGAGTACCCAGCAAAC +TTAGATCAATTAAAACGTTGTAAACCAATCTTTGAAGAGTTACCAGGTTGGACAGAAGACGTAACAAATGTGCGTACTTT +AGAAGAATTACCTGAAAATGCACGTAAATATTTAGAGCGTATTTCAGAATTATGTAATGTACAAATTTCTATCTTCTCAG +TTGGTCCAGATAGAGAACAAACAAACCTATTAAAAGAATTGTGGTAGAACTTTATATAAGTCATACACAATGATTATAAA +TACATGAGCCTTCTATCTTTATTGGTAGGAGGCTTTTGTTATGCTTGCTTCTGTATCGATTCGATTATTTAGATAAAAAA +TACTAACGTAAAGGCGATATTTGCTAGTCATAATTTAGAAGATTAGATGATATTTAACGAAAATTAAGATGAAATACTTG +AATGTAAGAAGTCTGATGTCGAAAATAGCTATTAAAATAGAGTAGACGTAAGTGTAAATGAAAGCACCTAAAATAGAAAA +ATTTCAAAAATAGCGTAATTATTATAATAAATAGACTGCCAATAAAATGCAATTTTTCACTTATAACATTCTTCAAAAAA +TAATAGCAAAATTATGTAAAAAATATCTTGTCATGGCAAGATTGGCTGTGCTATAATCTATCTTGTGCTTAAGAACGGCT +CCTTGGTCAAGCGGTTAAGACACCGCCCTTTCACGGCGGTAACACGGGTTCGAGTCCCGTAGGAGTCACCATTTTTTAGG +TCTCGTAGTGTAGCGGTTAACACGCCTGCCTGTCACGCAGGAGATCGCGGGTTCGATTCCCGTCGAGACCGTACAAATGC +CTATCCAAGAGGATAGGCATTTTTTTGCGTTTAATATTATATTAATAAAAGATATATGGACGAATGATAATCATATTGAT +TTATCTGTTCGTCCATTTTCTTTAAAATGTATGAACCTCAAGTAACTTAGTGGTTGGATATGAAAGATAAACGTAGACAA +TAAAATCTTTATTAGACGTACAAACATATGCTACTGTCAACATATTTCTTCGTTGTGATATGCCACCAGTCCTCCATAAC +ATCAATTGTTAAAGTAACGAATAACGAATAATGATATTTATTTTCTGAGCAATGACGTGCAACTAGAAGTTGCCATTATC +CTAATTTTATTATTGGAATAGAGACCTCATCATTGTGTTAAATATCATTGTCACAATCCGCCGTGAGAAACTAATAAAAA +ATAGTAATATATAAGTTTATATTGGAAAATAGAATTAATAGCTTATAAATGGTAAATTATATAATAGGTTACTATACGTT +ATAAGACGGAAAATGCGCACAATAACAAAAATAGTAAGCGACATCCTGTGATTTTTTACACAAACATAAACGATAAAGAA +CAAAAAATGATAAAATAATATTAATGATTTAAGAAAAGAGGTTTATGCAAATGGCTAGAAAAGTTGTTGTAGTTGATGAT +GAAAAACCGATTGCTGATATTTTAGAATTTAACTTAAAAAAAGAAGGATACGATGTGTACTGTGCATACGATGGTAATGA +TGCAGTCGACTTAATTTATGAAGAAGAACCAGACATCGTATTACTAGATATCATGTTACCTGGTCGTGATGGTATGGAAG +TATGTCGTGAAGTGCGCAAAAAATACGAAATGCCAATAATAATGCTTACTGCTAAAGATTCAGAAATTGATAAAGTGCTT +GGTTTAGAACTAGGTGCAGATGACTATGTAACGAAACCGTTTAGTACGCGTGAATTAATCGCACGTGTGAAAGCGAACTT +ACGTCGTCATTACTCACAACCAGCACAAGACACTGGAAATGTAACGAATGAAATCACAATTAAAGATATTGTGATTTATC +CAGACGCATATTCTATTAAAAAACGTGGCGAAGATATTGAATTAACACATCGTGAATTTGAATTGTTCCATTATTTATCA +AAACATATGGGACAAGTAATGACACGTGAACATTTATTACAAACAGTATGGGGCTATGATTACTTTGGCGATGTACGTAC +GGTCGATGTAACGATTCGTCGTTTACGTGAAAAGATTGAAGATGATCCGTCACATCCTGAATATATTGTGACGCGTAGAG +GCGTTGGATATTTCCTCCAACAACATGAGTAGAGGTCGAAACGAATGAAGTGGCTAAAACAACTACAATCCCTTCATACT +AAACTTGTAATTGTTTATGTATTACTGATTATCATTGGTATGCAAATTATCGGGTTATATTTTACAAATAACCTTGAAAA +AGAGCTGCTTGATAATTTTAAGAAGAATATTACGCAGTACGCGAAACAATTAGAAATTAGTATTGAAAAAGTATATGACG +AAAAGGGCTCCGTAAATGCACAAAAAGATATTCAAAATTTATTAAGTGAGTATGCCAACCGTCAAGAAATTGGAGAAATT +CGTTTTATAGATAAAGACCAAATTATTATTGCGACGACGAAGCAGTCTAACCGTAGTCTAATCAATCAAAAAGCGAATGA +TAGTTCTGTCCAAAAAGCACTATCACTAGGACAATCAAACGATCATTTAATTTTAAAAGATTATGGCGGTGGTAAGGACC +GTGTCTGGGTATATAATATCCCAGTTAAAGTCGATAAAAAGGTAATTGGTAATATTTATATCGAATCAAAAATTAATGAC +GTTTATAACCAATTAAATAATATAAATCAAATATTCATTGTTGGTACAGCTATTTCATTATTAATCACAGTCATCCTAGG +ATTCTTTATAGCGCGAACGATTACCAAACCAATCACCGATATGCGTAACCAGACGGTCGAAATGTCCAGAGGTAACTATA +CGCAACGTGTGAAGATTTATGGTAATGATGAAATTGGCGAATTAGCTTTAGCATTTAATAACTTGTCTAAACGTGTACAA +GAAGCGCAGGCTAATACTGAAAGTGAGAAACGTAGACTGGACTCAGTTATCACCCATATGAGTGATGGTATTATTGCAAC +AGACCGCCGTGGACGTATTCGTATCGTCAATGATATGGCACTCAAGATGCTTGGTATGGCGAAAGAAGACATCATCGGAT +ATTACATGTTAAGTGTATTAAGTCTTGAAGATGAATTTAAACTGGAAGAAATTCAAGAGAATAATGATAGTTTCTTATTA +GATTTAAATGAAGAAGAAGGTCTAATCGCACGTGTTAACTTTAGTACGATTGTGCAGGAAACAGGATTTGTAACTGGTTA +TATCGCTGTGTTACATGACGTAACTGAACAACAACAAGTTGAACGTGAGCGTCGTGAATTTGTTGCCAATGTATCACATG +AGTTACGTACACCTTTAACTTCTATGAATAGTTACATTGAAGCACTTGAAGAAGGTGCATGGAAAGATGAGGAACTTGCG +CCACAATTTTTATCTGTTACCCGTGAAGAAACAGAACGAATGATTCGACTGGTCAATGACTTGCTACAGTTATCTAAAAT +GGATAATGAGTCTGATCAAATCAACAAAGAAATTATCGACTTTAACATGTTCATTAATAAAATTATTAATCGACATGAAA +TGTCTGCGAAAGATACAACATTTATTCGAGATATTCCGAAAAAGACGATTTTCACAGAATTTGATCCTGATAAAATGACG +CAAGTATTTGATAATGTCATTACAAATGCGATGAAATATTCTAGAGGCGATAAACGTGTCGAGTTCCACGTGAAACAAAA +TCCACTTTATAATCGAATGACGATTCGTATTAAAGATAATGGCATTGGTATTCCTATCAATAAAGTCGATAAGATATTCG +ACCGATTCTATCGTGTAGATAAGGCACGTACGCGTAAAATGGGTGGTACTGGATTAGGACTAGCCATTTCGAAAGAGATT +GTGGAAGCGCACAATGGTCGTATTTGGGCAAACAGTGTAGAAGGTCAAGGTACATCTATCTTTATCACACTTCCATGTGA +AGTCATTGAAGACGGTGATTGGGATGAATAATAAGGAGCATATTAAATCTGTCATTTTAGCACTACTCGTCTTGATGAGT +GTCGTATTGACATATATGGTATGGAACTTTTCTCCTGATATTGCAAATGTCGACAATACAGATAGTAAGAAGAGTGAAAC +GAAACCTTTAACGACACCTATGACAGCCAAAATGGATACAACTATTACGCCATTTCAGATTATTCATTCGAAAAATGATC +ATCCAGAAGGAACGATTGCGACGGTATCTAATGTGAATAAACTGACGAAACCTTTGAAAAATAAAGAAGTGAAGTCCGTG +GAACATGTTCGTCGTGATCATAACTTGATGATTCCTGATTTGAACAGTGATTTTATATTATTCGATTTTACGTATGATTT +ACCGTTATCAACATATCTTGGTCAAGTACTGAACATGAATGCGAAAGTACCAAATCATTTCAATTTCAATCGTTTGGTCA +TAGATCATGATGCTGATGATAATATCGTGCTTTATGCTATAAGCAAAGATCGCCACGATTACGTAAAATTAACAACTACA +ACGAAAAATGATCATTTTTTAGATGCATTAGCAGCAGTGAAAAAAGATATGCAACCATACACAGATATCATCACAAACAA +AGATACAATTGATCGTACGACGCATGTTTTTGCACCAAGTAAACCTGAAAAGTTAAAAACATATCGCATGGTATTTAACA +CGATTAGTGTTGAGAAAATGAATGCTATACTATTTGACGATTCAACCATCGTTCGTAGTTCAAAGAGTGGTGTTACAACC +TACAACAATAATACAGGTGTCGCAAACTATAACGATAAAAATGAAAAATATCATTATAAAAACCTGTCCGAAGATGAAGC +GAGTTCCAGCAAAATGGAAGAAACGATTCCAGGAACCTTTGATTTTATTAATGGTCATGGTGGTTTCTTAAACGAAGACT +TTAGATTGTTTAGTACGAATAATCAGTCAGGCGAGTTAACATATCAACGTTTCCTTAATGGTTATCCAACGTTTAATAAA +GAAGGTTCTAATCAAATTCAAGTCACTTGGGGTGAAAAAGGCGTCTTTGACTATCGTCGTTCGTTATTACGCACCGACGT +TGTTTTAAATAGTGAGGATAATAAATCGTTGCCGAAATTAGAGTCTGTACGTTCAAGCTTAGCGAACAATAGTGATATTA +ATTTTGAAAAAGTAACAAACATCGCTATCGGTTACGAAATGCAGGATAATTCAGATCATAATCACATTGAAGTGCAGATT +AACAGTGAACTCGTACCGCGTTGGTATGTAGAATATGATGGCGAATGGTATGTTTATAACGATGGGAGGCTTGAATAAAT +GAACTGGAAACTGACAAAGACACTTTTCATTTTCGTGTTTATTCTTGTCAACATCGTGTTAGTATCGATTTATGTTAATA +AAGTCAATCGCTCACACATTAATGAAGTCGAGAGTAACAATGAAGTTAATTTTCAGCAAGAAGAAATTAAAGTACCGACT +AGTATATTGAATAAATCAGTTAAAGGTATAAAATTAGAGCAAATTACAGGGCGATCAAAAGACTTTAGTTCTAAAGCTAA +AGGCGATTCGGATTTGACCACATCAGATGGTGGAAAATTATTGAATGCGAACATTAGTCAATCGGTAAAGGTCAGTGACA +ATAACTTAAAAGATTTGAAAGATTATGTTAACAAGCGCGTATTTAAAGGTGCTGAATATCAATTAAGCGAGATTAGTTCA +GATTCTGTAAAATATGAACAAACGTATGATGATTTTCCGATTTTAAATAACAGTAAAGCGATGTTAAACTTTAATATAGA +AGATAACAAAGCGACTAGTTATAAACAATCAATGATGGATGACATTAAGCCCACAGATGGTGCAGATAAGAAGCATCAAG +TGATTGGTGTGAGAAAAGCAATCGAGGCATTATATTATAATCGTTACTTGAAAAAAGGTGATGAAGTCATTAATGCTAGA +CTCGGTTACTACTCAGTCGTGAATGAAACGAATGTTCAATTGTTACAACCAAACTGGGAAATTAAAGTGAAGCATGACGG +TAAGGATAAAACGAATACTTACTATGTCGAAGCGACAAATAATAACCCTAAAATTATTAATCATTAATATGAATCGTAAT +AAGCTAGCATTGCAAGCTCATCATATGTGAGAAGCGGTGCTAGCTTTTTTGCTGGTACGGTTTATTATGGCTGATGTTTT +TGCGTCTCCAACGTGCGCATTTATTCATATTTTAAGTAGAACCGCATTGTAAAATTAGTGTAACTGTTATTTTAAAAACT +TTAGTATTTGTCTAATCATTGTTATAATAATTAAGAAATTCATTGCACGTGATTATCAAAATTTAAATATAAGAAACCGG +TCGATGAACTAAAGTTACATAATAGGAAAGGTATACAAAACAGCTAATATACTGATAGTTTCTGTAGGGAAAATCGTATA +TTTGCACTGATGTATATTGCAGTCATATAGAGAGATTGACTGTTTAAAGAGAAAGGATGAGCCGCTTGATACGCATGAGT +GTATTAGCAAGTGGTAGTACAGGTAACGCCACTTTTGTAGAAAATGAAAAAGGTAGTCTATTAGTTGATGTTGGTTTGAC +TGGAAAGAAAATGGAAGAATTGTTTAGTCAAATTGACCGTAATATTCAAGATTTAAATGGTATTTTAGTAACCCATGAAC +ATATTGATCATATTAAAGGATTAGGTGTTTTGGCGCGTAAATATCAATTGCCAATTTATGCGAATGAAAAAACTTGGCAG +GCAATTGAAAAGAAAGATAGTCGCATCCCTATGGATCAGAAATTCATTTTTAATCCTTATGAAACAAAATCTATTGCAGG +TTTCGATGTTGAATCGTTTAACGTGTCACATGATGCAATAGATCCGCAATTTTATATTTTCCATAATAACTATAAGAAGT +TTACGATTTTAACGGATACGGGTTACGTGTCTGATCGTATGAAAGGTATGATACGTGGCAGCGATGCGTTTATTTTTGAG +AGTAATCATGACGTCGATATGTTGAGAATGTGTCGTTATCCATGGAAGACGAAACAACGTATTTTAGGCGATATGGGTCA +TGTATCTAATGAGGATGCGGCTCATGCAATGACAGACGTGATTACAGGTAACACGAAACGTATTTACCTATCGCATTTAT +CACAAGACAATAACATGAAAGATTTGGCGCGTATGAGTGTTGGCCAAGTATTGAACGAACACGATATTGATACGGAAAAA +GAAGTATTGCTATGTGATACGGATAAAGCTATTCCAACGCCAATATATACAATATAAATGAGAGTCATCCGATAAAGTTC +CGCATTGCTGTGAGACGACTTTATCGGGTGCTTTTTTATGTTGTTGGTGGGAAATGGCTGTTGTTGAGTTGAATCGGCTT +GATTGAAATGTGTAAAATAATTCGATATTAAATGTAATTTATAAATAATTTACATAAAATCAATCATTTTAATATAAGGA +TTATGATAATATATTGGTGTATGACAGTTAATGGAGGGAACGAAATGAAAGCTTTATTACTTAAAACAAGTGTATGGCTC +GTTTTGCTTTTTAGTGTAATGGGATTATGGCAAGTCTCGAACGCGGCTGAGCAGCATACACCAATGAAAGCACATGCAGT +AACAACGATAGACAAAGCAACAACAGATAAGCAACAAGTACCGCCAACAAAGGAAGCGGCTCATCATTCTGGCAAAGAAG +CGGCAACCAACGTATCAGCATCAGCGCAGGGAACAGCTGATGATACAAACAGCAAAGTAACATCCAACGCACCATCTAAC +AAACCATCTACAGTAGTTTCAACAAAAGTAAACGAAACACGCGACGTAGATACACAACAAGCCTCAACACAAAAACCAAC +TCACACAGCAACGTTCAAATTATCAAATGCTAAAACAGCATCACTTTCACCACGAATGTTTGCTGCTAATGCACCACAAA +CAACAACACATAAAATATTACATACAAATGATATCCATGGCCGACTAGCCGAAGAAAAAGGGCGTGTCATCGGTATGGCT +AAATTAAAAACAGTAAAAGAACAAGAAAAGCCTGATTTAATGTTAGACGCAGGAGACGCCTTCCAAGGTTTACCACTTTC +AAACCAGTCTAAAGGTGAAGAAATGGCTAAAGCAATGAATGCAGTAGGTTATGATGCTATGGCAGTCGGTAACCATGAAT +TTGACTTTGGATACGATCAGTTGAAAAAGTTAGAGGGTATGTTAGACTTCCCGATGCTAAGTACTAACGTTTATAAAGAT +GGAAAACGCGCGTTTAAGCCTTCAACGATTGTAACAAAAAATGGTATTCGTTATGGAATTATTGGTGTAACGACACCAGA +AACAAAGACGAAAACAAGACCTGAAGGCATTAAAGGCGTTGAATTTAGAGATCCATTACAAAGTGTGACAGCGGAAATGA +TGCGTATTTATAAAGACGTAGATACATTTGTTGTTATATCACATTTAGGAATTGATCCTTCAACACAAGAAACATGGCGT +GGTGATTACTTAGTGAAACAATTAAGTCAAAATCCACAATTGAAGAAACGTATTACAGTTATTGATGGTCATTCACATAC +AGTACTTCAAAATGGTCAAATTTATAACAATGATGCATTGGCACAAACAGGTACAGCACTTGCGAATATCGGTAAGATTA +CATTTAATTATCGCAATGGAGAGGTATCGAATATTAAACCGTCATTGATTAATGTTAAAGACGTTGAAAATGTAACACCG +AACAAAGCATTAGCTGAACAAATTAATCAAGCTGATCAAACATTTAGAGCACAAACTGCAGAGGTAATTATTCCAAACAA +TACCATTGATTTCAAAGGAGAAAGAGATGACGTTAGAACGCGTGAAACAAATTTAGGAAACGCGATTGCAGATGCTATGG +AAGCGTATGGCGTTAAGAATTTCTCTAAAAAGACTGACTTTGCCGTGACAAATGGTGGAGGTATTCGTGCCTCTATCGCA +AAAGGTAAGGTGACACGCTATGATTTAATCTCAGTATTACCATTTGGAAATACGATTGCGCAAATTGATGTAAAAGGTTC +AGACGTCTGGACGGCTTTCGAACATAGTTTAGGCGCACCAACAACACAAAAGGACGGTAAGACAGTGTTAACAGCGAATG +GCGGTTTACTACATATCTCTGATTCAATCCGTGTTTACTATGATATAAATAAACCGTCTGGCAAACGAATTAATGCTATT +CAAATTTTAAATAAAGAGACAGGTAAGTTTGAAAATATTGATTTAAAACGTGTATATCACGTAACGATGAATGACTTCAC +AGCATCAGGTGGCGACGGATATAGTATGTTCGGTGGTCCTAGAGAAGAAGGTATTTCATTAGATCAAGTACTAGCAAGTT +ATTTAAAAACAGCTAACTTAGCTAAGTATGATACGACAGAACCACAACGTATGTTATTAGGTAAACCAGCAGTAAGTGAA +CAACCAGCTAAAGGACAACAAGGTAGCAAAGGTAGTAAGTCTGGTAAAGATACACAACCAATTGGTGACGACAAAGTGAT +GGATCCAGCGAAAAAACCAGCTCCAGGTAAAGTTGTATTGTTGCTAGCGCATAGAGGAACTGTTAGTAGCGGTACAGAAG +GTTCTGGTCGCACAATAGAAGGAGCTACTGTATCAAGCAAGAGTGGGAAACAATTGGCTAGAATGTCAGTGCCTAAAGGT +AGCGCGCATGAGAAACAGTTACCAAAAACTGGAACTAATCAAAGTTCAAGCCCAGAAGCGATGTTTGTATTATTAGCAGG +TATAGGTTTAATCGCGACTGTACGACGTAGAAAAGCTAGCTAAAATATATTGAAAATAATACTACTGTATTTCTTAAATA +AGAGGTACGGTAGTGTTTTTTTATGAAAAAAAGCGATAACCGTTGATAAATATGGGATATAAAAACGAGGATAAGTAATA +AGACATCAAGGTGTTTATCCACAGAAATGGGGATAGTTATCCAGAATTGTGTACAATTTAAAGAGAAATACCCACAATGC +CCACAGAGTTATCCACAAATACACAGGTTATACACTAAAAATCGGGCATAAATGTCAGGAAAATATCAAAAACTGCAAAA +AATATTGGTATAATAAGAGGGAACAGTGTGAACAAGTTAATAACTTGTGGATAACTGGAAAGTTGATAACAATTTGGAGG +ACCAAACGACATGAAAATCACCATTTTAGCTGTAGGGAAACTAAAAGAGAAATATTGGAAGCAAGCCATAGCAGAATATG +AAAAACGTTTAGGCCCATACACCAAGATAGACATCATAGAAGTTCCAGACGAAAAAGCACCAGAAAATATGAGTGACAAA +GAAATTGAGCAAGTAAAAGAAAAAGAAGGCCAACGAATACTAGCCAAAATCAAACCACAATCCACAGTCATTACATTAGA +AATACAAGGAAAGATGCTATCTTCCGAAGGATTGGCCCAAGAATTGAACCAACGCATGACCCAAGGGCAAAGCGACTTTG +TTTTCGTCATTGGCGGATCAAACGGCCTGCACAAGGACGTCTTACAACGCAGTAACTACGCACTATCATTCAGCAAAATG +ACATTCCCACATCAAATGATGCGGGTTGTGTTAATTGAACAAGTGTACAGAGCATTTAAGATTATGCGAGGAGAGGCGTA +TCATAAGTAAAACTAAAAAATTCTGTATGAGGAGATAATAATTTGGAGGGTGTTAAATGGTGGACATTAAATCCACGTTC +ATTCAATATATAAGATATATCACGATAATTGCGCATATAACTTAAGTAGTAGCTAACAGTTGAAATTAGGCCCTATCAAA +TTGGTTTATATCTAAAATGATTAATATAGAATGCTTCTTTTTGTCCTTATTAAATTATAAAAGTAACTTTGCAATAGAAA +CAGTTATTTCATAATCAACAGTCATTGACGTAGCTAAGTAATGATAAATAATCATAAATAAAATTACAGATATTGACAAA +AAATAGTAAATATACCAATGAAGTTTCAAAAGAACAATTCCAAGAAATTGAGAATGTAAATAATAAGGTCAAAGAATTTT +ATTAAGATTTGAAAGAGTATCAATCAAGAAAGATGTAGTTTTTTAATAAACTATTTGGAAAATAATTATCATAATTTAAA +AACTGACAATTTGCGAGACTCATAAAATGTAATAATGGAAATAGATGTAAAATATAATTAAGGGGTGTAATATGAAGATT +AATATTTATAAATCTATTTATAATTTTCAGGAAACAAATACAAATTTTTTAGAGAATCTAGAATCTTTAAATGATGACAA +TTATGAACTGCTTAATGATAAAGAACTTGTTAGTGATTCAAATGAATTAAAATTAATTAGTAAAGTTTATATACGTAAAA +AAGACAAAAAACTATTAGATTGGCAATTATTAATAAAGAATGTATACCTAGATACTGAAGAAGATGACAATTTATTTTCA +GAATCCGGTCATCATTTTGATGCAATATTATTTCTCAAAGAAGATACAACATTACAAAATAATGTATATATTATACCTTT +TGGACAAGCATATCATGATATAAATAATTTGATTGATTATGACTTCGGAATTGATTTTGCAGAAAGAGCAATCAAAAATG +AAGACATAGTTAATAAAAATGTTAATTTTTTTCAACAAAACAGGCTTAAAGAGATTGTTAATTATAGAAGGAATAGTGTA +GATTACGTTAGACCTTCAGAATCTTATATATCAGTCCAAGGACATCCACAGAATCCTCAAATTTTTGGAAAAACAATGAC +TTGTGGTACAAGTATTTCATTGCGTGTACCGAATAGAAAGCAGCAATTCATAGATAAAATTAGTGTGATAATCAAAGAAA +TAAACGCTATTATTAATCTTCCTCAAAAAATTAGTGAATTTCCTAGAATAGTAACTTTAAAAGACTTGAATAAAATAGAA +GTATTAGATACTTTATTGCTAAAAAAACTATCGAATTCTTCAACTACAGAAAATATATCTATAGATATATCAAGATTTTT +AGAACTAAGTAATATGATACTCTTGTTAGATGATATGCTCGACGTCAATATATATATAAATTCTTTTAAAAATAATACAT +TAGAGACATTTGATGCGACTGATCCTGAAGTTGATTACATCACTGAAATAGGAGATTACTTATTAAAATATGACGTTAAT +TCTATAAATGATGTCAGAATTGAAGTGATAGATAATTTGGGGCATTCAAATAATATGCTACTAAAAACGATACTACATGC +AGAAGTTGAAATGGAAGATGGAAAGAAATATTTATTACAAAATGGGAAATGGGGTTATTTTAATAGAGAATTTTTTGACC +TTTTGAATGATCATTTGAATGAGATAGAAATTAGGTATAACACACTAACTCCCACAGGTTTAGTATTTAAAGAAGGAGAA +GAAGGATATATAAAAGAAATAGTAGGAAGATTGCCAGAAGAATATTTAATGTTACATAAAAAATTCATAAAACCAATAAA +TAAGAATTTTATAGTAAAAGGAAATGGAATAGAGTTGGCAGACTTATACAATATTAAAAACAAAGAGCTTTTCACGATTA +AAAGAGGAATTAATACATCTTTATCTCTTTATAGTCTAGAACAGAATATAATAGCAATTAACGCCTTAAAATATCCAGAA +TCATATAATTTTGAAGAATTAATAGAAGCTATTCCTGATAACTCGGAAAATATATTTAATGATATACAACGGAGTACAAA +TTTTAGTATAGTATGGATTTTACCGATATCATCTATTGATAATATGCCCATAAAAGATATGGTTCATACTAGTAACGTAA +TAAATAAAAACTTTCAATTAACTAATTTAGGTTCAGTATTACTTAAAAATAAATTAGTAGAGTGGTCTCTCTATTTAAAA +GATCAAAGAATTAATCCAATTATTTATATGGAAACGCCAACTGAAGATAGAAATTAAACTTTTATTTTAAATCACCCTAG +TTCATACCTAAAAGACTTTCACACAAACAAAGGAGGAAACTTAAATTCCTCCTTTCCCTTATTACTCATACTATAATTCA +ATTTTAACGTCTTCGTCCATTTGGGCTTCAAATTCATCTAGTAGTGCTCGTACTTCTGCAATTGATTGTGTGTTCATCAA +TTGATGGCGAAGTTCGCTAGCGCCTCTTATGCCACGCACATAGATTTTAAAGAATCTACGCAAACTCTTGAATTGTCGTA +TTTCATCTTTCTCATATTTGTTAAACAATGATAGATGCAATCTCAACAAATCTAATAGTTCTTTGCTTGTGTGTTCGCGT +GGTTCTTTTTCAAAAGTGAATGGATTGTGGAAAATGCCTCTACCAATCATGATGCCATCAATACCATATTTTTCTGCAAG +TTCAAGTCCTGTTTTTCTATCGGGAATATCATCGTTAATTGTTAACAATGTGTTTGGTGCAATTTCGTCACGTAAATTTT +TAATAGCTTCGATTAATTCCCAATGTGCATCTACTTTACTCATGCGTTTGATAAAAACTTAAATAATATTAATTCGGTCA +TCAGTGGCGTTAAATCTTTTATCATTTTTAGTTATAGTTGATAAATTTATATTTATAAGCATATATGGATATTTCATCAA +AAATTTTTATTTATATAAATCCGAACTGCATACATATTTGTTTAAATAAGAGGTATTATTTTTCGGGAAATTGCTGTCTG +AGTTAAAAGGATTAGTTTTATAAAATGAGTTGAACTATAGCAAAAACGATTAAAATACTGATAATCCATTTTTGTATTAT +GTTAGGGACTTTTTTACTTAATTTTAACCCTATTGGAGCAAATATAATACTCCCTATTATAAGGAATAAGGCGTCATATA +AAGGGATATAACCTTGAATAAGTTTGATGACAAAAGCACCAATTGAAGATATAAAAGCAATTACTATACTATTAGCGACT +ACAGTATTCATTGGTAATTTGAATAAAACCAATAATATAGGAATAATAATGAAGGCACCACCTGCACCTACTATACCTGA +AATAATACCAATGAAAAGGCCAATGATAACTAATAAATATTTATTAAATGAAGACTTTTCGGAACTAGGTTTCACTTTAA +TAAACATTAATGTTAATGCAAGTAAAGCAATAATGATATATACCGTATTTACAAATGTAGCATCAAATAAATTTGCTAGA +AATGCACCTAACATACTCCCTATAATCATGCCGCCACCCATATAAAGAACTAATTGTGGCGAGAACTCTGTTTTTTTTCG +AGCTTTTAATGAGCCACTTAATGTACTGAAAAAGACTTGGCTAGAAGTAAGACCTGATGCGATATATGCGCTATATGCAG +GGGCTCCGAATAATGGTGGTAATAATAAAATAGCTGGATAAATAATGATAGCACCACCTACGCCTACTAGACCAGATATG +AACCCACCGAATACCCCAATGAGTAACATGATAACTATATTAACAATATCCATTACTTACTTTTCACCAATAAGTTTACA +GCTTCATTAATTAACTCTTGGGAGCTTTCTTCATCATCCGCAGCTGCTTTTACACATTCTATTAAATTCTCACTAATAAT +GATACCCATCAAGCGTTGGAGTGAACTCTTTGATGCACTTATTTGTGTAATGACATCTTTACAGTCTTTTCCTTCCTCCA +TCATTTTAATAATTCCATTTAGTTGCCCTTGTATTCTATTAATACGATTAATCATTTTTTTATCATAATTCATAGTCATA +CCTCCACTTTTAATTGAATAAAAATATATTAATAGATAAACACAAATGTGTCAAATACCCCTAGAGGTATTTGACAAGTT +CCATCCAACTGTTTAAAATACCCCTACAGGTATTTTTAGGGAGGTTATTATGAAACAATACGGAGAAAAGTTTATCGATG +AATTTAGTAAAGCAGAATTGGAAAAACTAGCCAAGCAAGGGCAATTAATTGACGTTAGAACAGAAGAGGAGTATGCATTA +GGACATATCAATGGTTCCATACTTCATCCTGTTGATGAGATTGAGTCATTCAATAAAGAAAAAAATAAAACCTATTATGT +AATCTGTAGAAGTGGTAACAGAAGTGCTAATGCTAGTAAATATTTAGCTAAACAAGGTTATAACGTTATAAATCTTGATG +GTGGTTATAAAGCTTATGAAGAAGAAAACGATAGTTATGATACACAAGAAGAATATAAAAGTATAGAAATTAAAGCAGAT +CGTAAACAATTTAACTATCGTGGTCTTCAATGTCCAGGGCCAATTGTAAAAATTAGTCAAGAAATGAAGAATATTGAAGT +AGGTGACCAAATTGAAGTCAAAGTCACAGACCCTGGATTCCCTAGTGACATTAAAAGTTGGGTGAAACAAACAAGGCATA +CTTTAGTTAAGCTTGATGAAAATAACAATGGAATTAATGCGATTATTCAAAAAGAAAAAGCAAAAGATTTAGATATAAAT +TATTCTGCTAAAGGTACTACAATTGTATTATTTAGTGGAGAATTAGACAAAGCTGTAGCAGCGTTGATTATTGCAAATGG +TGCTAGAGCTGCTGGAAAAGATGTAACTATCTTCTTTACTTTTTGGGGGCTTAATGCATTAAAAAAAGTGCAAACAGTTA +ATGTTAAAAAGCAAGGTATTGCAAAAATGTTTGATTTAATGTTGCCCAAAAAGAATATACGAATGCCTCTTTCCAAAATG +AATATGTTTGGTTTAGGAAATATGATGATGCGCTACGTAATGAAAAAGAAAAATGTTGATTCATTACCAACACTTATCAA +TCAAGCTATTGAGCAAAATATCAAATTAATCGCTTGTACGATGAGTATGGATGTCATGGGTATTCAGAAAGAAGAACTTA +GAGATGAAGTTGAGTACGGTGGTGTAGGCACTTATATTGGTGCTACTGAAAATGCGAATCATAATTTATTTATCTAATTA +AATCTATTAATAAAAGGAGTTGTTATCATGTTTTTTAAACAGTTTTACGATAATCATTTATCTCAAGCATCATATTTAGT +GGGTTGTCAACGTACAGGAGAGGCAATAATAATAGACCCTGTTCGTGATTTATCGAAATATATAGAAGTTGCAGATTCTG +AAGGTTTAACAATTACACAAGCTACAGAAACACATATTCATGCTGATTTTGCTTCAGGAATTCGTGATGTGGCTAAACGC +TTAAATGCAAATATATATGTGTCTGGCGAAGGTGAAGATGCATTAGGGTATAAAAATATGCCATCAAAAACACAATTTGT +TAAACATGGAGATATCATTCAAGTAGGCAATGTTAAATTAGAAGTTCTGCATACTCCAGGACACACGCCTGAAAGTATTA +GCTTTTTACTCACTGATTTAGGTGGTGGTTCAAGTGTTCCGATGGGATTATTTAGTGGTGACTTTATTTTTGTTGGTGAT +ATAGGTAGACCTGATTTATTAGAAAAATCTGTTCAAATAAAGGGTTCTACAGAAATTAGCGCGAAACAAATGTATGAGTC +CGTTCAAAATATTAAAAATTTACCAGACTATGTTCAAATCTGGCCGGGTCATGGTGCTGGAAGCCCTTGTGGTAAAGCAT +TAGGTGCCATACCTATATCTACAATAGGTTATGAGAAAATTAATAACTGGGCATTTAATGAAATTGATGAGACTAAATTT +ATTGAATCATTAACATCAAATCAACCAGCACCACCGCATCATTTTGCACAAATGAAACAAGTTAATCAGTTTGGTATGAA +TTTATATCAATCATATGATGTTTATCCTAGTTTAGATAATAAGAGAGTAGCATTTGATCTTCGTAGCAAAGAGGCCTTTC +ACGGTGGCCACACAAAAGGAACAATCAATATACCATACAACAAAAACTTTATTAATCAAATTGGTTGGTACTTAGATTTT +GAAAAAGATATAGATGTAATTGGAGATAAATCTACTGTTGAGAAAGCGAAACACACTTTACAATTAATTGGGTTTGATAA +GGTAGCAGGCTATCGTTTGCCAAAATCAGGCATTTCAACCCAGTCCGTTCATAGCGCTGATATGACAGGTAAAGAAGAAC +ATGTATTAGACGTACGTAATGATGAAGAGTGGAATAATGGACACTTAGATCAAGCAGTTAATATTCCGCATGGTAAATTA +TTAAATGAAAATATTCCTTTTAATAAAGAGGATAAAATATATGTACATTGTCAGTCAGGTGTTAGAAGTTCAATTGCAGT +GGGTATATTGGAAAGCAAAGGTTTTGAAAATGTGGTGAATATTAGAGAAGGCTATCAAGATTTTCCAGAATCATTAAAAT +AATTTAAGGATGTGGAAAAAATGAATAAGCATTATCAAATTGTTATTATTGGTGGCGGTACAGCAGGTGTTACCGTAGCA +TCAAGACTATTAAGAAAAAATCAAAACTTAAAAGAGAAAATAGCAATTATAGATCCAGCAGACCATCATTACTATCAACC +ATTATGGACGTTGGTTGGTGCAGGGGTATCTAGTTTGAAAAGTTCTCGTAAAGATATGGAAAGTGTTATACCTGAAGGTG +CTAACTGGATAAAACAGGCTGTTTCAAGTTTTCAACCTGAAAATAATAGCGTTATTTTAGGAGATAATACAGTCGTTTAT +TATGATTTTTTAGTAGTAGCTCCAGGATTACAGATTAATTGGTCTTCAATTAAAGGACTAAAAGAAAATATAGGTAAAAA +TGGTGTTTGCTCTAACTATTCACCTGACTATGTTAACGAAACTTGGAACCAAATTTCTAATTTTAAACAAGGAAATGCCA +TTTTTACGCATCCAAACACTCCTATAAAGTGTGGAGGTGCGCCTATGAAAATTATGTATTTAGCTGAAGATTATTTTAGG +AAACATAAAATCCGTTCTAACGCTAATGTGATATATGCAACGCCAAAAGATGCTTTATTTGACGTAGGAAAATATAATAA +AGAATTAGAAAGGATTGTTGAAGAAAGAAATATAACAGTCAATTATAATTATAACCTTGTTGAAATCGACGGTGACAAAA +AAGTGGCTACATTCGAACATATCAAAGCATACGATAGAAAAACAATAAGTTATGATATGTTACATGTAACACCACCTATG +GGTCCCTTAGATGTAGTAAAAGAAAGTACACTTTCAGATAGTGAGGGTTGGGTAGATGTTAACCCAACCACATTACAGCA +TAAAAGCTACTCTAATGTATTTGCACTTGGTGATGCTTCAAATGTACCTACTTCAAAAACAGGCGCAGCTATTCGTAAGC +AAGCACCTATCGTCGCTAATAATTTATTGCAAGTGATGAATAATCAAATGTTAACGCATCATTATGATGGTTATACTTCA +TGCCCTATTGTTACTGGATATAATAGGTTAATACTTGCAGAGTTTGATTATAATAAAAATACTAAAGAAACAATGCCGTT +TAATCAGGCCAAAGAACGTAGAAGTATGTATATATTTAAGAAAGATTTATTACCTAAAATGTATTGGTACGGCATGCTAA +AAGGATTAATATAATAAAGTACAGAAAACAATAAATTTTTAATGAAAAATCTTTTACTATAAAAGATTAAGTATTTAAAT +GACGTGTCAGTGTTGTGTTTATATGTCGTGAATTTTTAGCTCTAAATAGTATAAGATTGAAAAAGTTGTTACTGTTTTAA +ATGATCACGATGAAGTCATTCAATAAGAATGATTATGAAAATAGAAACAGCAGTAAGATATTTTCTAATTGAAAATCATC +TCACTGCTGTTTTTTAAAGGTTTATACCTCATCCTCTAAATTATTTAAAAATAATTAATGGTATTTGAGCACGTTTAGCG +ACTTTATGACTGACATTACCAATTTCCATTTCTTGCCAGATATTCAAACCACGTGTACTCAAAATGATAGCTTGGTATGT +ACCTCCAATAGTAATTTCAATAACTTTGTCTGTTGAACACTAAGAGCAATTTTAATTTCATAATGTGTTGTAAACATTTT +TTTTGATTGGAGTTTTTTTCTGAGTTAAACGATATCCTGATGTATTTTTAATTTTGCACCATTTCCAAAAGGATAAGTGA +CATAAGTAAAAAGGCATCATCGGGAGTTATCCTATCAGGAAAACCAAGATAATACCTAAGTAGAAAGTGTTCAATCCGTG +TTAAATTGGGAAATATCATCCATAAACTTTATTACTCATACTATAATTCAATTTTAACGTCTTCGTCCATTTGGGCTTCA +AATTCATCGAGTAGTGCTCGTGCTTCTGCAATTGATTGTGTGTTCATCAATTGATGTCGAAGTTCGCTAGCGCCTCTTAT +GCCACGCACATAGATTTTAAAGAATCTACGCAAGCTCTTGAATTGTCGTATTTCATCTTTTTCATATTTGTTAAACAATG +ATAAATGCAATCTCAATAGATCTAATAGTTCCTTGCTTGTGTGTTCGCGTGGTTCTTTTTCAAAAGCGAATGGATTGTGG +AAAATGCCTCTACCAATCATGACGCCATCAATGCCATATTTTTCTGCCAGTTCAAGTCCTGTTTTTCTATCGGGAATATC +ACCGTTAATTGTTAACAATGTATTTGGTGCAATTTCGTCACGTAAATTTTTAATAGCTTCGATTAATTCCCAATGTGCAT +CTACTTTACTCATTTCTTTACGTGTACGAAGATGAATAGATAAATTGGCAATGTCTTGTTCGAAGACGTGCTTCAACCAA +TCTTTCCATTCATCGATTTCATAGTAGCCAAGGCGTGTTTTAACACTTACCGGAAGCCCACCTGCTTTAGTCGCTTGAAT +AATTTCGGCAGCAACGTCAGGTCTTAAGATTAAGCCGGAACCCTTACCCTTTTTAGCAACATTTGCTACAGGACATCCCA +TATTTAAGTCTATGCCTTTAAAGCCCATTTTAGCTAATTGAATACTCGTTTCACGGAACTGTTCTGGCTTATCTCCCCAT +ATATGAGCGACCATCGGCTGTTCATCTTCACTAAAAGTTAAGCGTCCGCGCACACTATGTATGCCTTCAGGGTGGCAAAA +GCTTTCAGTATTTGTAAATTCAGTGAAAAACACATCCGGTCTAGCTGCTTCACTTACAACGTGTCGAAAGACGATATCTG +TAACGTCTTCCATTGGCGCCAAAATAAAAAATGGACGTGGTAATTCACTCCAAAAATTTTCTTTCATAATATATTTATAC +CCTCTTTATAATTAGTATCTCGATTTTTTATGCATGATGATATTACCACAAAAGCCTAACTTATACAAAAGGAATTTCAA +TAGATGCAACCATTTGAAAGGGAAGTCTAAGAGTAGTCTAAAATAAATGTTGTGGTAAGTTGATCAATACAAAGATCAAG +GATTATAGTATTAAATTGTTCATTATTAATGATACACTACTTATGAATATGATTCAGAATTTTCTTTGGCTACTTTTACA +GTAAAGCGATTTTTTAGTTATCTTATAACAAAGACAAATTTATAAAGGTGATATTATGGAAGGTTTAAAGCATTCTTTAA +AAAGTTTAGGTTGGTGGGATTTATTTTTTGCGATACCTATTTTTCTGCTATTCGCATACCTTCCAAACTATAATTTTATA +ACTATATTTCTTAACATTGTTATCATTATTTTCTTTTCCATAGGTTTGATTTTAACTACGCATATAATTATAGATAAAAT +TAAGAGCAACACGAAATGAATCATTAATACGAATGTGATTAAACATAAAACTGAAGGAGCGATTACAATGGCGACTAAGA +AAGATGTACATGATTTATTTTTAAATCATGTGAATTCAAACGCGGTTAAGACAAGAAAGATGATGGGAGAATATATTATT +TATTATGATGGCGTGGTTATAGGTGGTTTGTATGATAATAGATTATTGGTCAAGGCGACTAAAAGTGCCCAGCAGAAATT +GCAAGATAATACATTAGTTTCGCCATATCCAGGTTCTAAAGAAATGATATTAATTCTAGACTTTACCGAAGCAACAAATC +TCACTGATTTATTTAAGACCATAAAAAATGATTTGAAAAAGTGAAGTAGTGAAGTGTGGGTGCAGAGAGAACTAAGCCCA +TCGATAAATGGTCGCTTGTTAAAGAAGAGTGACGGTCACTCTTCTTTATGTGCATATTTTATTTTGTCTGTTTTGTTAAC +AAGCAGCAGTGTAACAAATATGAGTAAGGATAAAATGAGTATAATATAGAAACCGAATTTATCATTAATTTCATTAATCC +ATCTTCCTAAAAATGGAGCAATTAAACTTTGCAGTAACAATGAAATTGACGTCCATATCGTAAATGAGCGACCGACATAT +TTATCTGAAACAGTGTTCATTATAGCTGTATTCATATAAATTCTGATTGATGAAATTGAGTAGCCTAGTATAAATGATCC +TATGAATAAGTAAAATGCTGAGTTTATCCAAATAAATAGTGCTGAATTTATGACTAATATGAAATATAACAAAAATATCA +ATACTTTAGTTGAGATTTTCTTCGAAAGAATAGCTGAAATTAAACCTGCACATAATCCTCCAATGCCATATAACATATCT +GAAAAACCAAATTGTACAGACGAAAGTTTTAAAACATTATAAACATATCCTGGTAATGATATGTTAAAGATCATTGTAAA +CACCATTGGTATGATTGAAATAACTCCAAAAATAAATATCATCATGTTGTCTTTTAAAAATTTCCATCCTAATAAATATT +CTTGCAATAAGCTATTTGTTGATTCTTCCTCTGAATGAGTTGGTTTATCTACATGCAATCTAAATAACATAAAAATGCTG +ATTAGAAACATCATTATAGTCATCGCTATAATTAGAGTGAATCCATTTATTTTATATAATATTCCTGATAATCCACCTGC +AATAAACATACCTGTTTGCAAACTAATCTCTAATAGAGAATTTGCATCTGTATATTGATCTGGTTTTAATATCTGTTTAA +CTAAACTTCTAGATGTTGCCATATACGTAGTCCAACCTATCCCATTAACAATCGCAAATCCAATCACTAAATAGGTCTCA +AATCCTATCATTACGAGTGCTATGACAATGAGTAAATATAATATTACCTGAAGAAGATAGGTGATTAAAATAATATTTCG +TCTATTATATTTATCGGCTAGTCCACCTATTATAGGAGAAGCTAAGAAACCGGATAATACATTTAAAGCTAACATAATAC +CTAATAGTTGGGAGTCGTTAGTTTTGTCGATTAAATACCAATTAGCCCCAACTGTACTAATTCCAACTCCGAATGCTGAA +ATTATTTCTGCTAAAAAGAACAATCTGAAGTTTTTAATTTTCAACAAATCAGTCATAAATATCACCTTTTTTCAATGCAA +GATTTATATTTGCGAGCTCTTCTATAAATTGAGAGTCGTTTAAAATATCGAATGGTTTATTGCCTTCGCCGATCAAAAAT +CTATAGTTAGTAATATTCATAAACTCAAATATTAATTTGAATTGGTTGACTAAAGGTTGTGCTTTGGTATGCGGATTGTC +GCCACCTATGATTAAAATCAAATACTTTTTTTGTGACATGATTTCTTTGAAGTTGTCTATTTGCGTATCTCGCAATGACT +CGGTCCATCTATCAATGAAGAGTTTTAAAGACGCACTCATGGAGTACCAATAGAGTGGTGTTGAAAAAATAATAATATCT +GATTCTAAAACTTTGATTAAAATTTGTTCGTAATCATCATTATGAAAGTTGGCACCTTTGCTATGACGATTATCTGTAAC +CTTTTCAATGTTGCTTTGATATAAATTCACAAAGTTGACTTTTAAATTCTTCAATAGATTCTCTACTGCGATAGCTGAAT +TGCCATCTTTTCTACTACTTCCAAATAAACAAGTAATCATAGTCATAACTCCTTTCGATTTACCTTAATAATAATATTTA +ATATATTATTATTAAATCAGAATTCTTAGAATTCAGGATTCGATAAAAGGGAACCCTAAAGGAGGTACTACTTTGAACTT +AAACATTTTAAAATCATTTTTAGTAACTAGTGAAACAAAGAACCTTACTAAAGCATCAGAGTTACTTAACTATTCACAGT +CAACTGTATCTACACATATTGAAAAATTAGAAAGGCAATTAGATGTTAAATTATTTTATAGAAAAAAATATGGTATGGAA +CTAACGGAAGAAGGCTTAGCATACGTTAAATATGCTAAAGTGATTTTAGATAGTAATAGCGAATACGAGAGAGAAATAAA +AGGACTTTACAATAAGAAGGTAAATATAAGTATTAACATGCAAGAAAGTCAGTATTTGTATCGCTACTATAATAAGATTA +GTGAATGGTTAGCTGAACACCCATATGTAAACTTGAAGTTTAAATCCGCACATTCTAATTTCTATATTAAAGAAGAAATT +GCTAATTTCAAATCGGATATTAGCCTTATCACAGACGAAAAGATTATTAATAGTAACTTAACTGCTATTCCTATAACTGA +AGAACGTTTAATATTTGTTACTAACAAAGAGATGAAAGATTTCAAATTAAACGATATTCAAAACCATACATTACTTGTTA +CTGAAAAAGGGTGTAGTTACAGAGAACAGTTAGAAATGATTTTATATAAATATAGCTTTTCAACAAAACAAATGATTGAA +TTTCTAGGAATTGAGTCATTAAAAAAACACTTGAAAAATTCAGGTGGGATTGCGCTATTGCCGGAATTTATTGTTGCAGA +TGAATTAGAAAATAAAAGTTTGTTTCAAATAGACGTAGATGTCGATATTCCGAATTTAGAAACAACATTGATAATTAATC +CTGAATCGAATAAGCAAGTACTTGAATCTTTTGTAAAAGATGTTTTTTTATAATTATTGGTGAAAACGTGTAGTTATGGT +GAAACTCAAAGATAATAATTTAAATGAGATGTTAATGAAAAAGTAATTCAATATAAAAACAGGTGATTTATATCTTTAAT +AAGGATTATTTCTAGTTGAAATTTCAAATTGCGGGCATTCATTTTCAAAATAGTAAAGAATTTAATAGATGAAAACCTTT +GAATCATACTAGTTAGTATGGATTGATTAATATTTTAACCTGCTAAATGAGTAGCTTATGTATTTTAAATAGCTGTCATC +TAGCAGGTTCTTTATTATTAATAAGTTCAGATAAATTATTTTAGTCTGTTTTTCTAATGTTGATAATAATAACTTTAAAT +CTGTACTTTCATTGTGAGATATATAGTACATCATAAATTTTTATATTTGGATTGAGACTCTTTGGAAGATTTTCAATAAA +TGAATATTTCACAAACTTCCACTTATCAGGATTATAAATTATAATTTCTGCAACTGCATTATCGAAGATTCTAAACAATT +TTTTGATTTCTATTTCTTTTAGAGATAAAGTAGGGAAAATGTCGATATATTCTTCAAGCGCATAAAGACTATGTTTAACA +GAATCGGTTATGTTCATTGAATATATCACACCAATATTTATATTTTGCCATCCAAAAGAAGAAATAACATTATCATAAAA +TCCATTGAATATAAATTCAGTCATCCCCTCTGAACTTCTCCATAAATATAATGGTGAATAAGTATTTGTAATGCAATTAG +TAGTTGTTTGACTTATTAAATAAGCTTTTATAATTAAATTTTTAAATCCATCAGTCTTATAACCATTTAATCGAACTCTA +TCTCTAATTATTTCCATATTGTAATCACTAGGCAACTTAACATGATATTGCATTGCATGCATTTAGCATACCCCCTTTTA +TAAAAAGGATAGCAATAATAAGTAAAATCTCATATTATCCAATTGTGATATAGTTATCATAAAAAGTGATAGGTGATTAA +ATTGAACTTTAATGATTTGGAAATTTTTATAACTGTATGTGAAGAAGCATCTATCAATAAAGCTGCAATTAAACTTAGAT +ATGCACAATCTAATATATCTCAAAGAATTAGCAAGCTTGAAAATGAATTAGGTGTAGTTTTGCTTTTTAGAAATCAAAAA +GGTGCTAAGGCAACTAAAGCAGGCGAAGAATTCTTAGCGTATAGCAAAAAAGTATTAAGAGATACAGAGACTATAAAAAA +TAAAATGAAAAATAATACTATGTCTATTTTATGCTCAGAACTGTTATTTAATTATTTATCTGAGAGCGAAGAAATTATGA +TGTCGAATAACTCAATTAATTTTATTTCTAGTGGAAATATTAGAAAAGCTATAGAAAAAAATAATTATGATAAGGTTATT +TCATTCATAAAAATTAACGACTCAAATTATAGACTTAGTAATGTTGATACTATGAAAGTAACGCTTTACAGTAATGGAAG +TAATTATGATAAAGAGGCTTTACTAATAAATAAAGATGAGTTTTGTCCTTTAAGGAAAATAACTTTAGATAATAAGCTTG +ATTCACAACGGGTAATGGAAATTGATTCATTGGCAGCAATAATAAACTTAGTTAAACAAGGAAAAGGAAAGGCTTTATTA +CCTATGACTTTTGAGAATAAAAGAGATATAGTACAGGACATTTCTAAGATATTTGAAGTTAACTACTATACTTATAATCA +TATTATGCATCATTAATTGAAAGTTGATTGAGTTTAAATTACAGGAACGATTTTAAAAGTCTAGTAGTTAGAATGAGCGA +TGTGACAGAAAAGAAATTATGCCTTTTATTTTGTAAATTCGTTGTTTTAAAGTAAAGGTCCTTGTAGTATAGGAAAAAGT +GGATGACTTCTCCGCCTTTACCACCCTTTCAAGCATAATGATAACTGCTAAATATTGATTATAGCACCATTGCAAAACTA +ATCATTAATTGTCATATTCAACTATTTTGAAAAGTATCAATTTCACATTATCAAATTATTGAGTATCTCAATTTATTGTT +ATACACAAACAGTATTTTTGTTATCATAGAGTTAATAAATATAATAAATTAAACAAGTAACTTTACAAAAATTAGAATGA +TTCAATTCAATCATAATTGTTTGAAAATAGAGGTGAGCAGGTGAACGATATGTTAATTAGTCTTGTAATCCCAGTTTTTG +CTTTGTTAGTTATTGGTGGTATTATTTGGATGATTATAGAAGGTATAGTACATATTTCAAAAAAGAATAAAGCAATTGAT +AACTTTTTTAACCAAGTTAATAAAGTAAGTGAGACATATAAATTCGCTACTACTTTTTTATTTCTAATCTTGGCTACGGC +TGGTATTTCTCAATTTTATCTATATTATATAGTATCAGCGTTTCTTTTTTGGCTTGTCATACTTACCTTTGGCATTGCAG +GTATCATTTTTTTAATGCCATATGGATTATGTTTTCTACCGTTTTATAAGCAAAAAAAGAAAAAACAGACATTTAAAAAA +TACATGGTTTACACTACGATTGGTTTGTCAATTTGTCTAGGCTTATCTCTAGTTTTGGTTCACACTACGAAAATTTATAT +GGACGAAGGTGGCGTAAGATACTATTACGGTAGTTTTGTAATGAAACAAGCGGGCGGTTATGCTTATTTAGCTTTAGCGG +TACTTTCAACGTTGTTAATTGTTGCGAAAAAAGCTACAAATAAAAATAAAGAAATCGAAACCGTCGACAATACAAATATA +ACGGAAAGATAATTAAGGGAGTGCTCATTCAGGAGTGCTCTTTTTTGATGTCCAAATTTAGTTGCAAATGAAGGCATAGA +AGAAATTTTTATAGTCATTTATTGGATAATGAATTATGGTCATATCGATTAATTTTATTAGTGAGGATTTTACAAACATT +TGTTGAATAAATATGGTAATGATAATATATGAATATTTTTGAAAAAACACTAATATGATTAAATTATTTGTTAAAATAGC +ATTAAGTAAAAAACAAATAGGGAACAAGGTGGGATTTCATGAGTCAATTGCTAAATGATACGTTATCGGCTTGGTTGTTA +ATTGAATCTTTAAGTCCAGGAGAAGTAAATTTTACAGCGGAAGATATACTCTCAGCTGAACATTTTAAAAATGGTGCAAA +GCAAGCACAACTTCAAAGTTTTGATGAATATTTTGAAATATGGAATGCTGAACGCTTTATTATATCAGAAGAAAAATCAG +AGACTGGGGAACTTATATTTAAATTTTATAGACATTGCTTCCGCTATAATGAAATTAATTTGAAAATTCAGGATATTTTT +GATGATTATTCTGAGATTCATAATCCAAATGGGACACACTGTTATGGTTACACATTTAACACAGATAAACACGGCAAAGT +GATAGTTGATTCTATACATATTCCGATGATTATGAGTGCATTAAAAGAAATTGAAAAGAACAAAAATGCCAATATAGAAG +AAAAATTTAATGATTCTGTTGAAAAATTTGTTCAAAAAGTAAAAGAAATTTTAGCAGATGAACCAATTAATGAATTTAAA +TTGAAGAAGATGGACAAAGCTTATGATGAGTACTTTTCTGTATTAAATTCAAAGAAAGATGGATTATTTGGACATTATGT +AGCAATAGAATATGTGAAAGATAGTGATTTACCACAGCCGGAATTTAACAGCTTCTTCATAAGTGATATTGAGAAAGCAA +GAAAATCTCCCAACCAAACTTTAATTGATTACATTGAAGGTGTAGAAGAAAGTCAGCGCATAGAAGTAGATGAAAATAAA +GAAATGTTTGACAAATTTTTACATCCTTCACGTTTGCCTGATGGACGATGGCCATCACAGACTGAGTTTAGATTGTCTTT +AATGCAACAACTTGCTGTAAACCAAATTACGAGTGGTAATGAAAGAATAAGTTCAGTTAATGGGCCACCAGGTACAGGTA +AGACTACTTTATTAAAAGATATATTTGCTCATCTAGTAGTTGAAAGAGGTAAAGAGTTAGCTAAACTAAATAATCCTAAA +GATGCATTTGTCAAAACAAAAATTCATGAAACGGATGATAAATACGTATACTTACTAAAGGAATCTATTGCCAAATATAA +GATGGTAGTCGCATCTAGTAATAATGGAGCTGTTGAAAATATATCTAAAGATTTACCGAAAATTGAAGAAATTATAAGAA +ATCCCGAAAAATGTAAATTCCCTAAATATGAACAGAATTATGCAAATTTAGCACATGAATTAAAAGATTTTGCTGAAATA +GCTGAAGATTTGATTGGTGAAAGTGCCTGGGGCTTATTTTCTGGAGTTTTTGGTAAAAGTACTAATATTAACCAAGTATT +GAGTCATATGTTAAAACAAGATGCGAATGATATTGGCTTTGCTAAATTACTACAAAATGAGAATAATCGTATGAGTTATA +ACGAGTTAATGAGTGAATGGCAATCACATCAACGTGCATTTTTAGAAGAGTTGAGGCATGTTGAAATGTTAAAAGAAGAA +TCTATTAGAGCATATGATGTTTATAAAAATTGTGAGTCTTTCTCTAAGATTGAACAGGTTATTAATAGTGAAAAAACAAG +TATTGAAGAACAGGTATATCATTTAGATAATGAAACGTTACGAGACAATAAAGAAATAGAAGATTTGGATAATCGAATTA +ATTATATTGTTAAGCAAATAGAAACTTTAAATGAGTTAATTAAATCCATCAAAGAAAGCAACAAAGGTTTTATTAACAAA +CTGAAAGCGATGTTTAATTCAGAAGAAGATGAAAGCTATAAAGATCATAATAAAGAGAAGCAACAATTATTAACACAACA +GTTAGAGTTAGAGAAATGTAAAAAAAACAAACATGAAGACCTTGTTAGCAAACTAAAAGAAAAAGAGAAATTAATTAAAC +AATTAACTAAAGTACAGTTGCAATTAGACGAGTTAAATTCACAGTTACAAGAGTTAGAAGCATATCGTATTGAGTCAAAA +ATTACAATTCCAGAAAAAGATTTTTGGAGTGACAACAATTATGATGAGCGCCAAGTTACTAATCTGTGGACGAGTGACGA +ACTTCAATACAGACGTGCCATGCTCTTTTTAAGAGCAATGATATTGCATAAATTATTATTGATTGCTAATAATACAACTA +TTTATTATGCGATTAATGATTTTAAAGATAGAAGGAAATTAATTGATGCAAATCCAGATAAAGTACACAACGCATGGAAT +GTGATGCATTTAATATTTCCAGTAGTTAGTACGACGTTTGCAAGCTTTAAATCTATGTATGGGGGCATACCAAAAGATTT +CATAGACTACTTATTTATTGATGAAGCAGGACAAGCAATACCTCAAGCAGCTGTGGGAGCATTATATCGTTCAAAAAAAG +TTGTAGCTGTAGGTGATCCGATTCAAATAGAACCGGTTGTGACTTTAGAAAGTCATTTAATTGATAACATTCGTAAAAAT +TATCATGTTCCGGAATATCTAGTTTCTAAAGAAGCTTCTGTGCAGTCTGTTGCAGACAACGCCAATCAATATGGTTTTTG +GAAATCTGATGCTACTGATAGTAATCAAAAAACCTGGATAGGCATACCTTTATGGGTGCACAGACGATGTTTAAAACCTA +TGTTCACGATAGCTAACCAAATCGCTTATAATAATAAAATGGTGTTGCCAAGTAATATTACAAAAGTAGGTAAAACAGGT +TGGTATGACGTTAAAGGAAACGCAGTTCAAAAACAATTTGTGAAAGAGCATGGTGAAAAAGTAGTGGGATTATTAGCTGA +TGATTGGATTGAAGCAATTAAGGAAGGTAAAAATGAACCGAGCTCATTTGTAATATCGCCTTTTTCAGCAGTACAGCAAC +AGATTAAACGTATGTTAAAGCAACAACTACCGACTAGAATTGATATTGAACGTACAAAAATTAATCAATGGGTCGATAAA +TCCATTGGTACTGTTCATACTTTTCAAGGTAAAGAGGCTCAGAAGGTGTATTTTGTAATAGGTACTGATAATACCCAAGA +TGGTGCTGTGAACTGGTCATGCGAAAAACCAAACTTGTTAAACGTTGCAGTGACAAGAGCTAAGAAAGAGTTTTATGTAA +TTGGCGACATGCAAAGAATACAGATGAAACCATTTTATGAGACGATTTTTAAAGAAAGAAATGTAAAATAACATACAAAA +AAGTATATAGAGGAAGTTATACATTTTAAAAGGAGCAAAATTGAATAATGGAGAAATTTAACAACTGGATATTAAATGCA +ATAAGTGGATCTCAAACAGACAAGAATGAAACAACTGAAGAATTAAAAGGGGCAAAATTTATCATTTTATATGCATATTC +AATGCTCGTTTTGCTTGCGTTAGTAATTTCTAACATATTCATTCACATTTTGGAGCCTAAACTATCAATCACCACTCAAA +TCATCATCGTTTTGATTTTAATTGAAGCACTAATTGGACTGCGTTTCTTGAAAGCGTACGATGTTAAGCGTGGCAAAGAT +AAAGAAAATAAGAAAAATAGTAAGGATTTCGTTAAACTAAAATCAATTTTAGTAGCAATTTTATTTACATCATTGGCGCT +GACAGCAGGTACTGTAGCTGATATATACGGTTTCACTGACTTAGGAAATACTAGAAGTGATTTAATCGTTTGGAGCATAG +GTGGTATTATATTTGGCCTCGTATGTTACACAATGGAAGATAAAAGATAACGATAAGGAGCTGGCGATTATAAAGCTAGC +TCCTTTTTTAACTTATATATGTAAAGAACTATCCTAAGGGTTTTTAATCATATGTCAATAATTTCTATAATACATTATTA +AAACATCAATTAAATAAGTTTTAAAATTTTACACATATTTTTATTTAAAAAAGATGTATAATTAATGTATTAAATATAGA +AAGAAGTTGATATTATGAAAAAGTGTATTAAGACTTTGTTTTTAAGTATCATTTTAGTAGTGATGAGTGGTTGGTATCAT +TCAGCACATGCGTCAGATTCGTTGAGTAAAAGTCCAGAAAATTGGATGAGTAAACTTGATGATGGAAAACATTTAACTGA +GATTAATATACCGGGTTCACATGATAGTGGCTCATTCACTTTAAAGGATCCAGTAAAATCAGTTTGGGCAAAGACTCAAG +ATAAAGATTACCTTACCCAAATGAAGTCGGGAGTCAGGTTTTTTGATATTAGAGGTAGAGCAAGTGCTGATAATATGATT +TCAGTTCATCACGGCATGGTTTATTTGCATCATGAATTAGGAAAATTTCTCGATGATGCTAAATATTACTTGAGTGCTTA +TCCAAACGAAACAATTGTGATGTCTATGAAAAAGGACTACGATAGCGATTCTAAAGTTACGAAGACATTTGAAGAAATTT +TTAGAGAATATTATTATAATAACCCGCAATATCAGAATCTTTTTTACACAGGAAGTAATGCGAATCCTACTTTAAAAGAA +ACGAAAGGTAAAATTGTCCTATTCAATAGAATGGGGGGTACGTACATAAAAAGTGGTTATGGTGCTGACACGTCAGGTAT +TCAATGGGCAGACAATGCGACATTTGAAACGAAAATTAATAATGGTAGCTTAAATTTAAAAGTACAAGATGAGTATAAAG +ATTACTATGATAAAAAAGTTGAAGCTGTTAAAAATTTATTGGCTAAAGCTAAAACGGATAGTAACAAAGACAATGTATAT +GTGAATTTCTTGAGTGTAGCGTCTGGAGGCAGCGCATTTAATAGTACTTATAACTATGCATCACATATAAATCCTGAAAT +TGCAAAAACGATTAAAGCAAATGGGAAAGCTAGAACGGGTTGGCTGATTGTTGACTATGCAGGATATACGTGGCCTGGAT +ATGATGATATCGTAAGTGAAATTATAGATAGTAATAAATAAGGATTCAATAATGATATTAAGACGAGTATGAAAATAGTT +AGATTCTAATTATTTTCACTACTCGTTTTTATTTTGAAAATAAGTAATAATTCAACAATATTATAAATTGAACAGATTGT +TTGTGAAATTTTTGATAATATTAAAGTGAAAAAGTGTTATAAATTGATAAATATATGTAATTAACAAAAACAAATCATTT +TAAAAAGAAGAGAGTTGTAAGATGATGAAACGATTAAACAAATTAGTGTTAGGCATTATTTTTCTGTTTTTAGTCATTAG +TATCACTGCTGGTTGTGGCATAGGTAAAGAAGCGGAAGTTAAGAAAAGCTTTGAAAAAACATTGAGTATGTACCCTATTA +AAAATCTAGAGGATTTATACGATAAGGAAGGCTATCGTGATGATCAGTTTGATAAAAATGATAAAGGTACATGGATTATA +AATTCTGAAATGGTTATTCAACCTAATAATGAAGATATGGTAGCTAAAGGCATGGTTCTATATATGAATAGAAATACCAA +AACAACAAATGGTTACTACTATGTCGATGTGACTAAGGACGAGGATGAAGGAAAACCGCACGACAATGAAAAAAGATATC +CGGTTAAAATGGTCGATAATAAAATCATTCCAACAAAAGAAATTAAAGATGAAAAAATAAAAAAAGAAATCGAAAACTTT +AAGTTCTTTGTTCAATATGGCGACTTTAAAAATTTGAAAAATTATAAAGACGGAGATATTTCATATAATCCAGAGGTGCC +GAGTTATTCGGCTAAATATCAATTAACTAATGATGATTATAATGTAAAACAATTACGCAAAAGATATGATATACCGACGA +GTAAAGCTCCAAAGTTATTGTTAAAAGGTTCAGGGAATTTAAAAGGCTCATCAGTTGGATATAAAGATATTGAATTTACG +TTTGTAGAGAAAAAAGAAGAAAATATATACTTTAGTGATAGCTTAGATTATAAAAAAAGCGGAGATGTATAATCATGGCT +CAATCAGAATATGAAATCAATCCCGGAAAAAGAGAGTGATGAAATGATAAAACGTGTAAATAAATTAGTGCTTGGTATTA +GTCTTCTGTTTTTAGTCATTAGTATCACTGCTGGTTGTGGCATGGGTAAAGAAGCGGAAATAAAGAAAAGTTTTGAAAAA +ACATTGAGTATGTATCCGATTAAAAATCTAGAGGATTTATACGATAAAGAAGGATATCGTGATGATCAATTTGATAAAAA +TGATAAGGGAACATGGATTGTAAATTCTCAAATGGCAATTCAAAATAAAGGAGAAGCTCTAAAAATAAAAGGCATGCTTT +TGAAGATAGATAGAAATACAAGAAGTGCAAAAGGATTTTACTATACTAATGAAATAAAGACGGAGAAATACGAAGTAGCT +CAGGATAATCAAAAAAAATATCCAGTTAAAATGATTAATAATAAATTCATTTCTACTGAGGAAGTTAAAGAAGAAAACAT +AAAAAAAGAAATCGAAAACTTTAAGTTTTTTGCGCAATATAGCAATTTTAAAGATTTAATGAATTATAAAGATGGAGATA +TATCATATAATCCAGAGGTGCCGAGTTATTCAGCTCAATATCAATTAACTAATGATGATTATAATGTAAAACAATTACGT +AAAAGATATGACATACCAACAAATAAAGCGCCGAAGCTGTTGTTGAAAGGTACAGGGAATTTAAAAGGTTCATCAGTTGG +ATATAAAAAAATTGAATTTACTTTTTTAGAGAATAAAAATGAAAATATTTACTTTACTGATAGTCTACATCTTGAACCGA +GCGAGGATAAATAATCATCTTTTAACCAGAATATGAAATCAATCTAGGACAACCTAATAAAAGGAGAGAGTTGTAAGATG +ATGAAACGATTAAACAAATTAGTGTTAGGCATTATTTTTCTGTTTTTAGTCATTAGTATCACTGCTGGTTGTGGCATAGG +TAAAGAAGCGAAAATAAAGAAAAGTTTTGAGAAGACATTGAGTATGTATCCGATTAAAAATCTAGAGGATTTATACGATA +AAGAAGGTTATCGTGATGACGAATTTGATAAAAATGATAAAGGTACATGGATTATTGGTTCTGAAATGGCAACTCAAAAT +AAGGGGGAAGCTCTGAAAGTTAAAGGTATGGTCTTGTATATGAATAGAAATACCAAAACAACAAAAGGATATTATTATGT +AAATGCAATAAAGAATGATAAAGACGGAAGACCCCAAGAGAATGAAAAAAGGTACCCAGTTAAAATGGTCGATAATAAAA +TCATTCCAACAAAAGAAATTAAAGATAAAAACATAAAAAAAGAAATCGAAAACTTTAAGTTCTTTGTTCAATATGGAAAC +TTTAAAGATTTGTCGAAGTATAAAGACGGAGATATTTCATATAATCCAGAGGTGCCGAGTTATTCGGCTAAATATCAATT +AACTAATGATGATTACAATGTAAAGCAATTACGTAAAAGATATGACATACCAACAAATAAAGCACCGAAGTTGTTGTTGA +AGGGTACAGGGAATTTAAAAGGTTCATCAGTTGGATATAAAGACATTGAATTTACGTTTGTAGAGAAAAAAGAAGAGAAC +ATTTACTTTAGTGATGGGTTAATTTTTAAACCAAGTGAGGATAAATAATCATGACTCGATCAAAATATGAAATCAATCCA +GGAAAGATAATAAGAAGAATAGGATTGTGAAATTATGAAACGATTAAACAAATTGGTATTGTATATTAGTTTTTTGATAT +TAGTCATTAGTTTCACTGCTGGTTGTGGCATAGGTAAAGAAGCGGAAGTTAAGAAAAGCTTTGAAAAAACATTGAGTATG +TATCCGATTAAAAATCTAGAGGATTTATACGATAAAGAAGGTTATCGTGATGACGAATTTGATAAAAATGATAAAGGTAC +ATGGATTATTGGTTCTGAAATGGTTGTTCAACCTAAAGGGGAACGTATGAAATCAAAAGGTATGGTTCTATATATGAATA +GAAACACTAAGACTACAACCGGGAAGTATATAGTGAGTGAAACACTTCATGATGAAGATGGTAGACCTAAGAGTAAAGAT +AAAGAATATCCCGTTAAAATGGTAGACAATAAAATCATTCCAACTAAAGGTATCAAAGACGAAAATATAAAAAAAGAAAT +CGAGAATTTTAAGTTCTTCGCGCAATATGGCAGCTTTAAAGATTTGTCGAAGTACAAAGACGGCGATATATCATATAATC +CAGAGGTACCGAGTTATTCAGCAAAATATCAATTAACTAATGATGACTATAATGTAAAACAATTACGAAAAAGATATAAA +ATACCAACGAATAAAGCACCAAAGTTATTGTTGAAAGGTTCGGGAGACTTAAAAGGTTCATCAGTTGGATATAAAGACAT +TGAATTTACTTTTGTGGAGAAAAAAGGTGAAAATACTTTTTTTACTGATAGTCTACATCTTGAACCGAGTGAGGATAAAT +AATCATCCACACACACGATTCAATATGAAATTCAAATATGTTGCTGTAAAATAATGTAAAATAAACGTATTCATATTACT +GAATTGAGGGATAGTATGCAACGTGATTATTTAATTCGAGTAGAAACTGAGAGTATGTCAGATTTCAAAAGGCTCAATAG +TTTAATGATTGGTTTTGTTATTAAAGGTGAGGCACACATTTATGATGAAAATAATATGACGCAATGCAACAGTGGCGACA +TTTTCATCATTAACCACCGCGACTTGTATCGATTTCAACTTCAACAAGATGGCATCATATGTTATATCCAATTCCAAATG +AAATATTTAGCAGACAAGTTTGATGATGTGCATTGTCTATATTTTCACTTAACAGATGCGACCACAACAAAGAATATACA +TCAACTGAGAAATATAATGGCAAGACTGGTTTCAACACATATCCGACATAATGAGTTGTCTAAATTGACTGAGCAACAAC +TTGTGATTCAGTTGCTTATGCATATGATTCATTATGTCCCGCGTACATATCATTCGAACCAAAGTATCTTAAATGATGAT +AAAGTGAATCAAGTATGCGACTATATCGAGTTACATTTTCATGAAGATTTAAGCCTTTCAGAATTAAGCGAATACGTTGG +GTGGTCAGAGAGCCATCTGTCTAAAAAGTTTACAGAATCGCTAGGTGTAGGATTCCATCATTTCTTAAATACGACGCGAA +TTGAGCATGCGAAACTCGATTTGACATACACAGATGAAACCATTACTGATATTGCATTGCAAAATGGCTTTTCAAGTGCA +GCGAGCTTTGCGAGAACATTTAAACACTTTACGCATCAAACACCTAAACAATATCGAGGTGATCGTCCAGCAATCACTGA +AAACCAGCAATCGGCACAACATGATTATCACGACCGTGAATTGATATTACTTTTAAATGACTACATTGAAGAAATGAATC +ATTTTATTGAAGATATTGAAAAGATGAACTATAAAGAGATTGCCTTTCAACCAACCAATCACCAACTAAATCAATTTAAT +CATATTATTCAAGTGGGCTATTTGAGGAATTTGCTCAATACACAGTATCAATCACAGTTGCTTACATGTCATCATGATTT +TCAAGTCAATGAAGTATTAGCATATGATGTGATTCCATATATCATGAAAAAGCTCAATGCGCCATTCACGTATGATGCAG +AAATTTCGAATATATTTTATGATATTGATTTGTGTTTAGACTTTTTATTAGATCATAACTTTAGTTTGACCATGCATTTG +GATCAGTATGACTCACGTGATTATATCGATGCATTTAAAATTTTTATCCATCACGTTGCCCTGCATGTCAGTCATAGAAA +AGATTTGAAGTTCAACTTGTATGTGACGACATTGCACACGTCTTTGATTGAAATGATTGATTATTTTAAAGCATTATTCC +CTAATGGTGGCTTGTACATTCACTTAGATCAAGCTACGGAAAGACATCTACCATTATTGAAACGACTTGAGCCACACATC +GACCATTTTGTATTTGATGCCAATTCAAATGACGCTGTTGATTTTAATAAAATGAATGATGATGAATTTAAAACCGCGAG +TCAAATGATTATTAATAAAACGAATTACCTTATCGACTTAATGCATCGTCATCGCCTAAAGCGTCCACTCATTTTACTCA +ATTGGAATACATTGACGGGTGATACATTTATTACAAACGGCGAATATTTTAGAGGTGGTATCATCATTGAGCAATTATTA +AAATTAAGTTCTAAAGTAGAGGGTATCGGGTATTGGTTGAATTATGATTTGCACGTCAGTCATTGTAAAAATGAACGGGA +TTATATGAATTCTATTGAACTATTTCATCAATATAATGGAAAACGTCCGGTCTATTTCACGGCATTGCTATTTAATAAAT +TAACAAGCAATATTTTGTATTCTGATGATACATGTATTGTCACGGGGACTGATTCAAATTTTCAAATATTGTTATATGAT +GCAAAGCATTTTAATCCGTACTTAGCGTTGGACAATCAAATGAATATGCGTGCGACGGAAATGATTCATTTGAACATTAA +TGCCCTGGAAGAAGGTATGTATAAGATTAAACATTTTACCTTAGATAAAGAAAATGGTGCGTTATTTAATCTTTGGCGCA +AACATCATACGATACATGGCATGGACAAGGACTCTATAGATTACGTTAATCGAATGAGTTTTCCGAAATTAGAAGTATAT +GATATAGATATCACGGACACACTGGCATTAAACATTAAAATGATTACGAATGGGATTCACTTAATTGAAGTAAAACGTTA +CCCAAGTTCATAAAATGATCACAAATCACAAATTTTGATATACATAATTTGTGATTTTTTATATTCAAAGCTAAAATTGC +AAAAAATTAATGGTTAACATCTCTGTTGTTGGCAATATAAATAATGATTAATCATTTATGATGTAACTAAGGAGATGAAG +GATATGAATCAACAATTAATTGAAACTTTAAAATCTAAAGAAGGCAAAATGATTGAGATCAGACGTTATTTACATCAGCA +TCCAGAATTATCTTTTCATGAAGATGAAACGGCGAAATACATCGCTGAATTTTACAAAGGTAAAGATGTGGAAGTAGAAA +CGAATGTCGGACCACGTGGAATTAAAGTAACGATTGATTCAGGGAAACCTGGTAAAACATTAGCAATCCGTGCAGACTTT +GACGCATTACCCATTACTGAAGATACAGGATTATCTTTTGCATCACAAAATAAAGGTGTTATGCACGCATGTGGTCACGA +TGCACATACAGCATACATGCTTGTATTAGCAGAGACGCTTGCTGAAATGAAAGATAGTTTTACAGGAAAAGTCGTTGTGA +TACATCAACCAGCTGAAGAAGTACCACCAGGTGGTGCTAAAACAATGATTGAAAATGGTGTATTAGACGGTGTTGATCAT +GTATTAGGTGTACACGTCATGAGCACAATGAAAACAGGTAAAGTGTATTACAGACCTGGTTATGTTCAAACAGGACGCGC +ATTCTTCAAATTGAAAGTTCAAGGTAAAGGTGGTCATGGTTCATCACCACATATGGCCAATGATGCCATTGTTGCAGGTA +GCTACTTCGTCACAGCGTTACAAACAGTTGTATCTAGACGACTAAGTCCATTTGAAACCGGTGTTGTCACAATCGGTTCA +TTTGACGGTAAAGGTCAATTCAATGTCATTAAAGATGTTGTTGAAATTGAAGGTGATGTACGTGGATTAACAGATGCTAC +AAAAGCAACAATTGAAAAAGAAATTAAACGTTTATCAAAAGGATTAGAGGATATGTATGGTGTAACTTGCACCTTAGAAT +ATAACGATGATTATCCAGCATTATATAATGATCCAGAGTTTACTGAGTACGTGGCTAAGACGTTGAAAGAAGCAAACCTT +GATTTTGGTGTTGAAATGTGTGAACCACAACCACCTTCAGAAGACTTTGCATATTATGCTAAAGAACGCCCAAGTGCCTT +TATTTATACAGGTGCAGCGGTGGAAAATGGTGAAATTTACCCACATCATCATCCTAAATTTAACATTTCAGAAAAATCAT +TACTTATTTCTGCAGAAGCAGTAGGGACAGTTGTTTTAGATTACCTTAAAGGAGATAACTAACATGAATGAAACGTATCG +CGGGGGCAACAAGTTAATCTTAGGTATTGTATTAGGTGTTATTACATTTTGGTTGTTTGCACAATCACTTGTAAATGTTG +TACCGAATTTACAACAAAGTTTTGGTACAGACATGGGGACAATTAGTATTGCGGTCAGTCTAACTGCACTATTTTCAGGC +ATGTTTGTTGTTGGAGCAGGTGGTCTGGCAGATAAAATTGGGCGCGTGAAAATGACGAATATCGGTTTATTATTAAGTAT +TATTGGTTCAGCATTAATTATTATTACGAATTTACCGGCATTATTAATTTTAGGTCGTGTTATACAAGGTGTATCAGCAG +CGTGTATTATGCCTTCTACATTGGCCATTATGAAAACTTATTATCAGGGTGCTGAACGTCAGCGTGCCTTAAGTTATTGG +TCTATCGGTTCTTGGGGTGGCAGTGGTATCTGTTCACTCTTCGGTGGTGCAGTTGCGACAACTATGGGTTGGAGATGGAT +TTTCATCTTCTCAATTATCGTTGCCGTACTTTCAATGTTACTCATCAAAGGGACGCCTGAAACGAAATCAGAAATTACCA +ATACACATAAATTTGACGTTGCAGGGCTAATTGTTCTAGTAGTTATGTTGCTAAGTTTAAACGTTGTCATTACTAAAGGT +GCAGCACTTGGTTACACATCATTATGGTTCTTTGGTTTGATTGCAATCGTAATTGTAGCATTCTTTATTTTCTTAAATGT +TGAGAAAAAAGTAGATAATCCACTTATTGATTTTAAATTATTTGAAAATAAACCATATACAGGTGCAACGATTTCGAACT +TCTTATTAAACGGTTTTGCAGGTACATTAATTGTAGCGAATACATTCGTGCAACAAGGTTTAGGTTATACAGCATTGCAG +GCAGGATACTTATCAATTACTTATTTAATCATGGTGTTATTGATGATTCGAGTTGGTGAAAAATTATTACAAAAAATGGG +TTCTAAGCGACCAATGTTATTAGGTACATTCATTGTGGTCATTGGTATTGCACTTATTTCATTAGTATTCTTACCAGGCA +TATTTTATGTTATCAGTTGTGTCGTAGGATATTTATGTTTCGGACTAGGCTTAGGTATTTATGCAACACCTTCTACAGAT +ACAGCTATTTCGAATGCACCGTTAGATAAAGTTGGCGTTGCTTCAGGTATTTATAAAATGGCTTCATCACTTGGTGGCGC +ATTCGGTGTCGCAATTAGTGGTGCTGTATATGCTGGTGCAGTTGCTGCAACGAGCATTCATACAGGTGCGATGATTGCAC +TTTGGGTTAACGTATTAATGGGAATCATGGCATTTATCGCAATTTTATTCGCGATTCCTAATGATGATAAACGTGTCAAA +GATGCGAAATAATAACGATGACACGCCAAGTGTTTTGATAATGATATAAATGAAAGTAAAGCAAATTAAATGAGGAAATG +TATAGAGCAGTGCATATAGATAAATCTATGATTGGATTAACACTAATATAAATATAAATGAAAATGAAAAACCATCCACT +TAAATTTTTGATAAGTTAGGTGGATGGTTATTTATTGCGTTGTTCTAAAAATTTGGACATTTATATGGAAAGAACTATAT +ATAATGTGAAGGTTATGGCAATTCGCTTCAATGAAGGCGACGTGTAAAAGTGTATATTAAACGTTTCGGGGTCAGACTCC +GAGAACAGTGTCCGTCATGCTTACCGCATACTAATGAAGCATTATTATATCAGTGTTTATAGCCGATTTAAGATAATAAA +TTTTGCTAAACAAAAAAGCCAACCCATGAATGTTGGATTGGCTTTTTACATGCATCTGAATCTCTAATTTTAAAAAAATA +TGAATATAAATAAGACAGTAAAAATTAAATTTCAGTTGTTGCAATTTCTTCATCTGTAGGTACATCATCGTTAAGGCCAA +CAAGTGCTTCAGAAACATTTCGTGAATGATAACCGATACGTTCAAGAACACCAATCATATCGATATATAGTAATCCGCCT +TTTGTTGTACATTCACCACGATTAAGGCGTTTAATATGACCTTTGCGTAGTTTATGTTCAATATTAAATGATTCTCTACT +ACGTTCTACAATTTCATCTTTTTTCGTTTTGTCATAAACATCTAACATGTCGATGGCTTTATCAAATGACTCAGCAACAT +GGTTGAATAATTTATCCATACCGCGTTGTGCATCTTCTGTAATGCGAATATCTTCATCATGTTGGCGTTTTAATTGAGCG +ACATACTCTTCTGTTAGCTCTGCTACTTTTAAAATAGAGCGATTGACATCAAACATAACTGCTAAACGCTCAACGTCTGC +CTTCGTAATGGCTTTTGTAGAAATTCTAACTAAATAATTTCGAATGCTATCATTGATTGTTTCAACAGCTTGATGCTTTT +GTTCAAGCTTTTTGATCAATTTTTTATCGTCTTTTGTAATTTCGCGAATGTCTTCAAACATTGATAAGACAATCTGACCC +ACATTTTGTAATTCTTTTTGAGTTTCTTGTAATGCAACACCAGGTGCGTGATAAACAAGATCTTTGTTTAAGTGCTGAGG +TTTATAGTCATCAGCAATATCTTTACCTGGGACAAGCTTTGTAACAATCCATGCTAAACCTGCTACAAATGGTAATTGAA +TCAAAGTATTTGTTATGTTGAAGATACCATGTGATACTGCAATCGTCATCGCTGGTTTTAAGTGCCATAAATCTTGTAAC +AAACTAATCAAATGAATCACAACTGGCAAGAAAATTGTGAAGATAATTACCCCGATTAAGTTAAAGATGACGTGTACAAG +CGCCGCACGTTTTGCAGCGATTGAGCCGGCTAAACTAGCTAAGATAGCTGTAATCGTGGTACCAATGTTATCGCCTAGTA +ACACAGGGATTGCTGCGTTTAAGCTAATTAAATCTTGTTGATAAAATTCTTGTAAAATACCAATCGTCGCACTTGAACTT +TGAACTAGTGCTGTTAACCCTGCGCCGACAATGACAGCGAGTATTGGATTTGTAGACATATCAAGCATTAATTGCTTAAA +TCCATCTAATGATGCTAAAGGTTTAACGGCATCACCCATAAATTCTAGACCGAAGAATAGTGAACCGAAACCGAATAGTA +TGCGGCCAATGTTATTGATTTTAGAGCGTTTAAAGAAAAAGATTAAGAATGCACCTAATGCTAAAATTGGCATTGCATAT +TCGCCTAAATCTATACCGATAATAAATGCAGTTACCGTTGTTCCGATATTAGCACCCATTATCACTCCAATGGCTTGTTT +CAATGTCATAAATCCAGCTGTTACCAGTCCGATTGTGATAACTGTCGTACCTGAACTACTTTGTATTAAAATAGTTACAA +CGATACCTGCAATAACACCTAATACTGGATTTGATGTAAATTTGTTTAAAATATCTCGTAGCCTGTCTCCTGCTGATGCT +TGAAGCCCGTCTCCCATGATTTTTAAGCCGTAAAGGAAAATACCTAAACCACCTAAAAAGGAGAAAATGACTTCTGTAAC +CGACATTTCCATTATTTCACCTCAAATAAGCTTTATATTTAGATTATCGCTTATAATTGTAAATTTAATGTTAAGATTAG +GTAAAATTATTTAACAATATATGTTATTTGTATATGACTTGTAAAATATCGTCACTTATTATGTAAATTTTCAGTGTGAA +ATGGCAGGTTTGCAATCACTTGTTTAACAAAATGATGCAATCAATCATGTAATTATGTTTCATCAAAAAAATCATGAGAG +TGGAACAACGAAATAATTTTTATGAAAACATCATTTCTGTCCCAGTCTCATGATTTGAAATCACCAAATAAAAATCTATT +AATGGTTTTCGTTATAACAATTTGTGTTCTTTTAATAATGACTCAATGTACGTACCTTTTATCTTTTTAAGGAATCCTGC +TAATGCGAGTTTTTGCATTTTCGAATCTTTAGTAATCTCACGCAAATCTTGGTGGTCATTCAGTTCGTATATGGCATCCA +TTAAGACGCGAAGATCAAATGGACTATTGATGACTTCTGGAATACCACGATCTATATTTAGTAATTGATAAACAGCTTCC +ATGGCAGTACGAACCGAATATTCTGTTGTAAATACAGTGTCTCGCTCTGTTTCTGCAAAGTTACCAATAAATGCTAAGTT +CTGAGATTGATGCGGGACGACTAAAGGTCTGTCGCCGATAGCACGCGTCATGAAATAAGATGTGATATATGGCATATAAA +CAGGAATCGTATTAGATGCATGTTTTGCTAAGTCTTCAATTTTGTCAGTTGATACACCTAAGTGATACAGCCATTCTTGG +CATATTTCATTACCACTACATTCTGTAATTGGCTTTTTAATATAATCGCCGTTTACATCTGAATATAAGGCATAAATCCA +TGTAGATATTTCATTTTCAGGTTGGTCTTTAAACTGTTGCTGACGATTGATTGTAAAACTCATTTGCCATGCAGAATCAT +TGATTGTAATAATACCGCCTGTAACTGTTTTGCCTGCAAGTGGGTCACGTTTACAAATACTTTCTATTGTATCGATAATC +TCTTTATTGTTTGTTGTAGAAGTTGCTGAAACAAACCAACTTTTTTTAGGAATATTTTGGCAAAACTTATCAGGATTACC +AAATTCAGGACTTTGTCGCGCTAAATTTTTCCATAGTGTCCAACTACCACCTAATTCGTCAGTTGGTGGCGCTGGTGTAT +CATTATCACCATAAGTAGAGCTTTCTGTAATACTACCGTTTGTCACAAAGACAAGATCGTTTATAGTCAGTTTAATAGAT +TCTGCATTACCATTACGGTCAATTAATATTTCTCGGGCAATTTTTTGACTTGTCGTAACATCTATTTTAATATCTTCGAC +TTTTACATCGTATTCAAATTGAACCCCATGCGATTTTAAATATTCAACCATAGGTAATACTAAAGATTCATATTGATTAT +ATTTAGTGAATTTTAAAGCTGAAAAGTCTGCGAGACCACTAATATGATGAACGAATCGCATTAGATAGCGACGCATTTCC +ATTGCAGAATGCCACGGTTCAAATGCAAACATCGTTTTCCAGTAAATCCAAAAGTTTGAATTAAAGAAGTCATCGGAAAA +TACATCTGTTATTTTGACATCATCTAAATCTTCTTCATTCGTTAAGCATAAATCTAAAATTTCTTTAATCGCCGTTTTAG +TCAAAGTGAAGTCTCCGTCTGTGACTAAACGTTGACCCTGTTTCTCAATAACACGACAGCGAGAATAGTTAGGGTCTTCT +TTGTTTAGCCAATAGAACTCATCTAATACAGACGCGTTATCGATTTCTAATGAAGGGATAGATCTGAATAAGTCCCACAA +ACATTCAAAGTGGTTCTCCATTTCACGACCACCGCGGACAACATAGCCTTTTAAAGGCATATTTTCACCATCAAGACTAC +CACCTGCTTTAGGTAACTCTTCTAAAATATGAATCTTCGAACCTTCCATTTGACCATCCCTTATTAAAAAACAAGCTGCA +GCAAGTGAAGCTAGACCAGATCCGATTAAGTAAGCGGATTTGTTTTCTACATTTTCAGGTTTTTTAGGGCGCGCAAATGC +TTCATAATTTCCATAACTGTAATACATATCCACCAAGTCCTTTCGTATTAGAATACACTCAGACTATACCCCTTTGTGAA +TTGCAAATATTAGTATTTATATTGGTAAATTTGACGAAATTAGATCATTTTTATATTTTTGATTTTTAAAATTTTATCAC +TCGAATGTTTTTGGATTCTGAGGTGCGGGACTTGGAATAATTATGTATTTTGAAAGAAGTTACTTAAACTAACGCCTAGT +AGAGTGATAATTCCGCCTATAATAGCAAGGGTTGTTGGTAGCTCGTCTAATAACAGATAAGATAATAATAAAGAAACGAT +AGGTGTTAAATAAAGAGACATTGTTGCATCAGAGACACCAACTGACTTCACAATATAAGCAAGCAAAACGTATGGAATTA +TAGTAGGGAATATAGCTAAATAAAGTACCGATACTATTGATGTAAAAGTGGCGCCGTGTATATCGTTGATGATTTCAGGA +ATAAAAATAAGCATAAATGGTGAGCTTGCCATTATTGTATATAGTGTGAAAGCGATGAAGCCGTATTTTTCTATGTATTT +TTTCTGGAAAGTAAAATACAAACTTTCACTAAAAGATGCAAGTAAAATAATAAAAACACCTAATACATTAATAGTTGTGT +AATCATCTTTACTTATTGAAATAATGGATATTCCTATAAATGCGACGAGTGAACTTAACCAATTCCATTTTGAAAAATGC +TCTTTTAAAAATATATAAGCTAAAGCACTAGAAAAAATAGGCGTTGTAGAGACTAGAATTCCAGATATACCTGCACTAAT +CAAAGTTTCACCAAAATTTAAAGCTGTGTGATATATCACAAATCCACAAAATCCTAAAATAAAAATAACAGGGATATCTC +TTAGTTCAGGGGTAGGCAATTTCTTTATAATTACGAACGGCAAGAGAATTATTGTTGCTAAAATTAAACGAAATGCCGAC +AATGATTCTGCACTAAAATCATTTAACGCAATCTTTATCATTGGAAATGCAGATCCCCACAATATGATAGTAAATAAATA +TGATAGAAAAGTAGTGTCTCGAAGTTTATTCATTAATATCATCACTCCTTTAATTATGTGTTTCTATATTAAAAAATATG +ATTTAAAATGAGTACAACCAATTTTGAATGGATTTACCTATCCAATTTTAAAAGGGAGGGAGAAGATGGCTAAATATAAA +GATATTGCTAGTGACATAAGAGATAAAATAATCACAGGGGATTGGTTTTATGGAATGAAAATACCTTCACATAGGCAGTT +GGCGATACAGTACAACGTAAATAGAGTAACGATTATTAAAAGTATTGAGTTATTAGAAGCTGAAGGATTTATCTATACTA +AAGTAGGTAGTGGAACATATGTTAATGACTATTTGAATGAAGCACATATTACAAATAAGTGGTCTGAAATGATGTTATGG +TCCTCTCAACAAAGAAGTCAGTATACGGTGCAATTAATTAATAAAATTGAGACAGATGATTCGTATATACATATAAGTAA +AGGTGAATTGGGTATATCGTTAATGCCACATATTCAATTGAAAAAAGCCATGTCTAATACAGCCAGTCATATTGAAGACT +TATCTTTTGGTTATAATAATGGCTATGGTTATATCAAGTTAAGAGATATTATCGTTGAACGAATGTCAAAGCAAGGTATA +AATGTAGGTAGAGAAAATGTAATGATCACTTCAGGCGCTTTACATGCCATTCAACTTTTATCTATTGGGTTTTTAGGTCA +AGATGCCATAATAATTTCGAATACACCATCATATATTCACTCTACAAATGTTTTTGAGCAATTGAATTTTAGACATATTG +ATGTTCCTTATAATCAAATTAATGAAATTGATACCATCATTGATAGATTTATTAATTTTAAAAATAAAGCGATTTATATA +GAACCTAGGTTTAATAACCCGACAGGTCGTTCTTTAACGAATGAGCAAAAGAAAAATATAATTACTTATAGCGAAAGACA +TAATATTCCTATCATTGAAGATGATATCTTTAGAGATATTTTCTTTAGCGATCCAACTCCTTCTATCAAAACTTATGATA +AATTGGGAAAAGTTATACATATAAGCAGTTTTTCAAAAACGATTGCACCAGCAATAAGAATAGGTTGGATTGTTGCTTCT +GAAAAAATAATAGAGCAATTGGCAGATGTAAGAATGCAAATTGACTATGGATCCAGTATTTTGTCACAAATGGTTGTATA +TGAGATGTTGAAAAATAAGTCTTATGATAAACACTTAGTAAAGTTAAGGTATGTTTTAAAAGATAAACGAGACTTTATGT +TAAACATCCTCAATAATTTATTTAAGGATATAGCACATTGGGAGGTTCCAAGTGGAGGTTATTTTGTATGGTTAGTCTTT +AAAATAGATATAGATATTAAATATTTATTTTACGAATTGTTAAGTAAAGAAAAAATATTAATCAATCCGGGTTACATTTA +TGGCAGTAAAGAAAAGAGTATAAGGCTATCTTTTGCCTTTGAATCAAATGAAAATATTAAGCATGCGCTCTATAAAATTT +ATACATATGTGAAAAAGGTTTAATTAAAACAATAATTCGAATCATTATGTGGCATGTTAAACAGCTAAATATAAGCTATG +CACATTTAACAAGCGGATACTCCTGACGCAATTATAGTATCATTACATATTGTTTAATCGTTTGGGGTAATGTAATTAAA +AGGTAAAATAGGCAGAGTGTTATACATAAAATGTAAAGAAAGAGGCGATAGAATGTCTAAAATCAGGTCTTTTACAATAT +TAAGTCTACTTATTTACTTAGCTATGATGTGCTATACAGTAGTGACCTATCCAAAATTACCAACCAAAGTGCCTATTCAT +TATAATTTAGCAGGGGATGCTGATAATTTTGCTGATAAATGGGTGCTGCTTTTGATTAATAGCGCATTTATAGTGATTTG +GCTTATATTTTTCATTGCAGGTAGATACTATGAACGATTTGCCAAATGGTCACATTATAATCATACACCACGTGAAATTC +GAGCGATTAAATTATTTTTAAGTACGTTAAATTTAGAGATTATGAGCTATATGTCTATCTTCACAGTATTAGAAATTTGG +CAAATACAACACCATCATCAATTCAATTTACTATGGTTTAATATGATATTTATCATTATCATTGGTTTGACGCTTGTCAT +ATTTTGTTTACTTCCTACAATTCATAAAATGAGAGATTCTCAATAAAAATATAAACTCTCATGATGCAGTTTTGAAGCGA +ATTCCTGAGATTATTATAAATAATATCTAGTATTTATAATTAAAATGATATATTATTTATTATAGTTAAATATTGTGTAT +ATTTTGTGAACTTTTTGTAAAAATTCGATTGCCTGTCACATATAGGAGTGTTACATTTTAAATATGTGATCATCGCAAAA +TATAAGTTGAAATAGGTTGTAGATTAATCAGAATGATAAATATTTTATATAAAGAGAGGGAGTCATTATGACACTACTTA +CTGTAAATCCATTCGATAATGTCGGATTATCAGCCTTAGTTGCAGCAGTACCTATTATTTTATTTTTATTATGCTTAACC +GTTTTTAAAATGAAAGGCATTTATGCAGCATTGACAACTTTGGTTGTTACATTGATTGTGGCTTTATTTGTATTTGAATT +ACCAGCGCGTGTATCAGCAGGTGCGATTACAGAAGGCGTTGTTGCCGGTATTTTCCCAATAGGATATATCGTTTTAATGG +CAGTTTGGTTATATAAAGTTTCTATTAAAACAGGACAATTTTCTATTATTCAAGATAGTATTGCAAGTATTTCAGTGGAC +CAAAGAATCCAACTATTATTAATTGGATTTTGTTTCAACGCATTTTTAGAAGGTGCAGCAGGATTTGGTGTGCCAATTGC +GATTTGTGCAGTATTATTAATTCAACTTGGATTTGAACCATTAAAAGCAGCGATGTTATGTTTAATTGCTAATGGTGCGG +CGGGTGCCTTTGGTGCAATTGGTTTACCAGTTAGTATTATTGATACGTTTAACTTAAGTGGAGGCGTTACAACATTAGAT +GTTGCGAGATACTCAGCATTAACACTTCCAATTTTAAACTTTATTATTCCATTTGTTTTAGTATTCATTGTAGATGGTAT +GAAAGGTATTAAAGAAATTTTACCTGTCATTTTAACAGTGAGTGGTACATATACTGGATTACAATTATTATTAACAATAT +TCCATGGTCCAGAACTAGCAGACATTATTCCATCACTAGCAACAATGGTGGTGTTAGCATTTGTTTGTCGTAAATTTAAA +CCGAAAAACATTTTCAGATTGGAAGCGTCTGAACATAAAATTCAAAAACGAACGCCTAAAGAAATTGTCTTTGCTTGGAG +TCCGTTCGTAATTTTAACTGCCTTTGTATTAGTATGGAGTGCACCATTCTTCAAAAAATTATTCCAACCTGGAGGTGCAC +TTGAAAGTTTAGTAATAAAATTGCCAATTCCAAATACTGTGAGTGATTTATCGCCTAAAGGAATTGCGTTGCGTCTCGAT +TTAATTGGTGCAACTGGGACAGCGATTTTATTAACAGTAATTATTACAATTTTAATTACGAAGTTAAAATGGAAAAGTGC +AGGTGCTTTATTGGTCGAAGCAATTAAAGAATTATGGTTACCGATCCTTACAATTTCAGCTATCCTAGCTATTGCTAAAG +TTATGACATACGGTGGTTTGACTGTAGCAATTGGACAAGGTATTGCTAAAGCGGGAGCAATTTTCCCATTATTCTCTCCA +GTATTAGGTTGGATTGGTGTGTTTATGACTGGTTCAGTTGTAAATAACAATACTTTATTCGCACCTATTCAAGCGACAGT +GGCACAACAAATTTCAACAAGCGGTTCATTACTTGTGGCAGCTAACACTGCAGGTGGTGTAGCAGCGAAACTTATTTCAC +CACAATCAATTGCCATTGCGACTGCAGCTGTTAAAAAAGTTGGTGAAGAATCTGCATTATTAAAAATGACGTTAAAATAC +AGTATTATATTTGTTGCTTTTATTTGTGTTTGGACGTTTATACTAACGTTAATATTCTAAATATAAATAATGTTGTCACT +TGGATTCAAATGACATTTTAAATCTAATTATTCATGAATCGAACTAGTACGAAATGCAATGAGCATCTTGTCTAGTTCGA +TTTTTTAATGCCTAAAAATGTCATATATGTAATCAGAGTAGAAAGTGTTGAGGCGTTTCAGAAGTTGTTTAGAAAAGTAA +GTAAAATAAAAAATGCACTGAGCAACAAAAGATGTTGCTCGTGCATTTAGATGATTCTTATCATTTCAAATAAGAATGTG +TTAATCAACGTATATAAGTTAAAATTGGTTTGGATAAAATGATATCTATCGTTGTGTATTGTTTGTTTTTATAGTTCGCG +ACGACGTCCAGCTAATAACGCTGCACCTAAGGCTAATGATAATCCACCAAATACAGTTGTACCGATGAATGGATTTTCTT +CACCAGTTTCTGGTAATGCTTGAGCTTTGTTAGCATCTGCATGGTTTGCTGGTTGCTTCTTATCAACAACAAGTTCTTGA +CCAGGTTTGATCATGTTTTTATCAGCTAATTTGTTATCTGCAGCAATTTTGTCAGCAGTAGTGCCGTTTGCTTTTGCAAT +GTCATTTACTGTATCACCAGGTTTAACGACATGTACTCCGTTACCATCTTCTTTACCAGGTTTGTTGCCATCTTCTTTGC +CAGGCTTGTTGCCGTCTTCTTTACCAGGTTTTTTGTTGTCTTCTTTACCAGGCTTGTTGCCATCTTCTTTACCAGGTTTT +TTGTTGTCTTCTTTACCAGGCTTGTTGCCGTCTTCTTTGCCAGGCTTGTTGTTGTCTTCTTTACCAGGCTTGTTGTTGTC +TTCTTTACCAGGCTTGTTGTTGTCTTCTTTGCCAGGCTTGTTATTGTCTTCTTTGCCAGGCTTGTTATTGTCTTCCTCTT +TTGGTGCTTGAGCATCGTTTAGCTTTTTAGCTTCTGCTAAAATTTCTTTGCTCACTGAAGGATCGTCTTTAAGGCTTTGG +ATGAAGCCGTTACGTTGTTCTTCAGTTAAGTTAGGTAAATGTAAAATTTCATAGAAAGCATTTTGTTGTTCTTTGTTGAA +TTTGTTGTCAGCTTTTGGTGCTTGAGCATCATTTAGCTTTTTAGCTTCTGCTAAAAGGTTAGCGCTTTGGCTTGGGTCAT +CTTTTAGGCTTTGGATGAAACCATTGCGTTGTTCTTCGTTTAAGTTAGGTAAATGTAAGATTTCATAGAAAGCATTTTGT +TGTTCTTTGTTGAATTTGTTATCCGCTTTCGGTGCTTGAGATTCATTTAACTTTTTAGCTTCTGACAATAGGTTAGCACT +TTGGCTTGGGTCATCTTTTAAGCTTTGGATGAAACCATTGCGTTGTTCTTCGTTTAAGTTAGGCATATTCAAGATTTCAT +AGAAAGCATTTTGTTGTTCTTTGTTGAAATTGTTATCAGCTTTCGGTGCTTGAGATTCGTTTAATTTTTTAGCTTCACCT +AAAACGTTAGTGCTTTGGCTTGGGTCGTCTTTAAGACTTTGAATGAAGCCGTTACGTTGCGCTTCGTTTAAGTTAGGCAT +GTTCAAGATTTCATAGAAGGCGCTTTGTTGATCTTTGTTGAAGTTATTTTGTTGCGCATCAGCTTTTGGAGCTTGAGAGT +CATTAAGTTTTTGAGCTTCACCTAAAACGTTAGCACTTTGGCTTGGATCATCTTTAAGGCTTTGGATAAAACCATTGCGT +TGATCAGCATTTAAGTTAGGCATATTTAAGACTTGATAAAAAGCATTTTGTTGAGCTTCATCGTGTTGCGCAGCATTTGC +AGCAGGTGTTACGCCACCAGATATAAGTAATGTACCTAAAGTTACAGATGCAATACCTACACCTAGTTTACGAATTGAAT +AAATGTTTTTCTTTTTCAAATTAATACCCCCTGTATGTATTTGTAAAGTCATCATAATATAACGAATTATGTATTGCAAT +ACTAAAATCTATATTTATAATTAAATTTAAAGGTAAGTTTTACAACTTATAAAATAAATATTTTGCTGAAAGATTTATTC +AGGAAGTAAAAGGCTTAAAACCGCAAAATCACGCTATTTCGATTAATAAAAACAGATAAAATATAGAGATATTTTTTATA +ATGAAAGCGGTTTAATACATATGAGTTAAAAAATATTTTTAAGAATAAAGTGGGGCTTTGAATGTGCTGAGGTTTAATTT +CGGAAATGGTTTTAAGTGATATAGAAAATAGAAAGTTGTTCATTATGTGCTGAGGTTTTATTAAAAAATAGAAAACACAA +GTGCACTTTAGAATACAGCACACTTGCGTAATAGAGATATTATTCAAAAACAAGATGTAAATGATCTTTATCTGCTAATA +ATTGATTCACTTGAGCTAATAATTGTTCAGCATGGTCTTGCTGCGCGTCATCCATATGAATTAAAATTTTTCTTTCATCT +TCAGTTGAGCGTTCTTTTATTAGATAGCCTTGCTTTTTTAAATTATTGAGAGCTCTAACAGTTTGAGGGTATTTATGGTG +GATTGTTTCAATTAAATCTTTAAGAAGAACGATGTTTTTATTTTGAGAAGTGATAATAGCTAGAATTGTGAATTCTACAA +AACTTAATGTTAGATGTTTTTTGATAATATTCTTGAAATACATTGTATACATCATCAAGTTTAGAAATTCTTTACTATCT +TTTGGTATCATCTGTGATTCACTTTGATCTGCAAGGTTAAATTGTTTAATGATTTGATCAAACAATGTAACACGTTCTGC +AATTTTCTCTCGTTGTTCTTCAGATATTGAAATGTAAGTATTACGCTCATCAATTTTACTTCGAACTTTACTAATATATG +AATGTTTCACAAGTACTTTTATATGCTGTACTAAATCCGATTGTTTATAACATAAATCTGAAACAATCTTCTTAAATGGA +AGTGTGTTTTCTTGCTGATGAAATAAATAAGTCAGTAATATAAATTCTTTTATAGTCATATCGACTTCAGGCTTGACTTT +TTTCTTAAAACGAAACATATATGCTTCAATGATTATAAAATCTCTAATTTTGTCATGGTTATTATATTTCATTGTTTTAT +CTCCTTGTATATGCACTTTATTTTAATATAACACAATATAATAAGAAAAATAATCCATAATTTACAATTAAAAATAATAT +GATATTTTTATTAAAATTTGTGTTTTTGAATAAATGACATTATCAAACACTATATTTAAATAAGAAATAGTTATCGATAT +TTGAAAAATCGAAGAATATTGATTGATATTATAGAATATGAGTCGGTGTTATTTGTATATAAAGTATTAATATGTAATTA +AGAAAGTCGACGCTTGTTAACAATAACAACAACTATATGTATAAAAAAACCTCGCAACGGTTAGTTAACTGAATCATTTA +GCTAACTTCGTTGTGAGGTCATTTTGTTTTAATAATATCGTTATAACTTTTTCACGGTTAATAATAAGTATATGAAGAAT +GGGGCACCAAAAGCAGCAATAAATACACCTGCTGGCACTTCTTTAGGCAAGAATAAGGTACGCCCAATTAAGTCTGCAAT +AACAATTGATATGGCACCAATCATTGCTGACATTAGTAACTTTTTAGCATAACTTCCGCGAACGATTGTTTTCGCGATAT +GTGGTGCGATTAAACCGACAAACCCAATGTTACCTACTAAACTGATTGCCATAGATACGAGTATAGTAGAAGTGATTAAT +TGGATTAGTTTCATACGTTGTACATGTAAGCCTAAGCCAATCGCTACAGGGTCATCAAGTATAGATATTTTCATTTTTGG +TATAACAAGAAATAACAACGGCACAACAGCTAAAATAACCATACCCAAAATGATTGTATCTTTAAACGTAGCACCGTAAA +GACTTCCGACTAGCCATGTATAAGCTTTGGCAGCAGATAATTGCTTCGTTGTAATGAGTAATCCTTGGACAAGCGCAATA +AACAACGTTTGCATCGAAATACCGATGATTATGAGTGTTGTCGGGCGTATTTGTCCTTTCGTTTGAAACACTAATAGTAT +CATCATTGCAACTGCGCCACCTAATACTGCAAATAGTGGAAGTAAATGTATTGTTAAATGGCTGAAAAATGCAATAAAGA +CAACAGCACTTAAGCTAGCACCACCTGTGATACCGATAATATCAGGTGAGGCAATTGGATTTTTTAATACATTTTGCAAC +ATTAAACCACTCATTCCTAGTGCGGCACCTGCTAAAATCGCAAGTGTAATGCGAGGTAAGCGTAATACTTCTAAAGTGAA +TTGATCCATACTGTCATTTGGATTTATAAAGTACATCAGTACGCGTTGTAATGGTATAAAGCTTGAACCAATCATCATAC +TTACCACTGAAACGATGGCTAAAAAGATTAACGCGAAGATGAGATGGTAATTGTCTTTTTTATTAATCTTTTCGGTCATA +AGCGTTGACGTCCTTTCTTCATAATATAGATTAAGACAATAGCGCCAATGACAGCGGTAACGACACCGATAGGCAACTCT +AGTGGCTTAATTATTATACGAGCAACAATGTCTGAAATGATCATTAGGATTGCTCCAGCTAATGCAGTAAAAGGAATTAA +ATACTTATAGTTTGGTGGTAATAATCGTTTGCTAATATTCGGTACGATAAGACCCACAAAGACGATTGATCCAGCTACGG +CTACCGAAATACCGGCTAACATACTGATGAGCATAATAATCATCCATTTGATTAATTTTATGTTTTGACCGAGGCCGGTT +GCAATGTCGTCACTTGTCATCAAGATGTTGATGTGTGCAGCCATGCTAAATGCAATTAAAATAAGTATCAATACAAGCGG +AATAATCCATGGGATATCCCAAATATTACGTAATGAAACGGAGCCACTTAACCAAAATAATAGGCCTTGTAAGTCTGTTT +CGTTCATAATAAGTATGCCTTGAGTAAAGGCTGTAAATAGCATCGCAATCGCAGCACCTGCCAAAATGACACGGTGAGGT +GAGAATAGTGTTTGTCTAAACATACCTAGTGCAACAACTAATACAGTAACAACAATAGCCCCCAAAAATGCAATAACTAC +AATCATTTTAAAAGATTGAATTTGGATAAATGTAATACTAAAAATGACAAAAAATACTGCGCCTGCATTGACACCGAAAA +GCCCTGGTGAGGCTATTGGGTTTCGTGTAAGTGCTTGCATCAACAAACCTGAGACAGCAAGGGCAGCACCAGTCAATAAC +GCAATGATTGTTCTCGACGCCCGTGCACCAGTGACAACATCATGTAAATCGTTTTCACTATCAAAGTTGAATAACGCCTG +TATCACCGTACCTGGTGACACAAGCGTATTTCCAATCATTAAACTTAAGATAGCTACTATTGCAAGACATAAACCAGCAA +TAACGATTTGGTATTTTGGTTTAAGTAGCATCGTAAAACTCCTTAATTATTTTGATTGTTTTTCAATATTTAACTTTTCA +TATAAATCGTCAATAAGTTTTAATGAAGATTTATATCCGCCAGCTAAGTTCCAAGTGATTTCATCTAAATCATCAGATAC +TTGGTTGTTTTTAACTGCGTCTAAATTTTTCCACTCTTTACTTGAAGTCCATTCGCTTTCAGTCTTTTTAACTAATGCAG +CATCTTTCGCATTTGGATCTGATTTTACTACAAAAATATGATCAGCGTTCATTAATGGAATGCTTTCTTTAGATGTAAGT +TGGATAATATCTTTACCATTATCAACTTGTTTTTGTAAGTCTTTATTACGTTTGAATCCTAAATCATTTAAGATTTCACC +AGCATATCCACCAGCATAAATTCTTGTATGATCAGCACGGAAGTTAACAACTGAAGCTTTCAATGGCCATGCATCTTTAT +ACTTTGCTTTTGCATCTTTTTGGAATGCAGCTACTTTATCATCGTACTTTTTAAGTAAATCTTCAGCTTCTTTTTCTTTC +CCTAAAGCTTTCCCCATTAACTTAGTTGTATCTTTGAATTTGAAAACTGTATCAGTAGAAACTGTTGGTGCGATTTTAGA +TAATTGATCGTAAACTTTTTCATTTCTAACTTTTGACGCGACAATTAAGTCCGGTTTTAATTTAGAGATTTCCTCTAAGT +TAGGTGCAGGTTCTTGACCTACAATCTTAGTATCTTTTAAATCATTTTTTATGTATTCGAATTTCGGTTTTTGTGTCCAT +GATTCTACAGCACCTACAGGTTTAACACCTAAAGATACAGCGACGTCAGTGGCACCTTGATATAGCGTAACAACACGCTT +TGGTTTCCCTTTAATTTCAGTTGTACCCATTGCATGTTTAATTGAAGTTGTTTCCTTATCTTTGTTATCAGATGATTGTT +TATTTGAATTCCCACTACATCCTGCTAAAACAAGTAGGAAAGCAAGCGTAACAACAAGCATTTTAATTACTTTATTCATT +GACTAATTAGCCTCCTTCGTGATGTATGACAATGAGAATCATTATCACGATTTAGTATGAATTAAATTTTTTCCTAAGTC +AATAAAATATTTATGATTTACATGCAACTTATAATTATTTGACATATAAATGCATAAAAAATATAATCCTAATTACTTGA +TAGTGAGAATCATTATCAATTAGGTAACACACAATATTATAGAATTTTAAATTTGAGGAGGAAGCGCTTTTGATTGAAAA +AAGTCAAGCATGTCACGATTCATTGTTAGATTCTGTAGGGCAAACACCTATGGTTCAACTTCATCAACTATTTCCGAAAC +ATGAAGTGTTTGCAAAGTTAGAGTATATGAATCCTGGAGGCAGCATGAAAGATCGACCTGCCAAGTACATCATTGAACAT +GGTATTAAACATGGTTTAATCACTGAGAATACACATTTAATTGAAAGTACTTCTGGTAATTTAGGCATTGCGTTGGCAAT +GATAGCTAAAATCAAGGGATTAAAACTCACGTGTGTTGTTGATCCTAAAATATCACCAACAAATTTGAAAATTATTAAAA +GTTATGGTGCCAATGTAGAAATGGTTGAAGAACCTGATGCACATGGGGGTTATTTAATGACTCGTATTGCAAAGGTGCAA +GAACTGTTAGCCACTATTGACGATGCATATTGGATTAATCAATATGCGAATGAGTTAAATTGGCAATCCCATTATCATGG +TGCAGGCACAGAGATTGTTGAAACAATTAAGCAACCTATAGATTATTTTGTCGCGCCAGTCAGCACGACAGGTAGCATTA +TGGGTATGAGTAGAAAAATAAAAGAAGTGCATCCAAACGCACAAATTGTTGCTGTTGATGCGAAAGGGTCAGTCATTTTT +GGTGACAAACCTATTAATAGAGAATTACCTGGTATCGGTGCTAGTCGTGTACCCGAAATATTGAATAGATCAGAAATTAA +TCAAGTGATCCATGTAGATGATTATCAATCTGCTTTGGGCTGTCGAAAACTGATTGATTATGAAGGCATATTTGCCGGAG +GTTCAACAGGTTCGATTATTGCAGCGATTGAGCAGTTGATAACGTCAATTGAAGAAGGTGCAACAATTGTCACGATTTTA +CCAGATCGAGGCGATCGTTACTTAGATTTAGTTTATTCAGATACATGGTTAGAAAAAATGAAATCAAGACAAGGAGTTAA +ATCAGAATGAATAGAGAGATGTTGTATTTAAATAGATCAGATATTGAACAAGCGGGAGGTAATCATTCACAAGTTTATGT +GGACGCATTAACAGAAGCATTAACAGCCCATGCGCACAATGATTTTGTACAACCGCTTAAGCCGTATTTAAGACAGGATC +CTGAAAATGGACACATCGCAGATCGAATTATTGCAATGCCAAGTCATATCGGTGGTGAACACGCAATTTCAGGTATTAAG +TGGATAGGTAGTAAGCACGACAATCCATCGAAACGTAATATGGAGCGTGCAAGTGGCGTCATTATTTTGAATGATCCAGA +AACGAATTATCCAATTGCAGTTATGGAAGCAAGTTTAATTAGTAGTATGCGTACTGCAGCAGTTTCAGTGATTGCAGCAA +AGCATTTGGCTAAAAAAGGATTTAAAGACTTAACAATCATTGGATGCGGGCTAATCGGAGACAAGCAATTACAAAGTATG +TTAGAGCAATTCGATCATATTGAACGCGTGTTTGTTTACGATCAATTCTCTGAAGCATGTGCACGCTTTGTTGATAGATG +GCAACAACAGCGTCCGGAAATTAATTTTATTGCGACAGAAAATGCTAAAGAAGCAGTATCAAATGGTGAAGTAGTCATTA +CATGTACCGTAACGGATCAACCATACATTGAATATGATTGGTTACAAAAGGGTGCATTTATTAGCAACATTTCTATCATG +GATGTGCATAAAGAAGTCTTTATTAAAGCTGACAAAGTCGTAGTAGATGACTGGTCACAATGTAATCGAGAAAAGAAAAC +TATTAACCAATTGGTGTTAGAAGGTAAATTCAGCAAAGAAGCTCTTCATGCTGAACTAGGACAACTTGTGACAGGTGACA +TACCAGGACGTGAAGACGATGATGAGATCATATTACTTAATCCGATGGGTATGGCTATCGAAGATATTTCAAGTGCTTAT +TTTATTTATCAACAGGCACAACAACAAAATATTGGGACAACATTGAACCTATATTAAGAATGCGAGGTGTCTGAACATTG +CAGAATCATACAGCAGTCAATACAGCACAAGCGATAATATTAAGAGATTTAGTTGATGCATTATTATTTGAAGATATAGC +CGGAATTGTATCGAATAGTGAGATTACTAAAGAAAACGGACAAACGCTTTTGATATACGAACGTGAAACACAACAAATAA +AGATACCTGTTTATTTTAGTGCTTTAAATATGTTTCGTTACGAAAGTTCACAACCAATTACGATAGAGGGAAGGGTGTCT +AAGCAACCTTTAACGGCAGCTGAATTTTGGCAAACAATTGCTAATATGAATTGTGATCTAAGTCATGAATGGGAAGTGGC +TCGCGTTGAAGAAGGACTGACTACTGCTGCCACACAGCTTGCTAAACAATTATCAGAATTAGATTTAGCGTCACATCCTT +TTGTGATGTCAGAGCAGTTTGCAAGTTTAAAAGATCGTCCATTTCATCCATTAGCTAAAGAAAAAAGAGGATTAAGAGAA +GCGGATTATCAAGTGTATCAAGCTGAATTAAATCAATCATTTCCTTTAATGGTTGCAGCAGTTAAAAAGACACATATGAT +TCATGGCGATACTGCAAATATCGATGAATTAGAAAATTTGACAGTACCTATAAAAGAACAAGCGACAGACATGTTAAATG +ATCAAGGGTTATCAATAGATGACTATGTACTATTTCCGGTACATCCTTGGCAATATCAGCATATTCTGCCGAACGTCTTT +GCGAAAGAGATTAGTGAAAAGTTGGTTGTACTATTACCGTTAAAATTTGGAGATTATCTGTCGTCTTCAAGTATGCGTTC +ATTAATTGATATTGGCGCACCGTATAACCATGTCAAAGTACCATTTGCAATGCAGTCATTAGGCGCATTAAGGCTAACGC +CTACGCGTTACATGAAAAACGGAGAACAAGCAGAACAATTATTACGTCAGCTTATAGAAAAAGATGAAGCACTAGCTAAG +TATGTCATGGTTTGTGATGAAACAGCTTGGTGGTCATATATGGGTCAAGATAATGATATTTTCAAAGATCAATTAGGTCA +TCTAACTGTTCAGCTAAGAAAGTATCCCGAAGTGCTAGCCAAAAATGATACGCAACAGCTAGTGTCAATGGCAGCACTCG +CGGCAAATGATCGCACTTTATATCAAATGATTTGTGGAAAAGATAATATTTCTAAAAATGATGTCATGACGTTATTTGAA +GATATCGCGCAAGTCTTTTTAAAGGTAACACTATCATTTATGCAATACGGCGCATTACCAGAGTTGCATGGTCAAAATAT +ATTGTTGTCATTTGAAGATGGACGTGTACAAAAATGCGTGTTACGTGATCATGATACTGTCAGAATTTATAAACCATGGC +TAACAGCACATCAGCTTTCATTGCCGAAGTATGTCGTCAGAGAAGATACACCTAATACGCTAATTAATGAGGATTTGGAA +ACATTCTTTGCTTATTTTCAAACATTAGCTGTATCGGTAAATCTATATGCCATTATTGATGCAATTCAAGATTTATTTGG +TGTAAGTGAGCATGAACTTATGTCGTTGTTAAAACAAATTTTAAAAAATGAAGTGGCAACTATTTCCTGGGTTACAACTG +ATCAGCTAGCTGTCAGACACATTTTATTTGATAAACAGACGTGGCCATTCAAACAAATTTTATTACCATTGCTATATCAA +CGTGATAGTGGTGGAGGTAGTATGCCTTCAGGTTTAACTACCGTACCAAATCCAATGGTGACATATGATTAATCAGTCTA +TATGGCGCAGTAACTTTCGCATTTTATGGCTCAGTCAGTTTATAGCGATTGCTGGACTGACAGTACTTGTGCCATTATTG +CCAATTTATATGGCATCACTACAAAATCTATCAGTCGTAGAAATACAGTTGTGGAGTGGTATAGCGATTGCTGCTCCAGC +TGTAACGACGATGATAGCTTCGCCGATATGGGGGAAGCTAGGTGATAAGATCAGCCGAAAATGGATGGTGTTAAGAGCGT +TACTTGGTTTGGCGGTATGCTTATTTTTAATGGCATTGTGTACGACACCATTACAGTTTGTACTTGTGAGGTTATTGCAG +GGACTATTTGGTGGTGTTGTTGATGCATCAAGTGCGTTTGCGAGTGCAGAGGCGCCAGCTGAAGATCGTGGAAAGGTATT +AGGAAGACTGCAAAGTTCAGTCAGCGCAGGGTCTCTTGTGGGGCCATTAATTGGCGGTGTTACAGCTTCGATATTAGGTT +TTAGTGCGTTACTGATGAGTATTGCCGTTATTACTTTTATTGTCTGTATTTTCGGTGCATTAAAATTGATTGAAACGACA +CATATGCCAAAATCACAAACACCAAATATTAATAAAGGTATTCGCCGTTCATTTCAATGTCTATTATGCACACAACAAAC +ATGTCGATTTATTATCGTTGGCGTTTTAGCAAACTTTGCTATGTATGGCATGCTAACTGCATTATCACCACTTGCTTCAT +CAGTGAATCATACAGCGATAGATGACCGTAGTGTGATTGGATTTTTACAGTCCGCATTTTGGACGGCTTCGATATTAAGC +GCGCCTTTATGGGGACGCTTTAATGATAAATCATATGTTAAATCAGTATATATATTTGCCACGATTGCATGTGGTTGTAG +TGCGATACTGCAAGGTTTAGCGACGAATATAGAGTTTTTAATGGCTGCAAGAATACTTCAAGGATTAACATATAGTGCAT +TGATTCAAAGTGTCATGTTTGTTGTCGTGAATGCGTGTCATCAACAACTTAAAGGCACATTTGTTGGAACGACGAACAGT +ATGTTAGTTGTTGGTCAAATTATTGGCAGTCTTAGTGGCGCTGCCATTACAAGTTATACTACACCAGCTACTACGTTTAT +CGTTATGGGCGTAGTATTTGCAGTAAGTAGTTTATTTTTAATTTGTTCAACCATCACTAATCAAATCAACGATCACACAT +TAATGAAATTATGGGAGTTGAAACAAAAAAGTGCAAAATAAAGAATTAATACAACATGCAGCGTATGCGGCTATCGAACG +CATTTTAAATGAATATTTTAGAGAAGAAAATTTATATCAAGTACCACCTCAAAATCATCAATGGTCTATACAATTATCAG +AGCTCGAAACTTTAACGGGTGAATTTCGCTATTGGTCTGCGATGGGGCATCATATGTATCATCCAGAGGTATGGCTTATC +GATGGAAAAAGTAAAAAAATAACAACTTATAAAGAAGCAATTGCGCGTATTTTGCAACATATGGCTCAAAGTGCAGATAA +TCAAACGGCAGTGCAACAACATATGGCGCAAATTATGTCTGACATCGATAATAGCATTCATCGCACGGCACGTTATTTGC +AAAGTAACACAATAGACTACGTAGAGGATCGTTATATCGTTTCAGAACAATCTTTATACTTAGGTCATCCATTTCATCCG +ACTCCTAAGAGTGCAAGTGGGTTTTCAGAAGCAGATTTAGAGAAATATGCACCCGAATGTCATACATCATTCCAATTGCA +TTATTTAGCTGTGCATCAAGATGTTCTGCTCACGCGCTATGTAGAAGGTAAAGAAGATCAGGTTGAGAAAGTGTTGTATC +AATTAGCAGACATAGATATATCAGAGATACCCAAAGATTTTATTTTATTACCAACACATCCTTATCAAATCAATGTGTTG +CGACAGCATCCACAGTATATGCAATATAGTGAACAAGGTTTAATAAAAGACCTTGGCGTTTCCGGTGATTCAGTGTACCC +GACGTCTTCGGTTAGAACTGTATTTTCAAAAGCATTAAACATTTATTTAAAATTACCGATACACGTTAAAATCACTAATT +TTATACGTACGAATGACCTTGAACAGATTGAACGGACAATTGATGCCGCGCAAGTTATCGCATCAGTCAAAGATGAGGTT +GAAACACCCCATTTTAAATTGATGTTTGAAGAAGGATATCGTGCATTGTTACCGAATCCATTAGGGCAAACAGTTGAACC +TGAAATGGATTTATTAACAAATAGTGCCATGATTGTTCGTGAAGGGATACCGAATTACCATGCTGATAAAGATATTCATG +TATTGGCGTCATTATTTGAAACGATGCCTGATTCACCGATGTCTAAGTTATCACAAGTGATTGAGCAAAGTGGTTTAGCA +CCAGAAGCATGGCTTGAATGTTATTTGAATCGTACATTATTGCCGATATTAAAGCTGTTTAGTAACACAGGCATTAGTCT +AGAAGCACATGTACAAAATACATTAATTGAATTAAAAGATGGCATACCCGACGTATGCTTTGTCAGAGATCTTGAAGGCA +TTTGTCTATCTAGAACGATTGCTACTGAAAAACAGCTTGTGCCAAATGTTGTGGCAGCATCAAGCCCTGTTGTATATGCA +CATGATGAAGCATGGCATCGTCTTAAATATTACGTTGTAGTAAATCACTTAGGACATTTAGTATCAACTATTGGTAAAGC +GACTAGAAATGAAGTTGTGTTATGGCAACTTGTAGCGCATCGTCTTATGACTTGGAAAAAAGAATACGCGAATAACGCAG +TATTTGTTGACTGTGTAGAAGATTTATATCAAACGCCGACCATTGCGGCTAAAGCGAATTTGATGAGTAAATTGAATGAT +TGTGGTGCAAACCCTATTTATACACATATACCAAATCCAATTTGTCATAACAAGGAGGTATCGTATTGTGAATCAAACAA +TTCTTAATCGTGTAAAGACTAGAGTGATGCACCAACTGGTATCATCACTTATTTATGAGAATATTGTTGTGTATAAAGCG +TCATATCAAGACGGTGTCGGTCATTTTACAATAGAAGGACATGATTCAGAGTATCGTTTTACTGCTGAAAAGACACATAG +CTTTGATCGTATACGTATCACATCACCAATTGAGCGTGTCGTAGGAGATGAGGCAGATACAACAACAGACTATACACAAT +TATTGAGAGAGGTTGTATTTACATTTCCTAAAAATGATGAAAAGCTAGAACAATTTATTGTCGAGTTATTACAGACAGAA +TTAAAAGATACGCAAAGTATGCAGTATCGAGAATCAAACCCACCAGCAACACCTGAGACATTTAACGACTATGAATTTTA +TGCGATGGAAGGGCATCAGTATCATCCAAGTTACAAATCACGTTTAGGATTTACGTTGAGTGATAATTTGAAATTTGGTC +CTGATTTTGTACCAAACGTTAAACTGCAGTGGTTAGCTATCGACAAAGATAAAGTAGAAACGACGGTATCAAGAAATGTT +GTAGTTAACGAAATGTTACGTCAACAAGTTGGCGATAAGACTTATGAACATTTTGTACAGCAAATTGAAGCATCTGGCAA +ACATGTAAATGATGTTGAGATGATACCTGTACACCCATGGCAGTTTGAACATGTCATCCAAGTTGATTTGGCTGAAGAAA +GGCTTAATGGCACAGTACTATGGTTAGGGGAAAGTGATGAGCTATATCACCCTCAACAATCGATTCGTACGATGTCGCCA +ATAGACACGACAAAATATTATTTAAAGGTACCAATAAGTATAACGAACACTTCAACGAAACGAGTGTTGGCGCCTCATAC +AATTGAAAATGCAGCGCAAATTACGGATTGGTTAAAGCAGATACAGCAACAAGATATGTATTTAAAAGATGAATTAAAGA +CAGTTTTTCTAGGGGAAGTCTTAGGACAGTCTTATTTAAATACACAACTTTCGCCTTATAAACAAACTCAAGTTTATGGT +GCGTTAGGTGTTATATGGCGTGAAAATATATATCATATGTTAATCGATGAAGAGGATGCGATACCATTTAATGCACTTTA +TGCAAGTGATAAGGATGGTGTACCATTCATTGAAAATTGGATTAAACAATATGGTTCTGAAGCTTGGACAAAGCAATTTT +TAGCTGTAGCGATTCGTCCAATGATTCATATGCTTTATTATCACGGTATTGCCTTTGAATCGCATGCACAAAATATGATG +CTCATTCATGAAAATGGTTGGCCTACACGTATTGCCTTAAAAGATTTCCATGATGGTGTTCGTTTTAAGCGTGAGCATTT +AAGTGAAGCTGCTTCACACCTGACATTAAAGCCAATGCCAGAAGCACATAAAAAAGTGAATAGTAATTCATTTATTGAAA +CAGATGACGAACGTTTAGTACGCGACTTTTTACATGATGCATTTTTCTTTATTAATATCGCCGAAATCATCTTATTTATT +GAAAAGCAATATGGTATCGATGAGGAGCTGCAATGGCAATGGGTTAAAGGCATCATCGAGGCGTATCAAGAAGCATTTCC +AGAGTTGAATAACTATCAACATTTCGATTTGTTTGAACCTACGATTCAAGTTGAAAAGTTAACGACACGTCGATTATTAA +GTGACTCCGAGTTAAGAATTCATCATGTTACAAATCCATTAGGTGTAGGAGGTATCAATGATGCAACAACTATCTCTGAA +ACATAGATTAAACAATGGTGATTCAGTTTATGGCATTTTTAATTCTATACCGGACCCATTGATGATCGAGGTTATCGCAG +CAAGCGGGTATGACTTTGTTGTGATTGATACAGAACACGTGGCGATTAATGATGAGACACTAGCGCATTTAATTCGTGCA +GCTGAAGCAGCGCATATTATACCAATTGTACGTGTCACTGCAGTGATAGATAGAGATATCATTAAAGTGTTGGATATGGG +TGCGAGAGGTATTATTGTGCCACACGTTAAAGATCGTGAGACAGTTGAGCATATTGTGAAATTAAGTCGTTATTACCCGC +AAGGATTAAGAAGTTTGAATGGTGGTCGCATGGCAAGATTTGGACGTACACCATTACTTGATGCAATGGAGATGGCTAAT +GAGCATATTATGGTGATTGCCATGATAGAAGATGTTGAAGGGGTTATGGCCATTGACGATATAGCACAAGTCGAAGGTTT +AGACATGATAGTCGAAGGTGCCGCAGATTTATCGCAGTCACTTGGCATACCATGGCAAACGCGTGATGATCAAGTAACAT +CACATGTTCAACATATTTTTGAAGTTGTGAATGCACATGGTAAACATTTTTGTGCATTACCACGTGAAGATGAAGATATT +GCAAAATGGCAGGCACAAGGTGTACAAACATTTATTTTAGGTGATGATCGCGGAAAAATATATCGCCATTTAAGTGCATC +TCTAGCGACGTCTAAACAGAAAGGGGATGAAGGCTAATGCGTATAGTTCAACCTGTTATTGAACAATTAAAAGCACAATC +TCATCCAGTTTGTCATTATATCTATGATTTAGTCGGACTGGAACATCATTTGCAACATATTACATCGTCATTGCCGAGTA +ATTGTCAAATGTACTATGCAATGAAAGCAAATAGTGAACGAAAAATCCTAGATACAATTAGTCAGTATGTTGAAGGATTC +GAAGTTGCATCTCAAGGTGAAATAGCAAAAGGTCTTGCTTTTAAACCAGCAAATCATATTATTTTTGGTGGCCCTGGTAA +GACAGACGAGGAACTAAGATATGCAGTAAGTGAAGGTGTTCAGCGTATTCATGTTGAAAGTATGCATGAATTACAACGGC +TAAATGCCATCTTAGAAGATGAAGATAAGACACAACACATTTTATTGCGTGTTAATTTAGCAGGACCATTTCCCAATGCA +ACGTTGCATATGGCAGGACGCCCAACACAATTTGGTATTTCTGAAGACGAAGTTGATGATGTCATTGAAGCTGCGCTCGC +AATGCCAAAGATTCATCTAGATGGATTTCATTTTCATTCTATTTCTAACAATTTAGACTCGAATTTACATGTCGATGTAG +TGAAACTTTATTTTAAAAAAGCAAAGGCATGGTCTGAAAAACATCGATTTCCACTCAAACATATCAATCTTGGTGGTGGC +ATAGGCGTTAACTATGCAGATTTAACTAACCAATTTGAATGGGATAATTTTGTAGAACGTTTTAAAACACTTATCGTTGA +GCAAGAAATGGAAGATGTGACATTGAACTTTGAATGTGGGCGCTTTATTGTGGCACATATTGGTTACTATGTGACAGAAG +TGCTAGACATTAAGAAAGTACATGGTGCTTGGTATGCCATATTAAGAGGAGGTACGCAACAATTTAGACTGCCGGTATCT +TGGCAACATAACCATCCTTTTGACATTTATCGCTATAAGGACAATCCATATTCATTTGAAAAAGTTTCAATTTCGAGACA +GGACACAACGTTAGTCGGTCAATTATGTACACCGAAAGATGTCTTTGCTAGAGAAGTACAGATAGACGCAATCAGTACAG +GCGACGTTATTGTTTTCAAATATGCAGGTGCATACGGATGGTCTATTTCACATCACGATTTCTTAAGCCATCCACATCCT +GAATTTATTTATTTAACACAAACAAAGGAGGATGAATAACTATTGAATCATATTCATGAACATTTAAAATTGGTACCAGT +AGATAAGATTGATCTTCACGAAACATTCGAACCTTTAAGATTGGAAAAAACGAAAAGTAGTATTGAAGCAGATGATTTTA +TACGTCATCCTATTTTAGTGACAGCGATGCAACATGGTAGATATATGGTTATAGATGGTGTGCATCGGTATACAAGTTTG +AAAGCGTTAGGATGTAAGAAAGTTCCAGTGCAAGAAATCCATGAAACACAATATTCAATTAGTACATGGCAACATAAAGT +TCCATTTGGTGTGTGGTGGGAAACGTTACAACAAGAACATCGCTTGCCATGGACTACTGAGACAAGACAAGAAGCGCCAT +TTATTACAATGTGTCATGGTGATACAGAACAATATTTGTATACAAAAGATTTAGGCGAAGCACATTTTCAAGTATGGGAA +AAGGTTGTCGCAAGTTATAGTGGTTGTTGTTCTGTAGAGAGAATTGCACAAGGTACATATCCTTGTCTTTCTCAACAAGA +TGTACTCATGAAGTATCAGCCATTGAGTTATAAGGAAATTGAAGCGGTTGTTCATAAAGGGGAAACTGTGCCAGCAGGTG +TGACACGCTTTAATATTTCAGGACGATGTCTTAATCTTCAAGTACCACTGGCATTACTTAAACAAGATGATGATGTTGAA +CAACTGCGCAATTGGAAGCAGTTTTTAGCAGATAAGTTTGCCAATATGAGATGCTATACTGAAAAAGTATACTTGGTGGA +GCAATAGTTTTACTGTGATGTTGAGGGAAATATGATGATTTAGCGTATTGATAGCGAAAATATAATAAAACAATATAGTG +TGGAGAACTTTTGATATTTTATAAATATTGAAGTTCTCCATTTTTGTATTTTGCATATAAAAATTAAATAAAATAAGGTA +TATTAAGGTAAAGTATAAATTTTAAATAAATGGGGAATGAGTATGAGCTCAATTATAGGAAAAATAGCAATTTGGATAGG +CATCGTAGCTCAAATATATTTTAGTGTCGTTTTTGTTAGGATGATATCTATTAATATTGCTGGAGGATCTGATTACGAAA +CAATTTTTTTATTAGGATTAATATTGGCTCTTTTCACTGTTTTACCAACCATCTTTACTGCGATTTATATGGAAAGTTAC +TCTGTAATCGGAGGTGCACTTTTTATTGTTTATGCTATTATTGCACTGTGTTTATATAATTTCCTTTCGTCAATTTTATG +GCTGATTGGTGGTATTTTGCTGATTTGGAATAAATACTCAAAAGATGAATCGACAGACGAAAATGAAAAAGTTGATATTG +AAAGTACAGAGAATCAATTTGAATCTAAAGATAAAATCACTAAAGAATAAAGAGAATATTTAAGGTAAAGTATAAATTTT +AAATAAATGGGGAATAGACATGGAAAAAAATGTAGAAAAATCATTCATAAAGATAGGTTTATATTTTCAAATAGCTTATA +TAGTACTCATGGCTATAACTTTATGTGGGTTTGTAATTTGCTATGGACTAATTTTCGGCCTTTTCTATTTATTATCAGGT +AGCAGAGCTGATTATTTAATAGTAACAATAGTTATATCGGCAATAATTTCTATATTTGTAATTATACTTTCAATCGTACC +TGTCATCGTATTGGCATCTGACTTATTTAAAGAAAGGATTTCAAAAGGTGTCATATTAATTGTATTGGCTATTATCGCTT +TAGTATTATGCAACTTTGTATCTGCAATACTCTGGTTTGTTTCAGCCATATCTATTTTAGGTAGAAAAAAATTAGTAGCT +GCAGCAGATACTACCACTATTCAAAAAAGTAAAGGGAACGCAAATCAAGCATCACATAAAGACACGTGTAAAAAGGAACT +TGATAGTCAAGACATGATGGAACATCCTGAGGTTAAAAATCCCACGACTAAAAACCTTGAAGGATTTAACGAAGAAATAC +ATAAAGATGAAGCTACAACTAAAGTTGTCAGTGATAACACGGAACCGCCTATTGAATCAAAAGACCATGTCTCGAAAAAA +GATTGATGACAAACTAATCGAGAGACTTAAAAAAATAATATTCAACATAAGAACTTTTAAAACGACATTTAAACGCATTG +CCAATCACTAATGGTAGTGCGTTTAACTATACCTTAAATATCTGAATATTTTGTTAAATGGAGCTACCTTTGTTGTACTA +TTCAAATGAAGAGGAGTAAAATGTAATTAAAGGAAAGAAATTTGAGGAGTGATCTTTATGACAAACAACAAAGTAGCATT +AGTAACTGGCGGAGCACAAGGGATTGGTTTTAAAATTGCAGAACGTTTAGTGGAAGATGGTTTCAAAGTAGCAGTTGTTG +ATTTCAATGAAGAAGGGGCAAAAGCAGCTGCACTTAAATTATCAAGTGATGGTACAAAAGCTATTGCTATCAAAGCAGAT +GTATCAAACCGTGATGATGTATTTAACGCAGTAAGACAAACTGCCGCGCAATTTGGCGATTTCCATGTCATGGTTAACAA +TGCCGGCCTTGGACCAACAACACCAATCGATACAATTACTGAAGAACAGTTTAAAACAGTATATGGCGTGAACGTTGCAG +GTGTGCTATGGGGTATTCAAGCCGCACATGAACAATTTAAAAAATTCAATCATGGCGGTAAAATTATCAATGCAACATCT +CAAGCAGGCGTTGAGGGTAACCCAGGCTTGTCTTTATATTGCAGTACAAAATTCGCAGTGCGAGGTTTAACACAAGTAGC +CGCACAAGATTTAGCGTCTGAAGGTATTACTGTGAATGCATTCGCACCTGGTATCGTTCAAACACCAATGATGGAAAGTA +TCGCAGTGGCAACAGCCGAAGAAGCAGGTAAACCTGAAGCATGGGGTTGGGAACAATTTACAAGTCAGATTGCTTTGGGC +AGAGTTTCTCAACCAGAAGATGTTTCAAATGTAGTGAGCTTCTTAGCTGGTAAAGACTCTGATTACATTACTGGACAAAC +AATTATTGTAGATGGTGGTATGAGATTCCGTTAATAATCATCCACTAATGATAAATAAATCCTTATTGTTAAGTTTAATC +ACTTAGCAGTAAGGATTTTTTAGTGCACTTAGAAGGGAGTGTATTGGTAGAAAATTAATAAGCGAAGTTCTTAAGTGAGT +TATGATGTCACAGTCTAATGCATCAGTTGAAAGCATTATTAGTATTAACACACCCAAGATATTATAAAACATCACAAAAA +CACCACTATCTAATTTATCTCAATAAAAATTCACAAAGTTATCTCATTTTATTTTTATAAATAAAAAATATCGATAAAAA +GCTTACAATACTTTATGTTTTTATGATATATTTTTAATGTATAAATGAGGTGGAAGATTTGGAAAGAGTTTTGATAACTG +GTGGGGCTGGTTTTATTGGGTCGCATTTAGTAGATGATTTACAACAAGATTATGATGTTTATGTTCTAGATAACTATAGA +ACAGGTAAACGAGAAAATATTAAAAGTTTGGCTGACGATCATGTGTTTGAATTAGATATTCGTGAATATGATGCAGTTGA +ACAAATCATGAAGACATATCAATTTGATTATGTTATTCATTTAGCAGCATTAGTTAGTGTTGCTGAGTCGGTTGAGAAAC +CTATCTTATCTCAAGAAATAAACGTCGTAGCAACATTAAGATTGTTAGAAATCATTAAAAAATATAATAATCATATAAAA +CGTTTTATCTTTGCTTCGTCAGCAGCTGTTTATGGTGATCTTCCTGATTTGCCTAAAAGTGATCAATCATTAATCTTACC +ATTATCACCATATGCAATAGATAAATATTACGGCGAACGGACGACATTAAATTATTGTTCGTTATATAACATACCAACAG +CGGTTGTTAAATTTTTTAATGTATTTGGGCCAAGACAGGATCCTAAGTCACAATATTCAGGTGTGATTTCAAAGATGTTC +GATTCATTTGAGCATAACAAGCCATTTACATTTTTTGGTGACGGACTGCAAACTAGAGATTTTGTATATGTATATGATGT +TGTTCAATCTGTACGCTTAATTATGGAACACAAAGATGCAATTGGACACGGTTATAACATTGGTACAGGCACTTTTACTA +ATTTATTAGAGGTTTATCGTATTATTGGTGAATTATATGGAAAATCAGTCGAGCATGAATTTAAAGAAGCACGAAAAGGA +GATATTAAGCATTCTTATGCAGATATTTCTAACTTAAAGGCATTAGGATTTGTTCCTAAATATACAGTAGAAACAGGTTT +AAAGGATTACTTTAATTTTGAGGTAGATAATATTGAAGAAGTTACAGCTAAAGAAGTGGAAATGTCGTGAAAATGACATT +GAAGCTGTCCATAATAATAAGGGTTATGCCTATCAAAGAAAATTAGACAAACTAGAAGAAGTGAGAAAAAGCTATTACCC +AATTAAACGTGCGATTGACTTAATTTTAAGCATTGTTTTATTATTTTTAACTTTACCGATTATGGTTATATTCGCCATTG +CTATCGTCATAGATTCGCCAGGAAACCCTATTTATAGTCAGGTTAGAGTTGGGAAGATGGGTAAATTAATTAAAATATAC +AAATTACGTTCGATGTGCAAAAACGCAGAGAAAAACGGTGCGCAATGGGCTGATAAAGATGATGATCGTATAACAAATGT +CGGGAAGTTTATTCGTAAAACACGCATTGATGAATTACCACAACTAATTAATGTTGTTAAAGGGGAAATGAGTTTTATTG +GACCACGCCCGGAACGTCCGGAATTTGTAGAATTATTTAGTTCAGAAGTGATAGGTTTCGAGCAAAGATGTCTTGTTACA +CCAGGGTTAACAGGACTTGCGCAAATTCAAGGTGGATATGACTTAACACCGCAACAAAAACTGAAATATGACATGAAATA +TATACATAAAGGTAGTTTAATGATGGAACTATATATATCAATTAGAACATTGATGGTTGTTATTACAGGGGAAGGCTCAA +GGTAGTCTTAATTTACTTAATAAGTTCAAATAAAAGTTATATTTTAAAGATTGTGACCAATTGTTACAGTATAACGAGGA +ATCCCTTGAGACAGTATCAAATGGCATTAAGAAATATGTGCCATCATTGATTTGCATGGCTATAAATACTATTCATCTGA +TGAGATAGCCATGTTAAGAAATTGAAAGTATAGCATTAAAGGGGTTTGTAACAGTTGAAAATTATATATTGTATTACTAA +AGCAGACAATGGTGGTGCACAAACACATCTCATTCAACTCGCCAACCATTTTTGCGTACACAATGATGTTTATGTCATTG +TAGGCAATCATGGACCAATGATTGAACAACTAGATGCAAGAGTTAATGTAATTATTATCGAACATTTAGTAGGTCCAATT +GACTTTAAACAAGATATTTTAGCTGTCAAAGTGTTAGCACAGTTATTCTCGAAAATTAAACCTGATGTTATCCATTTACA +TTCTTCCAAAGCTGGAACGGTCGGACGAATTGCGAAGTTCATTTCGAAATCGAAAGACACACGTATAGTTTTTACTGCAC +ATGGATGGGCTTTTACAGAGGGTGTTAAACCAGCTAAAAAATTTCTATATTTAGTTATCGAAAAATTAATGTCACTTATT +ACAGATAGCATTATTTGTGTTTCAGATTTCGATAAACAGTTAGCGTTAAAATATCGATTTAATCGATTGAAATTAACCAC +AATACATAATGGTATTGCAGATGTTCCCGCTGTTAAGCAAACGCTAAAAAGCCAATCACATAACAATATTGGCGAAGTAG +TTGGAATGTTGCCTAATAAACAAGATTTACAGATTAATGCCCCGACAAAGCATCAATTTGTTATGATTGCAAGATTTGCT +TATCCAAAATTGCCACAAAATCTAATCGCGGCAATAGAGATATTGAAATTACATAACAGTAATCATGCGCATTTTACATT +TATAGGCGATGGACCTACATTAAATGATTGTCAGCAACAAGTTGTACAAGCTGGGTTAGAAAATGATGTCACATTTTTGG +GCAATGTCATTAATGCGAGTCATTTATTATCACAATACGATACGTTTATTTTAATAAGTAAGCATGAAGGTTTGCCAATT +AGCATTATAGAAGCTATGGCTACAGGTTTGCCTGTTATAGCCAGTCATGTTGGCGGTATTTCAGAATTAGTAGCTGATAA +TGGTATATGTATGATGAACAACCAACCCGAAACTATTGCTAAAGTCCTGGAAAAATATTTAATAGACAGTGATTACATCA +AAATGAGTAATCAATCTAGAAAACGTTATTTAGAATGTTTTACTGAGGAGAAAATGATTAAAGAAGTGGAAGACGTTTAT +AATGGAAAATCAACACAATAGTAAATTACTAACATTGTTACTTATCGGTTTAGCGGTTTTTATTCAGCAATCTTCGGTTA +TTGCCGGTGTGAATGTTTCTATAGCTGACTTTATCACATTACTAATATTAGTTTATTTACTGTTTTTCGCTAACCATTTA +TTAAAGGCAAATCATTTTTTACAGTTTTTCATTATTTTGTATACATATCGTATGATTATTACGCTTTGTTTGCTATTTTT +TGATGATTTGATATTTATTACGGTTAAGGAAGTTCTTGCATCTACAGTTAAATATGCATTTGTAGTCATTTATTTCTATT +TAGGGATGATCATCTTTAAGTTAGGTAATAGCAAAAAAGTGATCGTTACCTCTTATATTATAAGCAGTGTGACTATAGGT +CTATTTTGTATTATAGCTGGTTTGAACAAGTCCCCTTTACTAATGAAATTGTTATATTTTGATGAAATACGTTCAAAAGG +ATTAATGAATGACCCTAACTATTTCGCGATGACACAGATTATTACATTGGTACTTGCTTACAAGTATATTCATAATTACA +TATTCAAGGTCCTTGCATGTGGTATTTTGCTATGGTCTTTAACTACAACGGGGTCTAAGACTGCGTTTATCATATTAATC +GTCTTAGCCATTTATTTCTTTATTAAAAAGTTATTTAGTAGAAATGCGGTAAGTGTTGTGAGTATGTCAGTGATTATGCT +GATATTACTTTGTTTTACCTTTTATAATATCAACTACTATTTATTCCAATTAAGCGACCTTGATGCCTTACCGTCATTAG +ATCGAATGGCGTCTATTTTTGAAGAGGGCTTTGCATCATTAAATGATAGTGGGTCTGAGCGAAGTGTTGTATGGATAAAT +GCCATTTCAGTAATTAAATATACACTAGGTTTTGGTGTCGGATTAGTGGATTATGTACATATTGGCTCGCAAATTAATGG +TATTTTACTTGTTGCCCATAATACATATTTGCAGATCTTTGCGGAATGGGGCATTTTATTCGGTGCATTATTTATCATAT +TTATGCTTTATTTACTGTTTGAATTATTTAGATTTAACATTTCTGGGAAAAATGTAACAGCAATTGTTGTAATGTTGACG +ATGCTGATTTACTTTTTAACAGTATCATTTAATAACTCAAGATATGTCGCTTTTATTTTAGGAATTATCGTCTTTATTGT +TCAATATGAAAAGATGGAAAGGGATCGTAATGAAGAGTGATTCACTAAAAGAAAATATTATTTATCAAGGGCTATACCAA +TTGATTAGAACGATGACACCACTGATTACAATACCCATTATTTCACGTGCATTTGGTCCCAGTGGTGTGGGTATTGTTTC +ATTTTCTTTCAATATCGTGCAATACTTTTTGATGATTGCAAGTGTTGGCGTTCAGTTATATTTTAATAGAGTTATCGCGA +AGTCCGTTAACGACAAACGGCAATTGTCACAGCAGTTTTGGGATATCTTTGTCAGTAAATTATTTTTAGCGTTAACAGTT +TTTGCGATGTATATGGTCGTAATTACTATATTTATTGATGATTACTATCTTATTTTCCTACTACAAGGAATCTATATTAT +AGGTGCAGCACTCGATATTTCATGGTTTTATGCTGGAACTGAAAAGTTTAAAATTCCTAGCCTCAGTAATATTGTTGCGT +CTGGTATTGTATTAAGTGTAGTTGTTATTTTTGTCAAAGATCAATCAGATTTATCATTGTATGTATTTACTATTGCTATT +GTGACGGTATTAAACCAATTACCTTTGTTTATCTATTTAAAACGATACATTAGCTTTGTTTCGGTTAATTGGATACACGT +CTGGCAATTGTTTCGTTCGTCATTAGCATACTTATTACCAAATGGACAGCTCAACTTATATACTAGTATTTCTTGCGTTG +TTCTTGGTTTAGTAGGTACATACCAACAAGTTGGTATCTTTTCTAACGCATTTAATATTTTAACGGTCGCAATCATAATG +ATTAATACATTTGATCTTGTAATGATTCCGCGTATTACCAAAATGTCTATCCAGCAATCACATAGTTTAACTAAAACGTT +AGCTAATAATATGAATATTCAATTGATATTAACAATACCTATGGTCTTTGGTTTAATTGCAATTATGCCATCATTTTATT +TATGGTTCTTTGGTGAGGAATTCGCATCAACTGTCCCATTGATGACCATTTTAGCGATACTTGTATTAATCATTCCTTTA +AATATGTTGATAAGCAGGCAATATTTATTAATAGTGAATAAAATAAGATTATATAATGCGTCAATTACTATTGGTGCAGT +GATAAACCTAGTATTATGTATTATTTTGATATATTTTTATGGAATTTACGGTGCTGCTATTGCGCGTTTAATTACAGAGT +TTTTCTTGCTCATTTGGCGATTTATTGATATTACTAAAATCAATGTGAAGTTGAATATTGTAAGTACGATTCAATGTGTC +ATTGCTGCTGTTATGATGTTTATTGTGCTTGGTGTGGTCAATCATTATTTGCCCCCTACAATGTACGCTACGCTGCTATT +AATTGCGATTGGTATAGTAGTTTATCTTTTATTAATGATGACTATGAAAAATCAATACGTATGGCAAATATTGAGGCATC +TTCGACATAAAACAATTTAAGTACCGGTAATGCTATACTTTAGAAAATTAAGATTAAGAAGAAAAGGCAATTTCTTATTG +AAAAATGGAAGTTGTCTTTTTTAATTCTCTTTAAAAGCGGGAAACAAAAGCAGTTAAATGCCTTTTTGCATTCAATATTA +AATATTATATCAATTTCGAATATTTAAATTTTATATAATTGGATATAACAAATAAATAATAATTATTGCAAAACACACCC +AAAATTAATTATTATAAAAGTATATTCATAAAAGGAGGAATATACTTATGGCATTTAAATTACCAAATTTACCATATGCA +TATGATGCATTGGAACCATATATAGATCAAAGAACAATGGAGTTTCATCACGACAAACATCACAATACGTACGTGACGAA +ATTAAACGCAACAGTTGAAGGAACAGAGTTAGAGCATCAATCACTAGCGGATATGATTGCTAACTTAGACAAGGTACCGG +AAGCGATGAGGATGTCAGTCCGTAATAATGGCGGTGGTCATTTTAACCATTCATTATTCTGGGAAATACTATCACCTAAT +TCTGAAGAAAAAGGTGGCGTAATAGATGACATCAAAGCGCAGTGGGGCACTTTAGATGAATTTAAAAATGAATTTGCAAA +TAAAGCAACAACATTATTTGGATCAGGTTGGACTTGGTTAGTTGTTAATGATGGCAAATTAGAAATTGTGACAACGCCAA +ACCAAGATAATCCATTAACAGAAGGCAAAACACCAATCTTACTATTTGATGTTTGGGAGCATGCCTACTATCTGAAATAT +CAAAATAAACGTCCAGACTATATGACTGCATTTTGGAACATTGTTAACTGGAAAAAAGTTGATGAATTATACCAAGCAGC +AAAATAATATAACTTAATATAATTGAGGTGGAGCATCTACAAGGTGTTCCACTTTTTTGTACTTAATATCTTTACTTATT +TGTTTTTGTGTAATGGGATAGCACGTAAAAGTGGAAAAATAATAAGATAAATTTCAACTCGAAATACCTTATTATTGTTG +ACTATAACGATGACCTAAATATTATATACATTAGACAACACTATTAATTAATCAACATATTGACATTAAATAAATTGACA +AAATAAGTAATTATTGTGAATCTAAAGTGAAATTTTTATAAAAAAATGTAATGATTCAAAAATTTTGTTGCATTTCTTTT +GTAATCGTATGATAATGTAAATGTAATCAAATTGTAATATAAGGGGACAAGACAATGAAAAAATTAGCAACAGTAGGTTC +TTTAATTGTAACAAGCACTTTAGTATTCTCAAGTATGCCTTTTCAAAATGCGCATGCCGACACAACTTCAATGAATGTGT +CGAATAAACAAAGCCAAAATGTACAAAATCATCGTCCTTATGGCGGAGTAGTACCACAAGGAATGACGCAAGCACAATAT +ACTGAATTAGAGAAAGCTTTACCCCAATTAAGCGCTGGCAGTAATATGCAAGACTATAATATGAAATTGTATGATGCGAC +GCAAAATATTGCTGATAAATACAATGTGATAATTACAACTAATGTAGGGGTATTTAAACCACATGCTGTTAGAGATATGA +ATGGCCATGCGTTACCTTTAACAAAAGATGGCAATTTTTATCAAACGAATGTAGATGCAAATGGTGTTAATCATGGTGGT +AGTGAAATGGTGCAAAATAAAACAGGTCATATGAGTCAACAAGGCCATATGAATCAGAACACACATGAACCAACAGCCAC +ACATGCAACAAGGTCATATGCAATCATCAAACCATCAAATGATGAGTCCAAAAGCAAATATGCATTCATCAAATCATCAA +ATGAACCAAAGTAACAAAAAAGTTTTACCAGCTGCTGGTGAAAGTATGACATCAAGTATTCTTACTGCAAGTATTGCCGC +ACTACTATTAGTATCTGGGTTATTCTTAGCATTTAGACGACGTTCAACAAATAAATAAACATAATACGATTAATAATAGA +AAAATCGTGTGATTATCTGAGCGAGCCTAGGACATAAATCAATGTCCTAGGCTCGCTAATGTTATATTGGCAGTAGTTGA +CTGAATGAAATTGCGCTTGTAACAAGCTTTTCCATTTCTTACCAACTCCCTAAATAGTCATCAAAAAATTTCTTATATTT +TAATAATTTTTAATAATCCGATTGTCTTATACGTGTCAGTGTTAATTCAGATATTTCCTGTGGAATATACCACTTATTAA +TCATAATTGGATAAGGTGTTTGTGCGTACAGTGTTTCAATAATCAGCCAACAATGTGTATCACCATCAAACACGTGACTA +TGATTTTTGAAGTGGGGCGCTTTGGTAATAGACATTTTTAAATCTGATTGATATGCATTGCTATAAATCGTTTGCTCAAC +GAATGTCTTCATGTCGTCTTCGTTTTGTGTATTCACTTTAAATGTGTCAATGACATTTAACGGTATAAAGGTAAAGCAAA +ATGCATCAGCTTGCTTAGAATGATTGTCCTTTTTTTGATAATAGCGTTCCATTGCAATGACGGCAGAAGGATGGTTTGCA +AACAAATGATTTGTATATTCACTTTCTAAATCAACACGATAATTAATTGATGACATAGATACGCGAGCTAGCAATATTTG +ATCAAGTGGATGCTTAAATTGATCCATACTTGAAGCGTGTTGGGCATTTGTTTGTGGAATAACAAAGTGTCCCTTCCCTC +TTGTACTCTCTACGATGCCATCTTCGGCTAACAATTTTATAGCTTGGCGCAAAGTCATACGACTGACATCAAAGCGCGCA +CAAAGTTCCTTTTCAGTAGGTAATGCATGGCCACTCGGATATTTTCCTATTTGAATTTCTTTATATAACGTATTATAAAT +CGTTAAAAATTTTGGTTGTGTTTGCGTCACGTAGACAACCTCCATAAAGTTACTTAATCACTCTCATCATACAATAATTT +TTACTCAAATTGGAAAAATTATAAAAATTAAATATAGATAGGCTTTGAAAATTAGTTTTATACAAGGTTAGTAGCTGTAA +CTGTAAAATGTTCTTAATATTGTCAAAATGTAATGCTTGAAAGCGCTTTTAAAAAATATTATTATATACATGGTTAGACA +AATAGACAAATCACTATACAAATATTGGGAGGAATATTTTATGAAATCAACACCACACATTAAACCAATGAATGACGTCG +AAATTGCAGAAACGGTTCTATTGCCAGGAGATCCGTTAAGAGCTAAGTTCATTGCAGAAACTTATTTGGATGATGTGGAA +CAGTTCAATACAGTGCGAAACATGTTTGGTTTTACCGGAACATATAAAGGTAAAAAAGTTTCTGTCATGGGTTCAGGTAT +GGGTATGCCATCTATTGGCATTTACTCTTATGAATTAATTCATACATTTGGTTGTAAAAAATTAATTCGCGTTGGCTCTT +GTGGCGCGATGCAAGAAAACATTGATTTATATGATGTGATTATTGCACAAGGTGCCTCTACTGATTCAAATTACGTTCAA +CAATATCAATTACCAGGTCATTTTGCGCCAATTGCTTCTTATCAATTATTAGAAAAAGCAGTTGAAACAGCACGTGACAA +AGGTGTACGTCATCATGTAGGTAATGTGTTATCAAGTGATATTTTCTATAACGCGGATACAACAGCGAGTGAACGTTGGA +TGCGTATGGGTATTTTAGGTGTAGAAATGGAATCAGCTGCATTATACATGAATGCAATTTACGCTGGTGTCGAAGCATTA +GGTGTGTTCACAGTGAGCGATCATTTAATTCATGAAACGTCAACAACACCTGAGGAAAGGGAACGTGCATTTACAGATAT +GATTGAAATTGCACTGTCATTGGTGTAGATGATTATGAATGTTGAATATTCTAAAATAAAGAAAGCAGTACCTATTTTAT +TATTCTTATTTGTATTCAGTTTGGTTATAGACAACTCATTTAAATTGATTTCTGTAGCCATTGCTGATGACTTAAACATA +TCTGTAACGACAGTAAGTTGGCAAGCGACATTAGCCGGTTTAGTAATTGGTATTGGCGCTGTAGTATACGCTTCATTATC +TGATGCCATTAGTATACGCACACTATTTATTTATGGCGTGATATTAATCATTATCGGATCAATTATTGGTTACATTTTCC +AACATCAATTCCCATTACTTTTAGTTGGACGTATTATTCAAACTGCCGGTTTAGCTGCTGCAGAGACATTATATGTGATA +TATGTTGCAAAGTATCTTTCTAAAGAGGACCAGAAGACTTACCTTGGCTTAAGTACGAGCAGTTATTCCTTGTCATTAGT +TATCGGTACATTATCAGGTGGATTTATTTCTACGTATTTACACTGGACAAATATGTTTTTAATTGCATTAATCGTAGTAT +TTACGTTGCCATTCCTATTTAAATTATTACCAAAAGAAAATAATACGAATAAAGCTCATTTAGATTTTGTTGGCTTAATT +CTAGTGGCAACTATTGCTACAACAGTCATGCTGTTTATTACGAACTTTAATTGGTTATATATGATTGGTGCCTTAATTGC +GATTATCGTTTTTGCGCTATATATTAAAAATGCGCAACGTCCATTAGTAAATAAATCATTTTTCCAAAATAAACGTTATG +CTTCATTTTTATTTATAGTATTTGTAATGTATGCTATCCAATTGGGTTATATTTTTACGTTCCCATTCATAATGGAGCAA +ATTTATCATCTGCAACTAGACACAACATCACTGTTATTAGTACCGGGTTATATAGTAGCAGTCATTGTTGGTGCACTAAG +TGGTAAAATCGGCGAATATCTGAATTCAAAACAAGCGATTATCACAGCAATTATTTTAATAGCACTGAGCTTGATTTTAC +CTGCATTTGCAGTAGGTAATCACATTTCAATCTTCGTCATTTCTATGATATTCTTTGCAGGTAGCTTTGCTTTAATGTAT +GCACCTTTACTTAACGAAGCCATTAAAACAATAGATCTTAATATGACAGGTGTGGCTATTGGTTTTTATAATTTAATTAT +TAATGTGGCGGTATCTGTAGGTATTGCGATTGCTGCGGCTCTAATCGATTTTAAAGCATTAAATTTCCCAGGCAATGATG +CATTAAGTTCACATTTCGGTATTATTTTAATTATTTTAGGTTTAATGAGTATTGTCGGATTAGTTTTATTCGTCAGCTTA +AATCGTTGGACACAATCTGAAAAATAAATAGATATAAATTCGCGAGATATATTCGTATTTATAGTAAAATTAAATAAAGA +GATTATATAACACGAGGAGTAGTAAGTATGAAATTTGAGAAATATATAGATCACACTTTATTGAAGCCTGAGTCAACACG +TACGCAAATCGATCAAATCATCGATGAAGCGAAAGCATACAATTTTAAATCTGTATGTGTGAATCCAACACATGTTAAAT +ATGCAGCAGAGCGACTAGCTGATTCAGAGGTGCTCGTTTGTACGGTAATAGGATTCCCATTAGGTGCGTCGACAACTGCA +ACGAAAGCATTTGAAACAGAAGATGCAATTCAAAATGGTGCAGATGAAATTGACATGGTCATCAACATCGGCGCATTAAA +AGATGGACGTTTTGATGATGTACAACAAGACATTGAAGCAGTGGTTAAAGCTGCGAAAGGTCACACAGTAAAAGTGATTA +TTGAGACGGTATTGTTGGACCATGACGAAATTGTAAAAGCGAGTGAATTAACAAAAGCGGCTGGTGCGGACTTCGTTAAA +ACTTCAACAGGTTTTGCAGGTGGCGGTGCGACTGCAGAAGACGTTAAATTAATGAAAGATACAGTAGGTGCTGATGTAGA +AGTAAAAGCATCAGGTGGCGTACGTAATTTAGAAGATTTCAATAAAATGGTTGAAGCAGGTGCGACACGTATTGGTGCGA +GCGCAGGTGTTCAAATTATGCAAGGTTTAGAAGCAGATTCAGATTACTAATATATATAAATTTTGGGAGTGATAGCTATG +ACAAGACCATTTAATCGTGTACATTTAATCGTAATGGATTCAGTAGGTATTGGTGAAGCGCCAGACGCAGCTGATTTTAA +AGATGAAGGTTCACATACTTTAAGACATACCTTAGAAGGTTTCGATCAAACTTTACCAAACCTTGAAAAGTTAGGTCTAG +GGAACATCGATAAATTACCAGTAGTAAATGCAGTTGAACAACCAGAAGCATACTATACTAAATTGAGTGAAGCTTCAGTT +GGTAAAGATACAATGACTGGTCACTGGGAAATTATGGGATTAAATATTATGCAACCTTTTAAAGTATACCCTAATGGATT +CCCTGAAGAGTTAATTCAACAAATTGAAGAAATGACAGGTCGTAAAGTTGTTGCTAACAAACCGGCATCGGGTACGCAAA +TTATCGATGAGTGGGGCGAGCACCAAATGAAAACTGGTGACTTAATTGTTTATACAAGTGCAGACCCAGTATTGCAAATT +GCTGCACATGAAGACATTATCCCATTAGAAGAGTTATATGATATTTGTGAAAAGGTTCGTGAGTTGACAAAAGACCCTAA +ATATTTAATTGGTCGTATTATCGCACGTCCATATGTTGGTGAACCAGGAAACTTTACACGTACATCTAATCGACATGACT +ATGCGTTAAAACCTTTTGGTAAAACTGTCTTAGATCATTTGAAAGACGGTGGTTATGATGTTATTGCCATCGGTAAAATT +AATGACATTTATGATGGTGAAGGTGTAACAGAAGCGGTTCGTACGAAGAGTAACATGGACGGTATGGATCAATTGATGAA +AATTGTTAAGAAAGATTTCACAGGTATTAGCTTCTTAAACTTAGTAGACTTTGATGCATTATACGGTCATCGTCGTGATA +AACCAGGTTATGCACAAGCAATTAAAGATTTCGATGATCGCTTGCCAGAACTGTTTAGCAACTTAAAAGAAGACGATTTA +GTAATTATTACAGCAGACCATGGTAATGACCCGACAGCGCCAGGTACGGACCATACGAGAGAATATATCCCAGTAATTAT +GTACAGTCCGAAATTTAAAGGTGGTCATGCACTAGAAAGTGATACTACATTCAGTTCTATCGGTGCAACTATAGCAGATA +ATTTCAACGTAACATTACCAGAGTTCGGTAAAAGTTATTTAAAGGAATTGAAATAGAATAAATTTAGATATTATAAAAAC +AGCAGTGAAGTTAACTATAACAATAGTTTTCTTCACTGCTGTTTTTATTATAATAGAGAAACGTAAGACGGTAGGACTTC +TTATTTAGGAGTATCCTGATTTAATGTTAAACAATACGTTTTCGGATTGAACCGGAAATTAAATCGACAATTGCGACCAT +TAGTACTAAACCGATTAATATAATACCTACACGGTCCCAAGAACGTGTTTGAATGGCAAATATGAGTGGTGTCCCGATAC +CACCAGCCCCAATTAGCCCCAGTATAGAAGCTGAACGTAAGTTTAGTTCAAAGCGATAAAGTATGAGTGATAGAAAGGCA +GGCATAATTTGTGGTATGACTGCAAATACGAGTGTTTTAATCTTATTCGCACCACTGGCCTTTAATGATTCTACAGCACT +GAAATCTAGACCTTCAATATCTTCAGCTAAAAGTTTCCCAAGCATACCTACGGAATGGATACCTAAAGCTAATACACCTG +AAAATGAACCTGGGCCAACAGCTTTGATAAATATAAGTGCCATTACAATTTCTGGGAAGACACGTATAACACTTAAAATA +AATTTGCTAACACCTGAAACTGGGCGTAGCTTTACCATATTATTTGCACCTAGAAATGCTAATGGAATACAGATAATTGC +GGCGATGAAAGTACCTACAACGGCTATCGCAAAGGTTTCAAGTAAACCACGTAATAAGTCTTCGCCATCTGGTATATAGA +TATAGCTGATATCAGGATGGAATAATCCGCTGAATATGGATTTTAAGATTTCTAATGATTTACTTTTAAGTTCTAAACTT +GGTACACCTGCAAATGCCCAGATGATAATAGCTAAGACGACAATTGCAATAAGCCATCTTTTAATCAATTTTCGTTTGTG +TGCTTTTGTGTGAACATTATATTTTGCTATTTCCTGTGTCATGCGAGATGTGCCCTCACTTTCGTACTGATGTAATCAAT +GACGACGACGATAACTAAAGTAAATAAAATAATCGTTGCTGTTTTTGGATATTGAAATAAACCAAGTGTTTGATCATAAA +ACAATCCAATACCGCCAGCGCCGACTAATCCAAGCACAGCTGAAGCACGTATATTTACTTCAAATGCATATAATACGTAT +GACATAAATGACGATATGGCTTGTGGTACAACACCGAAAACAATCCATTTTATTTTATTAGCGCCAACAGCCGTCATTGC +TTCCATTGGACCTGGATCTATCGTTTCCAATGATTCATATAATAATTTTCCAATAATACAGATAGTTAAAATAAACAGTG +CTAATATCCCTGGAATTTGACCGATTCCAAATACAGCCACAAAGATTGCTGCTAATAACAAATCTGGAATAGTACGAACT +ATATTTAAAATAAAGCGCGAGGGTATTGAAATCCACTTTTGATGAACGATATTGCTAGCACATAATAACGCAATTGGTAT +TGAAACGATGCTACCTAATACTGTACTTACGATAGCCATTCGAATGGTATCTAACATTGGCGTTGTAATTTGTTGTAAAT +ACTCGAAATCAGGTGGAATCATTTGTTTGAATAGATCACCTATTTGAGGTATTCCTATCATTAAATCTCCAAAATTAAAT +CCCGTATAAATGAAGCTCCAAATGATAAGCACAATGATTAACATGAAGGTAAAACTCGTTTTTAAAGAAACCTTTTTCTT +TAAAAGGGAGTCATACTTTGTAGGTATTTCTAAAGGCATGTTAGTTCACTCCTAGCTTTTCATCTTCTTTAATTGTACGT +CCATATATTTCACTAAATACGTCATCTGTTGCTTCAGATGCAGGACCATCATAGACAACTTCACCATCACGTAAACCAAT +GATGCGTGTGCCATATTCTTTTGCCAAGTCAACAAAATGTAAATTAATTAAAATTGTGATGCCTAATTCTTGGTTGATTT +TTCTTAAATCATCCATAACCTGTTTCGTAGTTAATGGGTCTAATGAAGCAACTGGTTCATCTGCAAGAATAATTTCAGAT +TCTTGGCATAGCGCACGTGCAATAGATATACGTTGTTGTTGGCCACCTGATAATTCATCAGAGCGTTGATTATATTTATC +TAAGATATTGACGCGTTCTAGTGCATCCATTGCCTTAATTTTGTCTTCTTTTGGGAATAAACCTAATACCATTTTCCAAG +TAGGGTGATAACCTACACGTCCACTTAGTACATTTCGTAATACACTTGACCGTTTAACTAAATTAAAATGTTGGAAAATC +ATACCTATATTTCGGCGCATTTCTAATAATGCTTTACCATGGGCTTTAGTGATTGATTTACCTTGGATGAAAATTTCACC +TGACGTGATATCATGCAAACGATTTACAGATCTTAATAACGTGGATTTCCCAGCACCAGATAGTCCGACAATAACTGCAA +ATTCACCTTTTTCAATATTTAAGTTAATATTTTTCAAGCCTACATGACCGTTAGGATAGACTTTACTGACGTTTTTAAAT +TCGATTTGACTCATGTTTGACACCTTTCTTTAATAAAGAAAAGCCAAGGTAAACTGAAATATCATAGACATTGGGTTTCT +GTGTACAAATGGTTTCAAATGTACAGCGACACCTTTCTATATCTGTTTCAGTTTAGCGCCTAGCTTTTCAATATTAATCT +AGAATATATCTATTGAACGAAAGCTTTTAATACCAAATTCGCTAATGATTCATTTGTTAAATAATGATTATTTCATATCT +TTAACTAATTTTTCGTACTCTCTTACAATGTCGAAATTTGAATCTTTCGTTTCTGTGTATCCTTCATGTGAATAAACTTC +GCTAATAATTTTGTGACCTTCTTTTGATTTAGCAATGTCTATAAAAGCTTTTTTCAATTTTTCTTGAAAATCTTTATCCA +TATCTGGTCTTACAGAAATTGTGTCATTCGGAATAGCTTGTGTTAATTTTAAAATTCGTGTGTCTTTAAATACATTTGGT +TGGTCTTTTTTCACAGTATTACGTGCATCGTTAAATACAGCCGCAGCATCTACATCTCCATTTAATAATGAGATAACTGC +TTGGTCATGACCTTTAACATTCACAATTTTCATATCTTTAGTTGCATTAATACCTGCTTCGTTTTTTAACATCGCAAGTG +GGAATGTATATCCAGCAGTTGATGTTACATCTTGTAAGGCAATTTTCTTACCTTTTAAATCTTTCAAGCTTTTAATTTTT +GAGTCTTTTTTAACAAGAATTTCTGATTTATAACTATCTACAAGTTCTTTACTTGCTGAACCATCTTCTTTTACACCGAA +ACGTTGTGCTTGTAATAATAAATCAGCTGCTTTTTGATCATGTGCTAATGTGTATGCCGTTGGTGGTAAGAAACCAACAT +CAACTTTTTTAGACTTCATAGCTTCAACAATTGTATTGTAGTTAGTTGATACAGACACTTTAACTGGAATCCCTAATTCT +TTAGATAGTAATTTTTCTAATGGTTTTGCTTTAGCTTCTAATGTTCCAGCATTTTGCGAAGGTACAAATTGAACGGTTAA +TTCTTTAGGTTTGTATCCTCCTGATTTAGAATCCGAATCATTACTAGCGTTCTTTTGATTATCTAAAGAACTTGAGTTTC +CACATGCTGCTGCAAAAACAATGACTGCTAACATTAATACAAATAAACACTTAAAATTTTTCATTTGATAACTGTCCCCT +TCACATTCTTAATCGTGTAATATGTATGCAATGAAATTACACTTATAGCGTACCACATTTAAATGTTAAAAAGTATTTTA +TAAAAATGAATTTTAATAACTTTCTTTAAGAGTTTGATAAGATTTTGTAAATTTTTCGTTAAATTGCTCATATGCAACGA +ATTTTTATCCTATAATTCAAGAACAAATTTATTTTAAAAGGAGCCTCACAAAATGAAAAAAATATATAAGTCATTAACTG +TCTCTGCAATTGTTGCAACGGTATCATTAAGTGCTTTACCGCAATCTTTAGCTATAACGCATGAATCGCAACCTACAAAG +CAACAGCGAACGGTATTATTCGATCGTTCTCATGGTCAAACAGCTGGTGCTGCAGATTGGGTTAGTGATGGTGCATTTTC +AGATTATGCGGATTCAATACAAAAACAAGGTTATGACGTTAAAGCTATTGATGGTCATTCGAACATAACAGAAGCAAGTT +TGAAAAGTTCCAAAATATTTGTAATTCCTGAGGCTAACATTCCTTTCAAAGAATCAGAACAGGCAGCAATTGTTAAATAT +GTGAAACAAGGTGGCAATGTTGTCTTTATTTCAGATCATTACAATGCTGACCGAAATTTAAATCGTATTGATTCATCGGA +GGCAATGAATGGTTATCGACGTGGAGCATATGAAGATATGTCGAAAGGTATGAATGCAGAAGAAAAAAGTTCTACTGCAA +TGCAAGGTGTGAAAAGTTCAGATTGGTTATCTACAAACTTTGGCGTACGTTTTCGATATAATGCACTAGGTGATTTAAAT +ACGAGCAATATTGTTTCTTCAAAAGAAAGTTTCGGTATTACTGAAGGTGTGAAATCTGTCTCTATGCATGCCGGATCGAC +ATTAGCAATTACTAATCCAGAGAAAGCAAAAGGTATTGTGTATACACCAGAACAATTGCCAGCGAAAAGTAAATGGTCAC +ATGCTGTAGATCAAGGTATTTATAATGGGGGCGGTAAAGCAGAAGGCCCCTATGTAGCAATTTCTAAAGTTGGAAAAGGT +AAAGCAGCATTTATCGGTGATTCATCACTTGTGGAAGATAGTTCGCCCAAATATGTAAGAGAAGATAATGGAGAAAAGAA +GAAAACATATGATGGTTTTAAAGAACAAGACAACGGTAAGCTATTAAATAATATAACGGCTTGGATGTCTAAAGATAATG +ATGGGAAATCACTTAAGGCGAGTAGCCTAACATTAGATACAAAGACTAAGTTGCTTGATTTTGAACGACCAGAGCGTTCA +ACTGAGCCTGAAAAAGAGCCATGGTCACAACCGCCGAGTGGTTATAAATGGTATGATCCAACAACATTTAAAGCAGGTAG +TTATGGCAGCGAAAAAGGCGCAGATCCTCAGCCAAACACACCAGATGATCATACACCACCAAATCAGAACGAAAAAGTAA +CATTTGATATCCCGCAAAATGTTTCTGTAAATGAGCCATTTGAAATGACAATACATTTAAAAGGATTTGAAGCAAATCAA +ACACTTGAAAATCTTAGAGTTGGTATTTACAAAGAAGGCGGACGTCAAATCGGACAATTTTCAAGTAAAGATAACGATTA +TAACCCACCAGGTTACAGTACTTTGCCAACAGTTAAAGCAGATGAAAACGGAAATGTCACAATTAAGGTCAATGCTAAAG +TACTTGAAAGTATGGAAGGTTCAAAGATTCGTTTAAAACTCGGTGACAAAACCTTGATTACAACAGACTTCAAATAAATA +TATAATAGAAATAAGAAAGATGTTTGTGATTGAAGGAGTGAGTGAGGATGTCAAACATAGCATTTTATGTCGTGAGTGAC +GTACATGGTTATATTTTCCCAACAGATTTTACGAGTAGAAATCAATATCAACCTATGGGATTGTTACTAGCGAATCATGT +TATAGAACAAGACAGAAGGCAGTATGACCAAAGTTTTAAAATAGATAATGGTGATTTTTTGCAAGGGTCACCATTTTGTA +ATTACTTAATCGCGCATAGCGGCAGTAGCCAGCCTTTAGTTGATTTTTATAATCGAATGGCATTCGACTTTGGTACGCTT +GGTAATCATGAATTTAATTATGGATTACCATACTTAAAAGACACTTTACGCAGACTCAATTATCCAGTTTTGTGCGCTAA +TATATATGAAAATGATAGTACATTGACTGATAACGGTGTGAAGTATTTTCAGGTTGGAGATCAAACTGTTGGTGTGATAG +GTTTAACGACACAATTTATTCCCCATTGGGAACAACCAGAGCATATTCAGTCACTTACGTTTCATAGTGCTTTTGAAATA +CTTCAACAATACTTACCTGAAATGAAGCGACATGCAGATATCATTGTGGTTTGTTACCATGGTGGATTTGAAAAGGATTT +AGAAAGTGGTACGCCGACCGAAGTATTAACGGGTGAAAATGAAGGATATGCCATGTTAGAAGCGTTTTCTAAAGATATAG +ATATCTTTATTACGGGTCACCAACATCGACAAATTGCTGAAAGGTTTAAGCAAACGGCTGTGATTCAACCTGGTACGAGA +GGTACAACTGTAGGCAGAGTAGTCTTGAGTACTGATGAATATGAAAATCTATCCGTTGAATCATGTGAATTACTTCCTGT +TATAGATGATTCCACATTTACTATTGATGAAGATGACCAACATTTACGAAAGCAGTTAGAGGACTGGTTAGATTACGAAA +TTACTACATTGCCATATGATATGACGATTAATCATGCATTTGAGGCACGTGTGGCACCGCATCCTTTTACAAATTTTATG +AATTACGCTTTATTAGAAAAAAGTGACGCAGATGTTGCCTGTACAGCTTTGTTTGATTCTGCTAGTGGTTTCAAGCAAGT +CGTGACGATGCGAGATGTTATTAACAATTACCCATTTCCAAATACATTTAAAGTTTTAGCTGTAAGTGGTGCCAAACTTA +AAGAAGCCATTGAACGATCAGCAGAATATTTTGACGTGAAAAATGATGAAGTTAGTGTGAGCGCAGACTTCCTTGAACCC +AAACCACAACACTTTAATTATGATATATATGGTGGCGTAAGTTATACCATTCATGTTGGAAGACCAAAGGGACAACGTGT +GAGCAATATGATGATTCAAGGTCACGCAGTTGATTTAAAGCAGACATATACAATTTGTGTAAATAATTATCGTGCAGTAG +GCGGTGGTCAGTATGATATGTATATCGACGCGCCAGTTGTAAAAGATATTCAAGTTGAAGGCGCACAATTACTTATTGAT +TTTTTATCAAATAATAATTTGATGCGCATCCCGCAAGTTGTTGATTTTAAAGTTGAAAAGTGACGGATATATGTAAAACT +TACTTGATTTTAATCGGGTAAGTTTTTGTTTTTGTTTAAAGCGAGAAAGTATTATCTTTAGACGACGTGATTGAAATGTC +ACGTCCTGTCAAAATGAATAGTTTATTAAAGTTGTATTTTGATAGAAATTTACTGACACCACAAAAATTATTGAATTATT +TAAAAGTTGATGAAACATTTTTGAATCATCTAGCAGGTATTAATTTAAAACTCTTTAAGGATTATGTTAATGAAAATAGA +GAATATAATATAACGAATCTATATAAATAAGGCACCAATATTTAAACCTCGAGCTGAAAAGATCATCATTTCAGTTCGAG +GTTTTTTATTTTATCCTCTTTGTATGGGTATTCTTTTACTTGAAATTTGTTTTCCTGTTTTTTTATTGTAAATTTTTATT +ACATAGTATCCTAAGTTATATCCTGACATTTGTTCTCCAGTATTTTTAGTTTTTTAGTAGGAACATAATGGTATTTTTTT +GCTGTTAGTGGAACATATTTTTTAGTGTTTTGAATTTCGTTTCATCCATTTGTAAAACTGGATACTCTAATATCCGGTAA +TAGCCGGTTAAATCGACATAGGATGTCACTAGCTATTCCAAAATTGTCTTTTGACGCTATAACGATTGTTGGGAATCTTA +GCATGTCTAACGCAGAGAGACTTTCACATTTTATGAGTACTAATCCTGAAATTCGGCTTTGGGATATTTTACAAACAAAC +TTTAAAGCTAAAGCTCTTAAAGAAAAAGTTTATATTGAATATGACAAAATAAAAGCAACTCTTTGGAATAGACGTAGTAT +GCGCGTTGAATTTAATCCTAATAAGCTTTCGCATGATGAAGTGCTTTGGTTAAAACAAAATATCATCAGTTATTTGGACG +ATGTTAGTTTTACGAGATTAGATTTGGCTTTTGATTTTGAATTTGATTTAAATGACTATTATGCATTGTCAGATAAGTCG +GTAAAGAAAACTATATTTTATGGACGTAATGTAAAACCAGAAACAAAATATTTTGGTGTGCGTAATAGTGATAGGTTTAT +TCGGATTTATAATAAAAACAAGAACGTAAAGATAATGCAGATGTTGAAATTGATTCAACATTTCTATGGCGTGTGGAAAT +TGAATTAAAACGAGATATGGTTGATTGTTGGAAAGATTGTTTTGATGATTTGCATATTTTAAAACCGAATTTAAAAATGA +TTGAAAATATACAAGAACGAGCAATGCTTCATTTATTAACTCACGAAGAAGAGGAATGGGGAAATTTAGAAAGACGTACT +AAAAATAAATATAGAGATAAGTTGAAAAATATAGCGTCTATTGATTTGACAGATTTAATGAAAATATCTTTAAGAGGAAA +TGAAAACCAATTGCAAAAACAAATCGACTTTTGGTTGAATTAATTATTATAACTAAAGAACTATGTTCACTTATGCTCAT +CTACTTAAAATAAGTATTTTTTTGTTGGTTTTTAAAAGTAGTTGAAATGTTTATAATAATATTGAAAGATATTTCTGTTT +TCCTTTTGAATAATAAACTTAAAAGAGAATATAAAATAATGCTTATTAAAATTAGGGGTGGAAATATGAAAAGAGATTTT +TTTGAAAAGTGGTCGTTTATTTTAAATTTAATTTTATTTTGTGCTTTTTTGTTTTTAGTGGTTCTGATGTTTTATAAGGA +TGTCGATTTGATTTATATTTTTTGTACATTATTAATATCATTATTACCTTTGCTAAATATTATAAAAAGATATAAGAAAA +AGACGGATAATTAATCCGTCTTTTTTTTATGAAAAGAATAGGCTGGGAAATAAATCAATGTTCTATGATCTACGAAGTTA +TATTGGCAGTAATTGACTGAACGAAAATGCGCTTGTAATAAGCTTTTTTCAATTCTAGTCAGGGGCCCCAACACAGAGAA +TTTCGAAAAGAAATTCTACAGGCAATGCGAGTTGGGGCGGGGCCCCAACAAAGAGAAATTGGATTCCCAATTTCTACAGA +CAATGCAAGTTGGGGTGGGACGGCGAAATAAATTTTGCGAAAATATCATTTCTGTCCCACTCCCCAGAAAAGGTTCTACC +ATTGTCAAAAAAATGCATCTCTACATGTTAGAGTAAATATTGGTCAGCCAACCAAAATAATCAACACGAGGAGATGCTAT +TTAATGTCATCTGACACAAACAGTTTAGCACATACGAAATGGAATTGTAAGTAACATATTGTCTTTGCACCTAAATACAG +AAGACAAGCGATATATGGAAAAATAAAAAAAGATATAGGGATTATATTACGTCAATTATATGAAAGAAAAGGTGTAGAGA +TAATTGAAGCAGAGGTATGTAAAGATCATATCCATATGTTAGTAAGTATACCACCCAAACTTGGGGTATCATCATTTGTT +GGCTATTTAAAAGGAAAAGTAATTTAATGATATTTGATAGACATGCTAACTTAAAGTATAGATATGGAAATAGAAAGTTT +TGGTGTAAAGGATTTTATGTGGATACAGTAGGTAGAAATAAAAAAGTGATTGAAAATTATATTCGTAATCAATTACAAGA +GGATATCGTTGCAGATGAAATTTCAATGGAAGAATATTTAGATCCTTTCACTGGAGAGAAAAATAAAAAAAGAAAGAAAA +AAGAGTAAACCTTTAGGATTGCTGGAATAGTAGTGCAGTTGGCTGACTTGTCAGTGCCCTTTTAGGGCTGGCCAGTAAGG +AAGGCTTATAGCCGCAGAACAAACCACCCGTTCACACGGGTGGTTTTTATTTGTAACATCTGTTGATAGGTATCGACAAG +GACCTTTTAATGTTGTGTTTTGTGCGATATAACAAGCTTTTTAGGCTATTAAAGAATATTCAGAATTAAATATATAATTA +TGAATAAATTATGTCATTGAAAAAATTATGATTATAAAATCATTCTAAAAACACCGAAATAACAATGATTTCATGAAAAC +ATTTATTTTAAAATTTGATATTTGTTCAAATAATATTCGAAATTAAACTTTTTTGTATAGAATTTTCTTTATATCCTGAG +AGACATGTACTATAATGTTTGTGAAATAATTCACAAAGTATAAAGGAGTGGTTGTATATGTTAACTATACCTGAAAAAGA +AAATCGTGGATCGAAAGAACAAGAAGTGGCAATTATGATTGATGCTCTAGCTGACAAAGGGAAAAAAGCATTAGAAGCAT +TATCTAAAAAGTCACAAGAAGAAATTGATCATATTGTTCATCAAATGAGCTTAGCAGCTGTTGATCAACATATGGTGCTA +GCAAAATTAGCACATGAAGAAACTGGAAGAGGTATATACGAAGATAAAGCGATTAAAAATTTATACGCTTCTGAATATAT +ATGGAATTCAATAAAAGACAATAAGACAGTAGGGATTATTGGTGAAGATAAAGAAAAAGGATTAACGTATGTAGCGGAAC +CAATTGGTGTTATTTGTGGTGTTACGCCAACAACAAATCCTACGTCGACAACTATTTTTAAAGCGATGATTGCAATTAAG +ACAGGAAATCCAATCATTTTTGCATTCCATCCAAGTGCACAAGAATCGTCGAAGCGTGCAGCAGAAGTTGTATTAGAAGC +GGCAATGAAGGCAGGTGCACCTAAAGATATTATTCAGTGGATTGAAGTGCCTTCTATCGAAGCAACAAAACAATTAATGA +ATCACAAAGGTATTGCATTAGTTCTAGCAACAGGTGGTTCGGGCATGGTTAAGTCTGCATATTCAACTGGCAAACCGGCA +TTAGGTGTGGGACCAGGTAACGTGCCGTCTTACATTGAAAAAACAGCACACATTAAACGTGCAGTAAATGATATCATTGG +TTCAAAAACATTTGATAATGGTATGATTTGTGCTTCTGAACAAGTTGTAGTCATTGATAAAGAAATTTATAAAGATGTTA +CTAATGAATTTAAAGCACATCAAGCATACTTTGTTAAAAAAGATGAATTACAACGCTTAGAAAATGCAATTATGAATGAA +CAAAAAACAGGTATTAAGCCTGATATTGTCGGTAAATCTGCAGTTGAAATAGCTGAATTAGCAGGTATACCTGTCCCCGA +AAATACAAAACTTATCATAGCCGAAATTAGCGGTGTAGGTTCAGACTATCCGTTATCTCGTGAAAAATTATCTCCAGTAT +TAGCCTTAGTAAAAGCCCAATCTACAAAACAAGCATTTCAAATTTGTGAAGACACACTACATTTTGGTGGATTAGGACAC +ACAGCCGTTATCCATACAGAAGATGAAACATTACAAAAAGATTTTGGACTAAGAATGAAAGCTTGTCGTGTACTTGTAAA +TACACCATCAGCGGTTGGAGGTATTGGTGATATGTATAACGAATTGATTCCGTCTTTAACATTAGGTTGTGGTTCCTACG +GTAGAAACTCAATTTCACATAATGTTAGTGCGACAGATTTATTAAACATTAAAACGATTGCTAAACGACGTAATAATACT +CAAATTTTCAAGGTGCCTGCTCAAATTTATTTTGAAGAAAATGCAATCATGAGTCTAACAACAATGGACAAGATTGAAAA +AGTGATGATTGTCTGTGACCCTGGTATGGTAGAATTCGGTTATACAAAAACAGTTGAGAATGTATTAAGACAAAAAACGG +AACAGCCTCAAATAAAAATATTTAGCGAAGTCGAACCGAACCCATCAACTAATACAGTATATAAAGGTCTGGAAATGATG +GTTGATTTCCAACCGGATACAATCATTGCACTTGGTGGTGGTTCAGCGATGGATGCTGCAAAAGCAATGTGGATGTTCTT +TGAACACCCTGAGACATCATTCTTCGGTGCTAAACAAAAGTTCCTAGACATCGGTAAACGTACTTATAAAATAGGCATGC +CTGAAAATGCGACGTTCATTTGTATCCCTACGACATCAGGTACAGGTTCAGAAGTAACACCATTTGCAGTTATCACAGAT +AGTGAAACAAATGTAAAATATCCGTTGGCTGATTTTGCTTTAACACCTGACGTTGCAATTATTGACCCTCAATTTGTGAT +GAGTGTGCCAAAAAGCGTTACAGCAGATACAGGAATGGATGTACTAACGCATGCAATGGAATCATATGTATCTGTAATGG +CTTCAGACTACACAAGAGGTTTGAGTCTACAAGCGATTAAATTGACGTTCGAATATTTAAAATCATCTGTTGAAAAGGGT +GATAAAGTTTCAAGAGAGAAAATGCATAACGCATCAACTTTGGCTGGTATGGCATTTGCAAATGCATTCTTAGGCATTGC +ACACTCAATTGCGCATAAAATTGGTGGCGAATATGGTATTCCGCATGGTAGAGCGAATGCGATATTACTACCGCATATTA +TCCGTTATAATGCCAAAGACCCGCAAAAACATGCATTATTCCCTAAATATGAGTTCTTCAGAGCAGATACAGATTATGCA +GATATTGCCAAATTCTTAGGATTAAAAGGTAATACGACAGAAGCACTCGTAGAATCATTAGCTAAAGCTGTCTACGAATT +AGGTCAATCAGTCGGAATTGAAATGAATTTGAAATCACAAGGTGTGTCTGAAGAAGAATTAAATGAGTCAATTGATAGAA +TGGCAGAGCTCGCATTTGAAGATCAATGTACAACTGCTAATCCTAAAGAAGCACTAATCAGTGAAATCAAAGATATCATT +CAAACATCATATGATTATAAGCAATAATCTATCTGATAATAATCATCTAACTCACCTGAAATTACAAAAGTAAAAAATGC +CACATAAACTTTAAGTCGATAATCATTATACGGTTATCGGCTTTTATTTATTGCCAAATCTTCAGAGAGATACAAACTAG +ACAATCATTTTTTTAAATAAAGAAAATATTAAGATTGATACTCATTTCACAAACTATTACTACTTTAGAGTATAATTATT +TTTAATTTCATATAAATAAAAAGGCGAAAATAATGCGGTTTAAAAGTAATTAATTGTTTAAACGATATGTAATATGTAAA +TACTATATATATAATACCAATTTTAATGAAAATTTTTAAGGGAGGTAAATAATGGAAAGTACATTAGAATTAACAAAAAT +TAAAGAAGTATTACAAAAAAACTTGAAGATTTTAATTATTTTACCGCTATTATTTTTAATTATTAGCGCTATTGTTACAT +TTTTCGTCTTATCACCTAAATATCAAGCTAATACTCAAATTTTAGTGAATCAAACTAAGGGTGACAATCCTCAGTTTATG +GCGCAAGAGGTTCAAAGTAATATTCAACTTGTAAATACGTATAAAGAAATTGTTAAAAGTCCTAGAATTTTAGATGAGGT +GTCAAAGGACTTAAATGATAAGTATTCACCATCTAAATTGTCGAGTATGTTGACAATTACAAACCAAGAAAATACGCAAC +TTATCAACATCCAAGTTAAAAGTGGTCATAAACAAGATTCGGAAAAAATTGCGAATAGCTTCGCTAAAGTTACAAGTAAA +CAAATTCCGAAGATTATGAGTGTGGATAACGTATCAATTTTATCTAAAGCAGACGGTACAGCAGTTAAAGTCGCACCAAA +AACTGTAGTGAATCTAATCGGTGCATTCTTTTTAGGATTAGTTGTCGCGCTTATATATATCTTCTTCAAAGTAATTTTCG +ATAAGCGAATTAAAGATGAAGAAGATGTAGAGAAAGAATTAGGATTGCCTGTATTGGGTTCAATTCAAAAATTTAATTAA +GGATGGTTGCTACTTATGTCAAAAAAGGAAAATACGACAACAACACTATTTGTATATGAAAAACCAAAATCAACAATTAG +TGAAAAGTTTCGAGGTATACGTTCAAACATCATGTTTTCAAAAGCAAATGGTGAAGTAAAGCGCTTATTGGTTACTTCTG +AAAAGCCTGGTGCAGGTAAAAGTACAGTTGTATCGAATGTAGCGATTACTTATGCACAAGCAGGCTATAAGACATTAGTT +ATTGATGGCGATATGCGTAAGCCAACACAAAACTATATTTTTAATGAGCAAAATAATAATGGACTATCAAGCTTAATCAT +TGGTCGAACGACTATGTCAGAAGCAATTACGTCGACAGAAATTGAAAATTTAGATTTGCTAACAGCTGGCCCTGTACCTC +CAAATCCATCTGAGTTAATTGGGTCTGAAAGGTTCAAAGAATTAGTTGATCTGTTTAATAAACGTTACGACATTATTATT +GTCGATACACCGCCAGTTAATACTGTGACTGATGCACAACTATATGCGCGTGCTATTAAAGATAGTCTGTTAGTAATTGA +TAGTGAAAAAAATGATAAAAATGAAGTTAAAAAAGCAAAAGCACTTATGGAAAAAGCAGGCAGTAACATTCTAGGTGTCA +TTTTGAACAAGACAAAGGTCGATAAATCTTCTAGTTATTATCACTATTATGGAGATGAATAAGTATGATTGATATTCATA +ACCATATATTGCCTAATATCGATGACGGTCCGACAAATGAAACAGAGATGATGGATCTTTTAAAACAAGCGACAACACAA +GGTGTTACAGAAATCATTGTAACATCACATCACTTACATCCTCGATATACCACACCTATAGAAAAAGTGAAATCATGTTT +AAACCATATTGAAAGCTTAGAGGAAGTACAAGCACTAAATCTAAAGTTTTATTATGGTCAGGAAATAAGAATTACCGATC +AAATCCTTAATGATATTGATCGAAAAGTTATTAACGGTATTAATGATTCACGCTATTTACTAATAGAATTTCCATCAAAT +GAAGTTCCACACTATACTGATCAATTATTTTTCGAATTACAGAGTAAAGGCTTTGTACCGATTATTGCACATCCAGAGCG +GAATAAAGCAATAAGTCAAAACCTTGACATACTATACGATTTAATTAACAAAGGTGCTTTAAGTCAAGTGACAACGGCGT +CATTAGCGGGTATTTCCGGTAAAAAAATTAGAAAATTAGCAATTCAAATGATTGAAAACAATCTGACACATTTCATCGGT +TCAGATGCGCATAACACAGAAATCAGACCGTTCTTAATGAAAGACTTATTTAATGATAAGAAATTACGTGATTATTATGA +AGATATGAACGGATTTATTAGTAATGCGAAGTTAGTTGTTGATGATAAAAAAATTCCTAAACGAATGCCACAACAAGATT +ATAAACAGAAAAGATGGTTTGGGTTATAAACAGCAAATGAGGGGTTTTATGGCACATTTATCTGTGAAATTGCGGCTTTT +AATACTAGCATTAATCGATTCACTGATAGTGACATTTTCAGTATTCGTAAGTTATTACATTTTAGAACCGTATTTCAAAA +CATATTCTGTCAAATTATTAATATTGGCAGCTATATCACTATTCATATCGCATCATATTTCAGCATTTATTTTTAATATG +TATCATCGAGCGTGGGAATATGCCAGTGTGAGTGAATTGATTTTAATTGTTAAAGCTGTGACGACATCTATCGTTATTAC +GATGGTGGTCGTGACAATTGTTACAGGCAATAGACCGTTTTTTAGATTGTATTTAATTACTTGGATGATGCACTTGATTT +TAATAGGTGGCTCAAGGTTATTTTGGCGTATTTATCGGAAATACCTTGGAGGTAAGTCATTTAATAAGAAGCCAACTTTA +GTTGTTGGTGCTGGTCAAGCAGGTTCAATGCTGATTAGACAAATGTTGAAAAGTGACGAAATGAAACTTGAACCGGTATT +AGCAGTCGATGATGACGAACATAAACGCAATATCACAATTACTGAGGGTGTAAAAGTCCAAGGTAAAATTGCGGATATTC +CAGAACTAGTGAGGAAATATAAGATTAAAAAAATCATCATTGCAATTCCAACTATTGGTCAAGAGCGTTTGAAAGAAATT +AATAATATTTGCCATATGGATGGCGTTGAGTTATTGAAAATGCCAAATATAGAAGACGTCATGTCTGGTGAGTTAGAAGT +GAACCAACTTAAAAAAGTTGAAGTAGAAGATTTACTAGGCAGAGATCCTGTTGAATTAGATATGGATATGATATCAAATG +AATTGACGAATAAAACTATTTTAGTTACGGGTGCAGGTGGTTCAATAGGATCAGAAATTTGTAGACAAGTTTGTAATTTC +TATCCAGAACGTATTATTCTACTTGGCCATGGTGAAAACAGTATTTATTTAATCAATCGTGAATTGCGAAATCGCTTCGG +AAAAAATGTTGATATCGTTCCTATTATAGCGGATGTGCAAAATAGAGCGCGTATGTTTGAAATTATGGAAACGTATAAAC +CATACGCAGTTTATCATGCAGCAGCACACAAGCACGTGCCGTTAATGGAAGACAACCCTGAAGAAGCAGTACGTAATAAT +ATTTTAGGTACGAAAAATACTGCTGAAGCTGCTAAAAATGCAGAGGTAAAGAAATTCGTTATGATTTCTACGGATAAAGC +CGTTAATCCGCCTAATGTCATGGGAGCTTCAAAGCGAATTGCAGAAATGATTATTCAAAGTTTAAATGATGAAACGCATC +GAACAAATTTTGTTGCAGTGAGATTTGGTAATGTACTTGGATCGAGAGGATCTGTGATTCCACTTTTCAAAAGTCAAATT +GAAGAAGGTGGGCCAGTTACTGTGACACATCCTGAAATGACACGTTACTTTATGACAATTCCTGAAGCTTCTAGACTAGT +TTTGCAGGCAGGGGCATTAGCAGAAGGTGGCGAAGTATTTGTGCTAGATATGGGAGAACCAGTGAAAATTGTAGATTTGG +CACGTAATTTAATTAAGCTAAGTGGTAAAAAAGAAGACGACATACGCATTACTTATACAGGGATTAGACCCGGCGAAAAA +ATGTTTGAAGAGCTTATGAATAAAGATGAGGTTCATCCTGAACAAGTATTTGAAAAAATTTATCGTGGCAAAGTACAACA +TATGAAATGTAATGAAGTTGAAGCGATTATTCAAGACATCGTCAATGACTTTAGTAAAGAAAAAATTATTAACTATGCCA +ATGGCAAAAAGGGAGATAATTATGTTCGATGACAAAATTTTATTAATTACTGGGGGCACAGGATCATTCGGTAATGCTGT +TATGAAACAGTTTTTAGATTCTAATATTAAAGAAATTCGTATTTTTTCACGCGATGAGAAAAAACAAGATGACATTCGAA +AAAAATATAATAATTCAAAATTAAAGTTCTACATTGGTGATGTGCGTGATAGTCAAAGTGTAGAAACAGCAATGCGAGAT +GTTGATTACGTATTCCATGCAGCAGCTTTAAAACAAGTGCCGTCATGTGAATTCTTTCCAGTTGAGGCAGTGAAGACAAA +TATTATTGGTACAGAAAATGTCTTACAAAGTGCTATTCATCAAAATGTTAAAAAAGTCATATGTTTATCTACAGATAAGG +CAGCGTATCCTATTAATGCTAGGGGTATTTCAAAAGCAATGATGGAAAAAGTATTCGTAGCCAAATCAAGAAATATTCGT +AGTGAACAAACGCTTATTTGTGGTACAAGATACGGTAATGTGATGGCTTCAAGAGGATCAGTAATACCTTTGTTTATCGA +CAAAATCAAAGCTGGAGAACCTTTAACGATTACAGATCCTGATATGACAAGATTTTTAATGAGCTTAGAAGATGCGGTAG +AACTAGTTGTTCATGCATTTAAGCATGCAGAGACAGGAGATATTATGGTTCAAAAAGCACCAAGCTCAACGGTAGGGGAT +CTTGCGACCGCATTATTAGAATTGTTTGAAGCTGATAATGCAATTGAAATCATTGGTACGCGACATGGAGAGAAAAAAGC +AGAAACATTGTTGACGAGAGAAGAATACGCACAATGTGAAGATATGGGTGATTATTTTAGAGTGCCGGCAGACTCCAGAG +ATTTAAATTATAGTAATTATGTTGAAACCGGTAACGAAAAGATTACGCAATCTTATGAATATAACTCCGATAATACACAT +ATTTTAACGGTGGAAGAGATAAAAGAAAAACTTTTAACACTAGAATATGTTAGAAACGAATTGAATGATTATAAAGCTTC +AATGAGATAGGAGAGATTGACGTTGAATATTGTAATTACAGGAGCAAAAGGTTTTGTAGGAAAAAACTTGAAAGCAGATT +TAACTTCAACGACAGATCATCATATTTTCGAAGTACATCGACAAACTAAAGAGGAAGAATTAGAGTCAGCATTGTTGAAA +GCAGACTTTGTCGTGCATTTAGCGGGTGTTAATCGACCTGAACATGACAAAGAATTCAGCTTAGGAAACGTGAGTTATTT +AGATCATGTACTTGATATATTAACTAGAAATACGAAAAAGCCAGCGATATTATTATCGTCTTCAATACAAGCAACACAAG +ATAATCCTTATGGTGAGAGTAAGTTGCAAGGGGAACAGCTATTAAGAGAGTATGCCGAAGAGTATGGCAATACGGTTTAT +ATTTATCGCTGGCCAAATTTATTCGGCAAGTGGTGTAAGCCGAATTATAACTCAGTGATAGCAACATTTTGTTACAAAAT +TGCACGTAACGAAGAGATTCAAGTTAATGATCGGAATGTTGAACTAACGCTAAACTACGTGGATGATATCGTCGCTGAAA +TAAAGCGTGCTATTGAAGGAACTCCAACGATTGAAAATGGTGTACCTACAGTACCAAACGTATTTAAAGTGACATTGGGA +GAAATTGTAGATTTATTATACAAGTTCAAACAGTCACGTCTCGATCGAACATTGCCGAAATTAGATAACTTGTTTGAAAA +AGATTTGTATAGTACGTATTTAAGCTATCTACCTAGTACAGACTTTAGTTATCCCTTACTTATGAATGTGGATGATAGGG +GTTCTTTTACAGAATTTATAAAAACACCGGATCGTGGTCAAGTTTCTGTAAATATTTCTAAACCAGGTATTACTAAAGGT +AATCACTGGCATCATACTAAAAACGAAAAATTTCTAGTCGTATCAGGTAAAGGGGTAATTCGTTTTAGACATGTTAATGA +TGATGAAATCATTGAATATTATGTTTCTGGCGACAAATTAGAAGTTGTAGACATACCAGTAGGATACACACATAATATTG +AAAATTTAGGCGACACAGATATGGTAACTATTATGTGGGTGAATGAAATGTTTGATCCAAATCAGCCAGATACGTATTTC +TTGGAGGTATAGCGCATGGAAAAACTGAAATTAATGACAATAGTTGGTACAAGGCCTGAAATCATTCGTTTATCATCAAC +GATTAAAGCATGTGATCAATATTTTAATCAGATATTAGTACACACTGGTCAAAATTATGATTATACATTGAATCAAATTT +TCTTTGATGATTTGGAATTAAGACAACCGGACCACTACTTAGAGGCAGTTGGAAGTAACCTTGGAGAAACGATGGGGAAT +ATTATTGCGAAGACATATGATGTTTTATTACGCGAACAACCAGATGCACTTTTAATTCTTGGTGATACAAATAGTTGTTT +AGCAGCAGTATCTGCTAAACGATTAAAGATTCCTGTGTTCCACATGGAAGCGGGTAATAGATGCTTTGATCAGAATGTAC +CTGAAGAAATCAATCGTAAAATTGTTGACCATGTCAGTGATGTGAATCTACCTTATACGGAACATAGCAGACGTTATTTA +TTAGATGAAGGCTTCAATAAAGCGAATATCTTTGTGACAGGATCACCGATGACAGAAGTGATAGAAGCGCATCGAGATAA +AATTAATCACAGTGACGTTTTAAATAAACTAGGATTAGAACCGCAACAATACATTTTAGTATCTGCGCATAGAGAAGAGA +ATATCGATAATGAAAAGAATTTTAAATCATTAATGAATGCGATAAATGATATTGCCAAAAAGTATAAAATGCCTGTGATT +TATTCAACGCATCCAAGAAGTTGGAAGAAAATTGAAGAAAGTAAATTTGAATTTGATCCATTAGTTAAACAGTTAAAGCC +ATTTGGTTTCTTTGATTATAATGCATTGCAAAAAGATGCATTTGTTGTGCTATCAGATAGTGGAACATTGTCAGAAGAGT +CGTCTATTTTGAAGTTCCCTGGTGTCCTTATTCGAACTTCCACAGAAAGACCGGAAGTACTAGATAAAGGTACGGTTATT +GTAGGTGGTATTACCTATAACAATCTAATCCAATCCGTTGAACTAGCAAGAGAGATGCAAAACAATAACGAACCGATGAT +TGATGCTATTGATTATAAAGACACTAACGTTTCGACAAAGGTAGTTAAAATTATTCAAAGCTATAAAGATATTATCAATC +GAAATACTTGGAGGAAATGACGATGAGGATAGCGATTGAAAAGATAATTGGTTTGCTGAAAAACCAGTCCTCTAAAGAAT +CGAATGTTAAGATTCATCGCTTGGCGTATATTACAAACTCAAAATTTGATGGCAATAACTATATAGATAGATGGTGTAAA +ATCAGGAATTCTCACATTGGTGAATACAGTTATATTGGATTTGGTAGTGATTTTAATAATGTAGAAGTAGGAAGATATTG +TTCGATATCTTCGGATGTAAAAATTGGGTTAGGAAAACATCCTACACACTTTTTTAGCTCATCACCGATTTTTTATTCTA +ATAATAATCCATTTAACATAAAGCAAAAGTTTATAGACTTTAATGACCAACCAAGCCGTACAACAATTAAAAATGATGTG +TGGATTGGTGCAAATGTAATTATTATGGATGGTTTAACAATAAATACTGGTGCAGTCATAGCAGCCGGCTCAGTTGTTAC +TAAAAATGTAGGAGCATATGAGGTTGTTGGTGGTGTTCCTGCAAAAGTGATTAAGAAGCGATTTGACAATAAAACAATTG +AAAAACTTTTGGAAAGCAAGTGGTGGGAGAAAACGCCTGACAAACTAAAAGGATTTTCGGTTGAATATTTAAATAAAAAG +GATACTTAATGATATGAGAATTTTAAATATTGTATCGAGTAATATTGTTCAAGACCCAAGGGTACTTAAACAAATAGAAA +CAATTAAAGGCGTTACGGATGATTATAAAATTGTTGGAATGAATAATTCACAAGCTACTAATAAGCGATTGGAAAATTTA +GATTGTAATTATCGTTTGTTAGGTAGCAAGGTAGATCCAAAAAATATTCTTTCTAAATTAATTAAGCGTATAAGATTTGC +AACAGGTGTTATCCGAGAAATTAAAGCTTATAAACCTGACGTGATTCATGCAAATGATTTCGACGTATTATTAATGGTCT +ATTTAAGCAATTATAAAAAAGCTAATATTGTTTATGATGCGCATGAAATATATGCGAAAAATGCCTTTATTAATAAAGTT +CCACTTATTTCAAAGTTTGTAGAAAGTATAGAAAAACACATAGTAAAACATCGTGTTAATGCCTTCGTAACAGTAAGTCA +TGCAGCAAAAGAATATTATCAATCTAAAGGATATAAGAAGGAAGCGAATGTTATTACGAATGCACCTATTTTAAATGATA +GCAGAGAATTTAAAGAAATCGAAAACTTTAAAGAAATTGTATATCAAGGTCAAATTGTAATGGACAGAGGATATGAAGAG +TTTATTATTGCTTCATCAGCTTTTAAACAAAATGCTCCTTCATTCATAATTCGAGGGTTTGGTCCGCATGAAGAAGTGAT +AAAAGAACTGATTAGTTATAACCCGGAAAATATTAGGTTGGATAAACCAGTTGAAGTAAAAGAATTGGTTGATAAGTTAG +CAGAAAGTAATGTTGGTGTTGTCTTGACGAAACCAGTATCTATTAATTTTGAATATACAGTATCTAATAAAATTTTTGAA +TGTATACATGCTGGTTTACCAGTAATTTTATCTCCTGTCAAAGAGCATATTTATCTCAATGAAAAATATAAATTTGGCAT +TGTTCTAAAGGAAGTTACGCCGTTAGAAATTGAAAAGGCGGTTAGAAAATTAAGAGATAATCACGATTTGTTTAATCATT +TACGTCAAAATGCAATTAAGGCGTCTAAAATTTTGAATTGGCAAATAGAGAGTGAACGGTTAGTAGAATTATATAAATTT +TAAAGAGAGGTAGACTATGAAATTTTTTGTACTTTGTGCAATTATCAGCATGAACATATTTATAGTAATCTCTACATTTA +CTAAAGAAGTATTAGGGTTCCCTATAGAGCCGGTGTATTACTCAACCATGGTTGGTATAGCATTAATTACTACGGTGTTT +GCTATTTATAAGATAATTGTCACCCAAGAAATTCCGCGAGGGTTAATATTATTAATTGCTATATGTTTGCTTTATCTAGC +TTTTTATTATTTTTCACCAGATAAGGAAGAGAAACTAGCTAAAAATAATATTCTATTCTTTTTAACATGGGCAGTTCCAG +CGGCAATTAGTGGTATTTATATTAAATATATAAACAAGGCTACGGTAGAAAGATTTTTTAAATTAGTATTTTTCATATTT +TCTGTTTCATTTATTTTTGTAATTTTAATACCAAAACTTACAGGTGAGATACCTAGCTATATCAATTTTGGACTTATGAA +CTATCAAAACGCTTCGTACCTTTCAGCATTTACTGCCGGATTAGGCATTTATTTCATTATGAAAGGTTCAGTTAAACATA +AGTGGATATATGTTCTATTTACAATAATTGATATCCCTATTGTGTTTATACCAGGAGGGCGTGGAGGTGCTATTTTATTA +ATTCTTTACGGCTTATTTGCATTTATACTTATTACGTTTAAAAGAGGAATACCTATCGCAGTAAAAAGCATTATGTATAT +TTTTGCATTAAGCATATCTAGTGTATTGATTTACTTTCTTTTTACAAAAGGTTCGAATACTAGAACATTTTCATATCTAC +AAGGTGGAACACTTAATTTAGAAGGTACTTCTGGAAGAGGACCGATTTATGAAAAAGGTATTTACTTTATTCAACAAAGT +TCGTTATTAGGCTATGGGCCATTTAACTATTATAAACTAATCGGAAATATACCACATAACATCATTATTGAGTTGATTCT +ATCATTTGGCTTATTAGGGTTTTTTATCATAATGATTTGCATTTTGCTACTAGTTTATAAAATGATTAGGAACTATGATC +CAAACACTATAGATTTACTCGTTATGTTTATAGCAATCTATCCAATCACATTATTAATGTTTAGTTCAAATTATTTAGTT +GTAAGTGAATTTTGGTTTGTGTTGTTCTATTTTATTACAAAAGGACGGCGTCATCATGGCTAAGAAAGTTTTTATTATGG +ATAGCGTAAAGACAATAATTGGTACGTTGCTTATAGCTTTAGGATTACAATTTTTAGCTTATCCAATTATTAATCAACGA +GTAGGTAATGAAGCGTTCGGTTCTATTTTAACGATTTATACAATAATAACAATCACGAGTGTTGTATTAGGCAATACGCT +TAACAATATACGATTGATTAATATGAATCTATACAAATCCAATCATTACTACTGGAAATTTGCATCGATACTTTTAATCT +CAATTCTGATTGAGAGTATAGCTTTAATTATTGTATTTCTTTACTTTTTTAATTTGAACATCATCGATATTATCTTTTTA +ATTCTACTTAATATTTTAATGTGTTTAAGGATTTATCTGAATGTATTTTTTAGGATGACTTTAAAATATAATCAGATTTT +GTATATTGCTCTTATTCAATTTTTAGGTTTGCTGATAGGACTATTTCTATATTATTTAACCCAAAACTGGATTGTTTGTT +TTATTACCAGTGAATTGTTTGCAACGATATATACATTGGTTAAATTACGGGGATTAACTATAGGCGAGTATCAAAGTGAA +GATAATAATGTGGTAAAAGATTATGTGATGCTACTGAGTACAAATAGCCTTAATAATTTGAATCTCTATTTAGATAGATT +AATCTTATTACCAATTATAGGTGGAACAGCTGTAACTATATCATTTCTTTCAACATTTATTGGGAAAATGTTAGCTACAT +TTCTATATCCGATTAATAATGTAGTACTTTCATATATTTCTGTAAATGAAAGTGACAATATAAAGAAGCAATATTTGAAA +ACTAATCTAATTGCTATAGCTGCCCTATGTTTAGTCATGATTATATGTTATCCAATTACAATAATTATTGTCTCTTTACT +GTATAACATTGATTCAAGTTTATATTCGAAGTTTATTATTTTAGGTAATATAGGTGTTTTATTCAATGCAGTGAGTATTA +TGATCCAAACTTTAAATACAAAACACGCATCAATAACATTACAAGCGAATTATATGACGCTTCACACGATTACATTTATA +TTCATAACTATTTTAATGACAATTGCGTTTGGTCTAAATGGATTCTTTTGGACAACGCTGTTCAGCAACATTATTAAGTA +TGTGATTTTAAATATTATAGGTTTAAAGTCTAAATTCATTAATAAAAAGGACGTCGATTAGATGAGTGAAAAAAAGATTT +TGATTTTATGTCAGTATTTTTATCCGGAATATGTATCTTCTGCGACGTTACCAACTCAATTGGCGGAAGATTTAATTGCG +AATCACATTAATGTCGATGTCATGTGTGGATGGCCATATGAATATAGTAATCATAAACAGGTTTCTAAAACCGAGATGCA +TCGTGGTATTCGCATTCGACGTCTCAAGTATTCGAGGTTTAATAACAAAAGTAAGGTTGGAAGGATCATCAATTTCTTTA +GTTTATTTTCAAAATTCGTGATTAATATACCTAAAATGTTGAAATATGATCAGATTCTTGTTTACTCTAATCCACCAATC +TTGCCATTAATACCAGACGTTTTACACAGACTGCTTAAGAAAAAATATTCTTTTGTGGTGTATGATATAGCACCTGATAA +TGCGATTAAGACAGGTGCAACTCGTCCAGGTAGCATGATTGATAAGCTGATGCGTTACATTAATAGACATGTCTACAAGA +ATGCTGAAAATGTCATTGTCCTTGGTACGGAAATGAAAAACTACTTACTAAATCATCAAATTTCTAAAAATGCTGACAAT +ATCCATGTGATTCCTAACTGGTATGACATGCGTCAATTACAAGACAATCGTATCTATAATGACACATTTAAAGCTTACCG +TGAGCAATACGACAAAATTTTATTGTATAGCGGTAATATGGGGCAGTTACAGGATATGGAGACACTTATCTCATTTTTAA +AATTAAATAAGGATCAGTCTCAAACGTTAACAATACTTTGTGGTCATGGTAAGAAATTTGCAGATGTCAAAACGGCAATA +GAAGACCATCGTATTGAAAATGTTAAAATGTTTGAGTTTTTAACAGGTACAGACTATGCTGACGTATTAAAAATTGCGGA +TGTATGTATTGCATCGCTGATTAAAGAAGGCGTCGGTTTAGGCGTGCCGAGCAAGAATTATGGCTATCTTGCAGCTAAGA +AAGCGTTGGTACTCATCATGGATAAGCAATCTGATATCGTTCAACATGTTGAACAATATGATGCGGGTATCCAAATTGAT +AATGGCGATGCACATGCCATTTATAACTTCATCAACACTCACTCGAGTAAGGAATTGCACGAGATGGGTGAGCGCGCACA +TCAACTGTTTAAAGATAAATATACGAGAGAAATTAATACTATGAAGTATTACAATCTGTTGAAGTGAGGAGATAATTATG +AAGCGATTATTCGATGTAGTGAGTTCAATATATGGTTTAGTAGTTTTAAGTCCGATTCTGTTAATTACAGCATTACTAAT +TAAAATGGAATCACCTGGACCAGCCATTTTCAAACAAAAAAGACCGACGATTAATAATGAATTGTTTAATATTTATAAGT +TTAGATCAATGAAAATAGACACACCTAATGTTGCAACTGATTTAATGGATTCAACATCGTATATAACAAAGACAGGGAAG +GTCATTCGTAAGACCTCTATTGATGAATTGCCACAATTATTGAATGTTTTAAAAGGAGAAATGTCAATTGTAGGTCCTAG +ACCAGCGCTTTATAATCAATACGAATTAATCGAAAAACGTACAAAAGCGAACGTGCATACGATTAGACCAGGTGTGACAG +GACTAGCTCAAGTGATGGGGAGAGATGATATCACTGATGATCAAAAAGTAGCGTATGATCATTATTACTTAACACATCAA +TCTATGATGCTTGATATGTATATCATATATAAAACAATTAAAAATATCGTTACTTCAGAAGGTGTGCATCACTAATGAGA +AAAAATATTTTAATTACAGGCGTACATGGATATATCGGTAATGCTTTAAAAGATAAGCTTATTGAACAAGGACATCAAGT +AGATCAAATTAATGTTAGGAATCAATTATGGAAGTCGACCTCGTTCAAAGATTATGATGTTTTAATTCATACAGCAGCTT +TGGTTCACAACAATTCACCTCAAGCAAGGCTATCTGATTATATGCAAGTGAATATGTTGCTGACGAAACAATTGGCACAA +AAGGCTAAAGCTGAAGACGTTAAACAATTTATTTTTATGAGTACTATGGCAGTTTATGGAAAAGAAGGTCATGTTGGTAA +ATCAGATCAAGTTGATACACAAACACCAATGAACCCTACGACCAACTATGGTATTTCCAAAAAGTTCGCTGAACAAGCAT +TACAAGAATTGATTAGTGATTCGTTTAAAGTAGCAATTGTGAGACCACCAATGATTTATGGTGCACATTGCCCAGGAAAT +TTCCAACGGTTAATGCAATTGTCAAAGCGATTGCCAATCATTCCCAATATTAACAATCAGCGCAGTGCATTATATATTAA +ACATCTGACAGCATTTATTGATCAATTAATATCATTAGAAGTGACAGGTGTGTACCATCCTCAAGATAGTTTTTACTTTG +ATACATCGTCAGTAATGTATGAAATACGTCGCCAATCACATCGTAAAACGGTATTGATCAACATGCCTTCAATGCTAAAT +AAGTATTTTAATAAGTTGTCGGTCTTTAGAAAATTATTCGGCAATTTAATATACAGCAATACGTTATATGAAAATAATAA +TGCACTTGAAATTATTCCTGGAAAAATGTCACTTGTTATTGCGGACATCATGGATGAAACGACAACCAAAGATAAGGCAT +AAGTCATCTATTAAATAAAATCAACATACAAATCGTTTTATTTGGAGGTTATAGTATGAAGTTAACAGTAGTTGGCTTAG +GTTATATTGGTTTACCAACATCAATTATGTTTGCAAAACATGGCGTCGATGTGCTTGGTGTTGATATTAATCAGCAAACG +ATTGATAAGTTACAAAGTGGTCAAATTAGTATTGAAGAACCTGGATTACAAGAGGTTTATGAAGAGGTACTGTCATCGGG +AAAATTGAAGGTATCTACAACGCCAGATGCATCTGATGTTTTTATCATTGCCGTTCCGACGCCGAATAATGATGATCAGT +ACCGGTCATGTGACATTTCGCTAGTTATGCGTGCATTAGATAGTATTTTATCATTTTTAGAAAAAGGAAATACCATTATT +GTAGAGTCGACAATTGCGCCTAAAACGATGGATGATTTTGTAAAACCAGTCATTGAAAATTTAGGGTTTACAATAGGTGA +AGATATTTATTTAGTGCATTGTCCAGAACGTGTACTGCCAGGAAAAATTTTAGAAGAATTAGTTCATAACAATCGTATCA +TTGGCGGTGTGACTGAAGCTTGTATTGAAGCGGGTAAACGTGTCTATCGCACATTCGTTCAGGGAGAAATGATTGAAACA +GATGCACGTACTGCTGAAATGAGTAAGCTAATGGAAAACACATATAGAGACGTGAACATTGCTTTAGCTAATGAATTAAC +AAAAATTTGCAATAACTTAAATATTAATGTATTAGATGTGATTGAAATGGCAAACAAACATCCGCGTGTTAACATCCATC +AGCCTGGTCCAGGTGTAGGCGGTCATTGTTTAGCTGTTGATCCGTACTTTATTATTGCTAAAGACCCTGAAAATGCAAAG +TTAATTCAAACTGGACGTGAAATTAATAATTCAATGCCGGCCTATGTTGTTGATACAACGAAGCAAATCATCAAAGTGTT +GAGCGGGAATAAAGTCACAGTATTTGGTTTAACTTATAAAGGTGATGTTGATGATATAAGAGAATCACCAGCATTTGATA +TTTATGAGCTATTAAATCAAGAACCAGACATAGAAGTATGTGCTTATGATCCACATGTTGAATTAGATTTTGTGGAACAT +GATATGTCACATGCTGTCAAAGACGCATCGCTAGTATTGATTTTAAGTGACCACTCAGAATTTAAAAATTTATCGGACAG +TCATTTTGATAAAATGAAGCATAAAGTGATTTTTGATACAAAAAATGTTGTGAAATCATCATTTGAAGATGTATCGTATT +ATAATTATGGCAATATATTTAATTTTATCGACAAATAAAATGTGTCAAACTAGGGCATACATGATTAAGGAAAGATAAGC +TGTCATGTGTTTGAACTTCAGAGAGGATAATGTTATGAAAAAAATTATGGTTATTTTCGGTACGAGACCCGAAGCAATAA +AAATGGCACCATTAGTAAAAGAAATTGATCATAATGGGAACTTTGAAGCGAACATTGTGATTACAGCACAACATAGAGAT +ATGTTAGATAGTGTGTTAAGTATATTTGATATTCAAGCTGATCATGATTTAAATATTATGCAAGATCAACAAACATTAGC +AGGCCTTACGGCGAATGCACTTGCTAAACTTGATAGCATCATTAATGAGGAACAACCGGATATGATTTTAGTACATGGTG +ATACTACAACGACTTTTGTAGGAAGTTTGGCAGCATTTTATCATCAAATTCCGGTCGGACATGTAGAAGCTGGACTTCGA +ACACATCAGAAATACTCACCATTTCCTGAAGAGTTAAATCGAGTCATGGTAAGTAATATTGCTGAATTGAATTTTGCGCC +AACAGTAATTGCAGCTAAAAATTTACTTTTTGAAAACAAAGACAAAGAGCGTATCTTTATTACTGGAAATACAGTTATTG +ACGCATTGTCAACAACAGTTCAAAATGATTTTGTTTCAACGATTATTAATAAACATAAAGGCAAGAAAGTTGTTTTACTA +ACAGCGCATCGTCGTGAAAATATTGGGGAACCGATGCATCAGATTTTTAAAGCAGTAAGAGATTTGGCAGATGAATATAA +AGATGTTGTCTTCATTTATCCAATGCATCGTAATCTAAAGGTAAGAGCGATTGCCGAAAAATATTTATCTGGGAGAAATC +GGATTGAATTAATTGAGCCATTAGATGCGATTGAGTTCCATAATTTTACAAATCAATCGTACCTCGTGCTGACAGATTCT +GGTGGTATTCAAGAGGAGGCTCCTACATTTGGAAAACCTGTGTTGGTATTAAGGAATCATACAGAGCGTCCCGAAGGCGT +TGAGGCGGGAACATCGAGAGTAATTGGCACAGATTATGACAATATTGTTCGAAATGTGAAACAATTGATTGAGGATGATG +AAGCGTATCAACGTATGAGTCAAGCGAATAATCCATATGGTGATGGACAAGCATCACGACGTATTTGTGAAGCAATAGAA +TATTATTTTGGATTGCGCACAGACAAGCCGGATGAATTCGTACCTTTACGTCACAAATAATAAAAAACCCCTAATCATGA +AGTTGGTTTAGACAACCAGCGGTGACTAGGGGTTTTTAATATATTTATTTTTGATAGTGGTAGCCAATATCATATTTGAA +TACTTTATTTGATAATATTGGACTTTGCTGTCCATCGTCATCACTTTTTAAACGTACATTTTTATGAGCTTCTTTAAATA +CATCGGAATTCAACCAATTATTAAAGCTATCTTCAGATTCCCAAATAGTTAAGATTTTAACTTCGTCTGTATCCTCGGTA +TTTAATGTTTTAGTGACAAACATTTGTTGGAAGCCTTCAATAGTTTCAATACCTTGTCTATTGTAAAAACGTTCAATCGT +TTCTTCCGCACTGCCTTTTTGTAATTGTAATCTATTTTCTGCCATAAACATGGGCAATCACTCCTCTATTTTATGATTTG +ATTTGGGTAATGTTTTTACAAATGTAAAGAGTACAGCGGTTTGTATGATAACCATTATGATTAATCCTACACGGACTGCA +AGAACATCCACCATATAAATTGAAAAACCTATTACAATGTATAAGCTAATTAAAATTTTAATTTTCTGTTGTAGCGTGTA +GCCTCGATGTAAATAAAAGTTTTCTACATATTCTTTATAAATTTTTTGATTAATAAGCCAATTGTAAAAGCGATCTGAAC +TTCGAGCAAAGCAAAAAACTGCTACGAGTAAAAAAGGGGTCGTTGGCAGTAAAGGTAATACGGCACCTGCAATACCAAGC +GCTGTAAATATTAAGCCAATGACGATTAAAATAAGTCGCATTGAAAAAACTCCATTCTAGTACTAATGCGCATGTAATAT +TGTTTTAGTAATATAACTCATGCTAAATATAATGTGTATGATAAGTGCAATGACTCAGTAAAATGAAACGATGTTGAATT +ATCCTTGTCACATTAACGCATTTTAAGCGCGACTTTCATAACAACCAAACTATTTAATGAGAATTATTCTCAAGTATTAT +AGTTATATTATGTGTTTTATTTTTGAAAAGTGCAATATGTTTTCGAAAATAAGATTATTTTTATGTGCAAAAACGACGCA +AAAGTTTTAAAAATGAGACTTCTGTGAGCTGATTATTTTATAAAATGTAAACGCTTACTATATAATGTGAATCATATCGT +TTAAAAGCATTATTAAATATGATGCTAAGAGATTTATATTATAGCCAATAAACAAAGGAGAGATAATATGGCAGTAAACG +TTCGAGATTATATTGCAGAGAATTATGGTTTATTTATCAATGGGGAATTTGTTAAAGGTAGCAGTGACGAAACAATCGAA +GTGACTAATCCAGCAACTGGAGAAACACTATCACATATTACAAGAGCAAAAGATAAAGATGTCGATCATGCAGTCAAAGT +GGCGCAAGAGGCATTTGAATCATGGTCATTAACTTCTAAATCAGAACGTGCACAAATGTTGCGTGATATTGGTGATAAAT +TAATGGCACAAAAAGATAAAATTGCAATGATTGAAACATTAAATAATGGTAAACCGATTCGTGAGACAACAGCAATTGAT +ATTCCATTTGCTGCAAGACATTTCCATTATTTCGCAAGTGTTATTGAAACAGAAGAAGGTACAGTGAATGATATCGATAA +AGACACAATGAGTATCGTACGACATGAGCCGATTGGCGTCGTAGGTGCTGTTGTTGCTTGGAACTTCCCAATGCTATTAG +CTGCATGGAAGATTGCGCCAGCCATTGCTGCAGGTAATACAATTGTGATTCAACCTTCGTCTTCAACACCATTAAGTTTA +TTGGAAGTTGCTAAAATTTTCCAAGAGGTATTACCTAAAGGTGTTGTCAATATACTAACGGGTAAAGGTTCAGAATCAGG +TAATGCAATTTTCAATCATGATGGTGTAGATAAATTATCATTTACGGGCTCAACTGATGTAGGTTATCAAGTTGCCGAAG +CTGCAGCAAAACATCTAGTACCCGCTACATTAGAGCTTGGTGGTAAAAGCGCCAATATCATATTAGATGATGCTAATTTA +GACCTTGCAGTTGAAGGTATTCAGTTAGGTATTTTATTCAACCAAGGTGAAGTATGTAGTGCAGGTTCTCGATTATTAGT +TCATGAAAAAATTTATGATCAATTGGTGCCACGTTTACAAGAGGCATTTTCAAATATTAAAGTTGGAAATCCACAAGATG +AAGCTACACAAATGGGTAGTCAAACTGGTAAGGATCAATTAGATAAAATTCAATCATATATTGATGCAGCAAAAGAATCA +GATGCACAAATTTTAGCAGGCGGTCATCGCTTAACTGAAAATGGATTAGATAAAGGGTTCTTCTTTGAGCCGACATTAAT +TGCTGTGCCAGACAATCATCACAAATTAGCACAAGAAGAAATATTTGGACCAGTGTTAACAGTGATTAAAGTGAAGGACG +ATCAAGAAGCAATTGATATAGCTAATGATTCTGAGTATGGTTTAGCAGGCGGTGTATTTTCTCAAAATATCACACGTGCA +TTAAATATTGCTAAAGCTGTACGTACAGGACGTATTTGGATTAACACTTACAACCAAGTACCAGAAGGCGCACCATTTGG +TGGTTATAAAAAATCAGGTATCGGTCGAGAAACTTATAAAGGTGCGTTAAGTAACTATCAACAAGTTAAAAATATTTATA +TTGATACAAGCAATGCTTTAAAAGGTTTGTACTAGAATAAATATCGTTTCTGAAGCGTGTTTGTAGGTCAGTCTAGCGGT +AAGTCTTAACATTTAACGGCGTTGTTTAGATTTTAAGCAAAACAAAATATATAGGAACACGTATCATGATATTAGGATAT +AATGACTAAAATAATAGCAGTAGGATGGTTTTTAATTGCAAATCATCTTACTGCTGTTTTTAATTATGCTAATTTGCGAT +GCGGCTATTATAAGGACAGAGTTGTTTATTAATTATGGTGATTTAGAAATATGAAGTTCAATATGCAAAGTCATCGTTTG +TTTTAATATGCGGAACAATCATTAAAGTTATTGCGATTTTTTGAACTTAATGAAACTAAACAATAAATTTGAGATACTTT +TTTGTCATTTTTATGTAACTAACACAATAATCTCGTACATTATTAAAATTTTCTATATGATAGGAATAAAGCAAAGCGCG +AGTGTGCTGTAAAAGTTTTCCAAGGTGATATTACATAAAGCTATAAAGGGTAAAGATTAATGAGTTGTCATGTAAATGAC +GATGATGTATAAATCATGGTTAATTACGGAAGCATTAATATTAACCTGAGAAGCTATAAAGAATTATTTTTAAAAGCGAC +AATATTAAATACGACGCATTTATTTAGGAGTGGCAAACGTATGAATGGGAAAAAGGCGAATACGATAAACAGATACAAAT +ATTTTCATCATGTCAATCATCAAAAAATTCAACAAAGTTCTAAAAAGACGCTGTGGGCATCACTAATCATCACATTGTTA +TTTACAGTGATTGAATTTGTCGGAGGTTTAGTATCTAATTCATTGGCATTACTGTCAGATTCATTTCATATGCTTAGTGA +TGTATTAGCACTTGGTTTATCTATGTTGGCCATTTATTTTGCAAGTAAAAAGCCGACTGCACGATACACATTTGGATATT +TAAGATTTGAGATATTAGCTGCATTTTTAAATGGTTTAGCATTAATTGTAATTTCAATCTGGATTTTATATGAAGCTATT +GTACGTATTATTTATCCGCAACCAATTGAAAGTGGCATTATGTTTATGATTGCTAGTATTGGTTTACTCGTCAATATTAT +TTTGACTGTTATCCTTGTAAGGTCTTTAAAACAAGAAGACAATATCAATATTCAAAGTGCATTATGGCATTTCATGGGAG +ACTTATTGAACTCTATTGGTGTCATCGTTGCAGTTGTATTGATTTACTTTACAGGATGGCGCATCATCGACCCAATCATT +AGTATTGTAATTTCACTCATCATTTTACGTGGTGGTTATAAAATTACGCGTAATGCGTGGTTAATTTTAATGGAAAGTGT +GCCTCAACATTTGGATACTGATCAAATTATGGCAGATATTAAAAACATAGATGGCATATTAGATGTACATGAATTTCATT +TGTGGAGTATTACAACAGAGCATTATTCATTAAGTGCCCATGTTGTGTTAGATAAAAAATATGAGGGTGATGATTATCAA +GCGATTGATCAAGTATCATCATTGTTGAAAGAAAAATATGGCATTGCACATTCAACGTTGCAAATTGAAAACTTGCAATT +GAATCCATTAGATGAGCCATACTTCGACAAATTAACATAAATAAAACATTGTAGCGCCTAAAACATTAATCTATGTCATA +GGCGCACGTTTCGTTTTATACTTATGTTGCATCATTTAAATGATTTTCGTCAATTTCTTTGATGCTATCTACATCTAACA +CGACATCTTTAGGTTTCAAAATATGAATATGTTTTTCATCATTTGTATGTAAAATGCGTTCTATGATGTACCTTTGACCG +GCCATTGTTTCTACAGCAATCTTTTTGTTTCTAGCTAAACTTGCTACGACAGATTCTTTATCCATAATGATAGCCCCCTA +TATATATGTTTATTTACTTATACCCTAACATGATTTTTATACTCTTTGAAAATATATTTTACAGAATTTTATCTAAATAT +TTAAAAAAATATCTTAATATCCTTGTAATCCGATAAGAATTATAGTAATATTTTTTCAACCATTGTTATAGGAGGTCTTA +TTAATGACATTATTTTTATTAGAAGCTAACAATCTTGATTTTGCATCAACGAAAGAAGAACTAGAAGCAAAGGCAGCATC +ACTATCTACGAAGACAATTCCAACATTAATTGAAGTACAAGCTACTGAAAATTTAACTCATGGTTATTTTATTGTGGAAG +CAAATGACGAAGCAGAAGCTAAACAATTTTTAACAGAAGCAGATATTAGTATTCAATTAGTCAAAGAAGTACGCTTAGTT +GGTAAAGATTTAGATGAAGTTAAAAATGGTGATGCACATGTTGATTACCTTGTAACTTGGAACATTCCGGAAGGCATTAC +GATGGATCAATATTTAGCACGTAAAAAGAAAAATTCTGTTCATTATGAAGAAGTGCCAGAAGTTGAATTTAAACGCACAT +ATGTATGTGAAGATATGTCTAAATGTATTTGTTTATACAACGCACCTGATGAAGAAGCGGTACGTCGCGCGCGCAAAGCA +GTTGATACACCGATTGATGGCATCGAAAAACTTTAATAAGACAACAAGTTGATGAGATATATGTATATAGGTTTGGCATG +GATTTCGATTGCAGTTAATTAGAATAGCTCAATGCTATAAATGTAAGTAGTTGATATGAAGAAACTAATGAACTAAATGC +AAGTATTGTCTAAAACAATCATTTTATTGAAATTTAGTAGAGCTGAAATTAATATAACGTCGTTAATTGAATAACGCTTA +TGTTATAAGAGCACTCATACCAAACCATAATCATCTATAGATATAACAATTCACGATATAAGGGCTGTGTTTGGCATAGC +CCTTTAGATATACACTTAATTCCTATTAAAATAGTAGGGATTAAAAGGGGGCTTGTCATGATTAAAATTCAACAATTACA +ACATCACTTTGGATCACATAAAGTAATTCATAACTTTAATTTGGACATTAGCAAGGGAGAAATAGTCACTTTCATAGGGA +AAAGTGGTTGCGGAAAGTCTACTTTACTCAATATTATCGGTGGATTTATTCATCCATCGTCTGGTCGTGTCATTATTGAT +AACGAAATTAAACAACAGCCATCTCCAGATTGTTTAATGCTATTTCAACATCATAATTTGCTGCCATGGAAAACGATTAA +TGACAACATTAGGATTGGATTACAACAGAAAATTAGTGATGAAGAGATTAACGCACAGCTTAAATTAGTTGATTTAGAAG +ACAGGGGAAAGCATTTTCCCGAGCAACTGTCCGGGGGTATGAAACAACGTGTGGCACTATGTCGAGCGCATGTGCATAAG +CCTAACGTTATATTGATGGATGAGCCATTAGGTGCATTAGATGCATTTACACGTTATAAACTTCAGGATCAACTAGTGCA +ACTAAAACATAAAACGCAATCAACTATTATTTTAGTGACGCATGACATTGATGAAGCTATTTATCTTTCCGACCGCATTG +TTCTGTTAGGTGAAGGGTGCAATATTATTTCTCAATATGAAATTACAGCATCACATCCACGCAGTCGTAATGATAGCCAC +CTACTTAAGATTCGTAATGAAATTATGGAAACATTTGCATTGAATCATCATCAAGTTGAACCTGAATATTATTTATAAGG +AGTGAGTGACGATGAAAAGGTTAAGCATAATCGTCATCATTGGAATCTTTATAATTACAGGATGTGATTGGCAAAGGACG +TCTAAAGAACGGTCTAAAAATGCCCAAAATCAGCAAGTGATTAAAATTGGATATTTGCCGATTACACATTCAGCTAATTT +GATGATGACTAAAAAATTATTATCACAATACAATCATCCGAAATATAAACTAGAATTAGTTAAATTCAATAATTGGCCAG +ATTTAATGGACGCATTAAACAGTGGTCGTATTGATGGTGCATCAACTTTAATAGAGCTAGCGATGAAATCAAAACAGAAG +GGCTCAAATATAAAGGCTGTGGCATTGGGCCATCATGAAGGCAATGTCATTATGGGACAAAAAGGTATGCACTTAAATGA +ATTTAATAATAATGGCGATGATTACCATTTTGGTATACCACATCGTTATTCAACACATTATCTTTTACTTGAGGAATTAC +GTAAACAATTAAAGATTAAACCGGGGCATTTTAGCTATCATGAAATGTCGCCAGCAGAAATGCCAGCCGCATTGAGTGAA +CACAGAATTACAGGGTATTCTGTAGCCGAACCATTCGGTGCACTGGGTGAAAAGTTAGGCAAAGGTAAGACTTTGAAACA +TGGTGATGACGTTATACCTGATGCGTATTGCTGTGTGCTAGTACTGAGAGGGGAATTGCTTGATCAACACAAGGATGTAG +CGCAAGCATTTGTACAAGATTATAAAAAGTCTGGCTTTAAAATGAATGATCGCAAGCAAAGTGTAGACATTATGACGCAT +CATTTTAAACAAAGTCGTGACGTTTTAACACAGTCAGCGGCATGGACATCCTATGGTGATTTAACAATTAAGCCATCCGG +CTATCAAGAAATTACGACATTGGTAAAACAACATCATTTGTTTAATCCACCTGCATATGATGACTTTGTTGAACCGTCAT +TGTATAAGGAGGCATCGCGTTCATGACACGTCCCACAAATAACAAATTTATATTACCTATTATCACATTTATTATTTTCT +TAGGCATTTGGGAAATGGTCATTATTATTGGGCATTACCAACCTGTATTGTTACCGGGTCCTGCTCTTGTAGGAAAAAGT +ATATGGTCTTTCATTGTTACTGGAGAAATTTTCCAACATTTAGCAATTAGTTTATGGAGATTTGTAGCGGGCTTTGTTGT +CGCATTGTTGGTTGCTATTCCATTGGGCTTCTTGCTTGGAAGGAATCGTTGGCTATACAACGCTATCGAACCGCTATTTC +AATTGATTAGGCCGATATCTCCGATAGCATGGGCACCATTTGTTGTTCTATGGTTTGGTATTGGTAGTTTGCCAGCGATT +GCGATTATTTTTATCGCTGCTTTTTTCCCAATTGTGTTCAATACTATTAAAGGCGTTAGAGACATTGAACCTCAATATTT +AAAAATAGCAGCAAATTTAAATTTAACTGGGTGGTCATTGTATCGCAATATATTATTTCCCGGGGCATTTAAACAAATCA +TGGCTGGGATACATATGGCGGTAGGAACAAGTTGGATATTTTTAGTTTCTGGTGAAATGATTGGTGCACAATCGGGATTA +GGTTTTTTAATCGTTGATGCACGAAATATGTTGAACTTAGAAGATGTTTTAGCAGCAATATTCTTTATCGGATTATTTGG +TTTTATTATTGATCGATTCATTAGTTATATTGAGCAGTTTATACTTAGAAGATTTGGTGAATAAGGAGAGATGATGATGA +CTTTAGAAACGCTTATCAAAGAACAATTAGATCCTCATTTAGTAGAAGTTGATGAAGGGACGTATTATCCGAGAACATTT +ATTCAGCAATTATTTGTAGATGGTTATTTCGGTGAGGCGGCATTGAGAAAAAATGCTGAAGTAATCGAAGCTGTATCGCA +GTCTTGTTTGACAACAGGATTTTGTTTATGGTGCCAATTAGCTTTTTCAACGTATTTAGAAAATGCCACGCAGCCACATT +TAAATAATGACTTACAACAGCAATTGTTATCTGGAGAAATATTAGGTGCTACCGGATTGTCTAATCCGATGAAGTCATTT +AATGATTTAGAAAAGTTGAACCTTGAACACACTTATGTTGATGGACAATTGGTTGTCAGTGGACGTATGCCAGCTGTAAG +TAATATTCAAGAAGACCATTATTTTGGTGCGATTTCGAAACATGAATCATCAGATGAATTTGTCATGTTCATTCTACGTG +CCAATCAAGATGGTATCACTCTTGTTGAAAAAACCAATTTTTTAGGAGTCAACGGGTCAGCAACGTATCAAATCACATTG +AATCAAGTCGTAGTGCCACAATCACAAATTATCACGCATGATGCGAAGCAGTTTGCGGCAACTATTCGCCCGCAATTTAT +TGCTTACCAAATTCCAATAGGATTAGGCTCAATTAAAAGTTCTTTAGAGTTAATTGATGCATTTTCAAATGTGCAAAACG +GAATAAATCAATATTTAGAGTATGATGTTGAAGCTTTTAAAAAACGTTATCGTCAACTTAGAGAGGAATATTATGCAATA +TTAGATGACGGTAACTTAACTTCACATTTAAATGAATTAATATCATTGAAGAAGGACATCGGCTATTTATTGTTAGATGT +AAATCAAGCTTCTGTTGTCAATGGTGGTTCTAGAGCGTACACACCATATTCGCCACAAGTTCGCAAGTTAAAAGAAGGAT +TCTTCTTCGCAGCATTGACACCGACATTAAGACATTTAGGTAAACTTGAAGCAGAGTTGAAGGGGTAAGTGTGATAAGCT +GATTTTTTGTTTAGATGCGTTTGTTGAAACATTTTTTAAAATAATATAAATCTTAGTTTATAAACATTTTCTGTTAATTT +GTTATATCCTTTTAACTAGGAAAATATACATTTCGTAATAATAATAATCGTTATCATTGAAAAAGTGTTAATAAGGTGTA +TAATGAAAATGTGAACAATTAATGAACTTCTTATTTTAAAGAAGGTGAATACTATAGATACGCATACTAAAGAACAACAA +TTCTCGAATCTAGTAAGATCTTATCGTAAAGAATACGTGGGTAAAGGACCCAATAGTATTCGAGTGTCGTTTAAAGATAA +TTGGGCGATTGCACATATGACAGGTGTTTTGAGTAAAGTTGAGAGTTTTTACCTAAACGACAAACGCAATGAATCGATGC +TCCATTATACACGCACAGAGAAGATTAAACAGATGTATAAAGAAATAGATGTAAATGAGATGGAAAGTCTTGTAGGCGCT +AAGTTTGTAAAATTATTTACAGATATTGATTTGAATGATGATGAAGTCATTTCAATATTTGTTTTCGATAAGTCAATAGA +ATAAGTGTTGCTGGTGTAAGGTACACGGTGCTGTTTGCTAACTTCGCTTTGAATTTAACAATAATTCAAGGGGGTGGTAT +GTCAAACGGTGCCGTTTTTTTGTCATATTTTTAAAACAAGCAACATGCAACACGTACTTTAAGGAAGTCAAAATTTATCA +TTTAGGAGAGATGGATATGAAAATCGTAGCATTATTTCCAGAAGCAGTAGAAGGTCAAGAAAATCAATTACTTAATACTA +AAAAAGCATTAGGATTAAAAACATTTTTAGAGGAAAGAGGACATGAGTTCATTATATTAGCAGATAATGGTGAAGACTTA +GATAAACATTTACCAGATATGGATGTGATTATTAGTGCGCCATTTTATCCTGCATATATGACTCGTGAACGTATTGAAAA +AGCACCGAACTTGAAATTAGCAATTACAGCAGGTGTAGGATCTGACCATGTAGATTTAGCGGCAGCAAGTGAACACAATA +TTGGTGTCGTTGAAGTTACAGGAAGTAATACAGTTAGTGTGGCAGAACATGCGGTTATGGATTTATTAATACTTCTTAGA +AACTATGAAGAAGGTCATCGTCAATCAGTAGAAGGTGAATGGAACTTGTCTCAAGTAGGTAATCATGCGCATGAATTACA +ACACAAAACAATTGGTATTTTTGGATTTGGTCGAATTGGACAACTTGTTGCTGAAAGATTAGCGCCATTTAATGTAACAT +TACAACACTATGATCCAATCAATCAACAAGACCATAAATTGTCTAAATTTGTAAGCTTTGATGAACTTGTTTCAACAAGT +GATGCGATTACAATTCATGCACCATTAACACCAGAAACTGATAACTTATTTGATAAAGATGTTTTAAGTCGTATGAAAAA +ACACAGTTATTTAGTGAATACTGCACGTGGTAAAATTGTAAATCGCGATGCGTTAGTTGAAGCGTTAGCATCCGAGCATT +TACAAGGATATGCTGGTGATGTTTGGTATCCACAACCTGCACCTGCTGATCATCCATGGAGAACAATGCCTAGAAATGCT +ATGACGGTTCACTATTCAGGTATGACTTTAGAAGCACAAAAACGTATTGAAGATGGAGTTAAAGATATTTTAGAGCGTTT +CTTCAATCATGAACCTTTCCAAGATAAAGATATTATTGTTGCAAGTGGTCGTATTGCTAGTAAAAGTTATACAGCTAAAT +AGAATAAGGATGCTGGGCTAGCGATTAACGCTTTCAATTTTATATAAATGAATCATATAAGCACTACTGCTGTTGTAAAG +ATGGCAGTAGTTTTTTTATGATTACATCTAAGTATAGTCACGGCTATGTTAGGACAATGATTTAACATTTACGCACATAT +GTGTTCACTTACGCAATTATTGATAAATTTCATTCATTTGGAATAATATATAGTCATATAGTGCTAATTTTTTTGAGGCA +TTATATGCAAGATGCATTGTAAAGTCGATGCATTTGTTTGTTAAATAACTTTGTATAACTAAAAATTCTTTGTTTCAACG +TATAATCTAATAATAGATTTTATATAAATGGTAGTGTGTATATATGTGGAAAGGGGTGTAGTTATTAATGAAACGCTTAA +GTACGACTTTGAAAGTACGATTGATTAGCAATTTTTTACAGCTAATTATTACGACAGCATTTATACCGTTTATAGCACTA +TATTTAACAGATATGTTAAGTCAATCAATTGTCGGTATATATCTTGTTGGTTTAGTGGTTCTAAAATTTCCATTGTCCAT +TATATCTGGTTACCTTATTGAGATATTTCCGAAAAAGTTGCTAGTACTTATTTATCAAGCGACGATGGTGATAATGCTTG +TGTTCATGGGCGTATTTGGGTCACATCAATTGTGGCAAATTATTGGTTTTTGTGTTGCATATGCCATATTTACAATCGTT +TGGGGATTACAATTTCCAGTTATGGACACATTAATTATGGATGCAATTACCGAAGACGTGGAACATTATATTTACAAGAT +TAGCTATTGGATGACAAACTTATCGGTAGCTATTGGGGCATTGTTAGGTGGCTTGATGTATGGCTACAGTATGTTACTAC +TTTTCTTAATAGCAGCTTGTATATTTTTAATTGTACTCTTTATTTTATATATTTGGTTACCTCAAGACCGAAATCAAGTA +AAGCAAAGTGATGACAAGAGGCATGCAAGTCGTTATCAAAAATTACAAATAATGAATATATTTCGCAGTTATAAATTAGT +TTTGAAAGACCGTAATTATATGTTATTGATTTCGGGGTTCAGTATCATCATGATGGGTGAATTTTCAATCTCCTCATATA +TTGCTATTAGACTAAAGGATCAGTTTGAAACAATAAGTATAGGTTCATATGATATTACAGGTGCTAAGATGTTAGCAATC +TTGCTAATGATTAATACGGTCGTCGTCATTTTACTCACGTATTCAATCTCGAAAGTTGTATTGAAAATAGATTTTAAAAA +AGCTTTAATCACTGGTTTGCTGATTTATATTGTTGGCTATAGTGGTCTAACCTATCTTAATCAGTTTGGCTTATTAGTTG +TTTTTATGATAATTGCGACTGTAGGTGAAATTATTTATTCGCCTATAGTTTCAGAACAACGCTTTAAAATTATTCCTAAA +GCTAAAAGAGGAACATATAGTGCAGTTAATGCATTAGGTATTCATTTTTCAGAAACACTAGCTAGGTTAGGGATTGTGTT +GGGTGTTTTCTTAACGTCATTACAAATGGGACTGTATATGTTTATCGTTTTAACAATTGGTGCTAGCATGCTTGTTGCTG +GTGTATTTGGGGGACAAAAACAAGTGAATACAAATTGAATTTATTTAAATATTTATTTTATGCATCTTTTTATATGGAAA +ACCTATTAATTTGGTCGTAATTAAAATAGAGATAAATTTATTAAGAATTTGTTAAAATTTAAGGATAACAATTGTTTTAT +GCGAGTTGTTTGTTTAATATAAACTTGCTTCATGGATATTTTGTAACAACAATATTGAGTTTGCAATGAGATGTGAACAA +CAGAAAGTGATTAACGATTTGTAGTGTGCTATTTATCGAAGATGTATGTAATTTACATTGAAAAATATACCAATTGTAAC +GATGATACAAGATGTATTTTAGGGTAGAATATATCGCTCATATGTTGCGAATGGTGTTTTATATTAATCATTACATAACG +AGTTAATTAACATTTCGTCGGTTATCATATCATCATGCAATTTCAGTAAGTAATGAGTATTATGATATGAAAGAAGGACT +TTTTATGATTATGGGTAATTTGAGATTTCAACAGGAATATTTTCGTATATACAAAAATAATACAGAATCAACGACACACC +GTAATGCGTATTGGGTTAAACTCGCTAAAAATGTTGAAGCTACTAAAATGATGTATGCATTATCGACAATTGTGCAACAA +CATGCATCTATAAGACATTTTTTTGATGTTACTACCGATGACAATTTAACAATGATACTTCATGAATTTCTGCCTTTTAT +TGAGATAAAACAAGTTCCATCTTCTTCCGCAAACTATGATTTAGAAGCTTTTTTTAAGCAAGAATTAAGTACTTACCATT +TTAATGATTCACCTTTATTCAAAGTTAAATTGTTTCAGTTCGCTGATGCTGCATATATACTATTAGATTTTCATGTGTCC +ATTTTCGATGATAGTCAAATTGATATTTTTCTTGATGATTTATGCAATGCATATCGTGGCAATACTGTTATTAACAATAC +TCGACAGCATGCACATATAAATAGAAATGATGATAAAGACAATCAAGATGCATCGCATATAGCATTAGACTCAAACTATT +TTCGGTTAGAGAATAACTCTGACATCCATATTGATAGTTATTTTCCAATTAAGCATCCATTTGAACAAGCTTTATATCAA +ACGTATTTGATTGATGATATGACATCAATAGATATGGCATCGTTGGCTGTTAGTGTGTATTTAGCTAATCATATAATGAG +TCAACAACATGATGTCACATTAGGTATACATGTACCATCACATTTACCAAATGATTTACACGGAAATATTGTGCCGTTAA +CGTTAACAATCGATGCAAAAGATGTATGTCAACGTTTTACAACAGATTTTAATAAATGTGTGTTGCAAAATATGTCGCAA +TTACAGTGCGCGAAGTCTTCGCTTTCACTAGAGACTATTTTTCATTGTTATCATCATATGATGTCTTGTTGTAATGATGT +TATTGAGGATGTACATCAAATACATGATGCACATACATCTTTAGCGGATATTGAAATTTTTCCACATCAACACGGGTTCA +AAATTATATATAACAGTGCAGCATATGATTTGCTCTCAATCGAGACGCTGAGTGACTTAGTTCGAAATATTTATTTGCAA +ATTACTGAAGAAAATGGAAATAAACGAACAACTGTAGATGAACTTAATTTGATGACAGAACGTGATATTCAATTATATGA +CGATATCAATTTAAGTTTGCCTGAGATAGATGATGCGCAAACAGTTGTTACCTTATTTGAGCAACAAGTTGAAGCAACGC +CGAATCATGTCGCTGTGCAATTTGACGGAGTGTTTATAACATATCAAACATTGAATGCACGCGCGAATGATTTAGCACAC +CGTTTGAGAAACCAGTATGGTGTTGAACCTAATGATCGTGTCGCTGTCATAGCTGAAAAAAGTATTGAGATGATAATAGC +GATGATAGGTGTGTTGAAAGCTGGTGGGGCTTACGTGCCAATTGATCCGAACTATCCAAGTGATCGTCAGGAGTACATTT +TAAAAGATGTAACGCCTAAAGTTGTAATAACGTACCAAGCTTTATATGAAAATGGTAAACAAAATATTAATCACATTGAT +TTGAATAAGATAGCGTGGAAAAATATTGATAATCTTTCTAAATGTAACACGTTAGAAGATCATGCTTATGTTATTTACAC +GTCGGGGACAACTGGTAACCCTAAAGGGACACTAATTCCGCACCGAGGTATTGTTCGCTTGGTCCATCAAAATCATTATG +TACCATTAAATGAAGAGACGACGATTTTGTTATCAGGAACTATAGCCTTTGATGCTGCAACATTTGAAATATATGGTGCA +TTGCTCAATGGTGGAAAGCTGATTGTTGCTAAAAAAGAACAATTATTAAATCCAATAGCGGTAGAACAATTAATCAATGA +AAATGACGTTAATACTATGTGGTTAACCTCCTCATTATTTAATCAGATTGCTAGTGAACGAATAGAAGTATTGGTACCGT +TAAAGTATTTATTAATTGGTGGAGAAGTATTGAATGCTAAGTGGGTGGATTTGCTTAATCAAAAACCGAAGCATCCTCAA +ATTATTAATGGTTATGGACCAACTGAAAATACAACATTTACAACGACGTATAATATACCTAACAAAGTTCCAAATCGTAT +TCCTATTGGTAAACCGATTCTGGGTACTCATGTTTATATCATGCAAGGCGAGCGTCGGTGTGGCGTTGGTATTCCTGGAG +AATTATGTACAAGTGGCTTTGGGTTAGCTGCAGGTTATTTAAATCAGCCAGAATTGACAGCAGATAAATTTATCAAAGAT +TCAAATATAAATCAGCTGATGTATAGAAGTGGTGATATCGTTCGTTTGTTACCCGATGGCAACATAGATTATTTATATCG +AAAGGACAAACAAGTTAAGATTCGAGGGTTTAGGATTGAGTTGTCAGAGGTTGAGCATGCGCTCGAGCGTATACAAGGTA +TTAATAAAGCAGTTGTTATTGTTCAAAATCATGATCAAGATCAGTATATCGTTGCTTATTATGAAGCGATGCATACATTA +TCACATAATAAGATTAAATCACAATTACGTATGACCTTACCGGAGTACATGATACCAGTTAATTTCATGCATATTGAGCA +AATTCCTATTACTATTAATGGGAAATTAGATAAGAAGGCATTGCCTATCATGGACTATGTCGATACGGATGCCTATGTAG +CACCGAGTACAGATACCGAACACTTGCTATGCCAAATTTTTGCAGATATTTTACATGTGAATCAAGTAGGTATTCATGAT +AATTTCTTTGAATTAGGTGGCCATTCATTAAAAGCAACGTTAGTGGTGAATCGGATAGAGGCATCTACTGGGAAACGATT +ACAAATTGGTGATTTATTACAAAAGCCAACTGTATTTGAACTAGCACAAGCGATTGCTAAGGTTCAAGAACAAAACTATG +AAGTGATTCCAGAAACTATAGTTAAAGATGATTATGTGCTGAGCTCTGCACAAAAGCGTATGTATTTATTATGGAAATCA +AACCATAAAGATACGGTGTATAACGTACCTTTTTTATGGCGGTTATCATCAGAACTTAATGTAGCTCAATTGCGACAAGC +AGTGCAGCGTTTGATAGCGCGACATGAGATTTTACGAACACAATATATTGTTGTAGATGATGAGGTTCGACAACGTATTG +TGGCAGATGTTGCAGTTGACTTTGAAGAAGTTAACACGCATTTTACGGATGAACAAGAAATCATGCGCCAATTTGTAGCA +CCTTTTAATTTGGAAAAGCCAAGTCAAATTAGAGTGAGATACATTAGAAGTCCCTTACATGCATACCTCTTTATAGATAC +GCATCATATCATTAATGACGGTATGAGTAATATACAATTAATGAATGATCTTAACGCACTTTATCAACATAAATTATTGT +TACCACTTAAATTGCAATATAAAGACTATAGTGAGTGGATGTCGCATCGTGATATGACGAAACATAGACAATATTGGTTA +TCTCAATTCAAAGATGAAGTACCTATTTTAAGCTTACCGACAGACTATGTTAGACCAAATATTAAAACGACAAATGGAGC +AATGATGTCATTTACAATGAATCAACAAATGAGACAGCTACTTCAAAAGTATGTAGAAAAGCATCAAATTACTGATTTTA +TGTTCTTTATGAGTGTGGTCATGACGTTGTTAAGTAGATATGCTCGAAAAGATGATGTTGTTGTCGGTAGTGTGATGAGT +GCGCGTATGCATAAAGGCACGGAGCAAATGCTAGGCATGTTTGCTAATACGTTGGTATATAGAGGGCAACCGTCACCTGA +TAAAATGTGGACACAGTTTTTACAAGAGGTTAAGGAAATGAGTTTGGAGGCATACGAGCATCAAGAATACCCATTCGAAT +GTTTAGTAAATGACTTAGATCAATCACATGATGCCTCACGGAATCCATTATTTGATGTCATGTTAGTACTACAAAACAAT +GAAACGAATCATGCTCATTTTGGGCATAGTAAATTAACACACATTCAACCCAAATCAGTGACGGCGAAATTTGATTTATC +TTTCATCATTGAAGAAGATCGCGATGACTATACAATCAATATCGAGTATAATACCGATTTATATCACTCAGAAACAGTTC +GTCACATGGGTAATCAATGTATGATTATGATTGATTATATTTTGAAGCATCAAGATACACTACAAATTTGTGATATACCA +AACGGCACGGAGGAACTTCTAAATTGGGTCAATACGCATGTTAACGATCGAATGCTTAATGTCCCGGGAAATAAATCTAT +CATAAGTTACTTTAATGAAGTTGTCTCACGACAAGGTAATCATGTTGCGCTAGTCATGAATGATTTGACAATGACGTATG +AAACATTACGCAACTATGTGGATGCCATTGCGCACATGCTCCTATCAAATGGTGTGGGCAATGGTCAACGGGTTGCCTTG +TTTACAGAACGTAGTTTTGAAATGATTGCGGCGATGTTGGCGACAGTTAAAGTAGGTGCATCTTATATACCTATCGATAT +TGATTTTCCGAATAAACGACAAGGTGCAATTTTGGAGGATGCTAAAGTAACTGCAGTCATGTCTTACGGCGTTGAAATTG +AAACGACATTACCAGTCATTCAATTGGAAAATGCTAAAGGCTTTGTTGAATCAAAGGAAAATGAACAATATGATGATTTA +CATGGCAATCAACTTGAAAACACAGCGATGTTAGATAATGAGATGTATGCTATTTACACATCTGGTACGACCGGGATGCC +TAAAGGGGTTGCCATACGACAACGAAATTTGTTGAATTTAGTGCATGCATGGTCAACTGAATTGCAATTAGGCGACAATG +AAGTATTTTTGCAACATGCAAATATTGTTTTTGATGCATCAGTTATGGAGATTTATTGTTGTTTGTTAAATGGTCATACG +CTTGTGATTCCAGATAGAGAGGAACGTGTTAATCCAGAACAGTTACAACAACTCATTAATAAGCATCGTGTGACGGTTGC +GTCGATTCCGTTACAGATGTGTAGTGTTATGGAAGACTTTTATATTGAAAAGTTGATTACAGGCGGGGCAACTAGTACGG +CATCCTTTGTTAAATATATTGAGAAGCATTGTGGCACGTATTTCAATGCCTATGGACCATCTGAGTCAACAGTCATCACA +TCGTATTGGTCACATCATTGTGGTGATTTGATACCTGAGACGATTCCAATTGGCAAACCCTTATCTAACATCCAAGTGTA +TATTATGTCAGATGGTTTGTTATGCGGTATTGGTATGCCAGGCGAGTTGTGTATTGCAGGTGATAGTTTAGCGATAGGAT +ATATTAATCGTCCAGAATTAATGGCTGATAAATGGCAAAATAATCCATTTGGTAAAGGAAAGTTGTATCATAGTGGTGAT +TTAGCACGTTATACATCTGATGGTCAAATTGAATTTTTAGGAAGAATAGATAAACAAGTGAAAGTTAACGGGTACCGTAT +TGAACTTGATGAAATTGAAAATGCAATATTAGCTATTCGTGGTATATCTGATTGTGTTGTAACAGTAAGTCACTTTGATA +CGCATGATATATTGAATGCTTATTATGTCGGAGAGCAACAAGTGGAACAGGATTTGAAGCAATATTTAAATGATCAGCTG +CCTAAGTATATGATTCCTAAGACTATAACGCATATCGATTGTATGCCATTAACCACGAATGACAAGGTGGATACTACGCG +TTTGCCAAATCCATCACCTATACAACAGTCTAATAAAGTGTATAGCGAACCCTCTAATGAAATTGAGCAGACATTTGTTG +ATGTATTTGGAGAGGTATTGAAACAAAATGATGTCGGTGTTGACGATGATTTCTTTGAACTTGGTGGTAACTCATTAGAG +GCGATGTTAGTTGTCTCGCATTTAAAACGATTTGGCCATCATATTTCAATGCAGACATTATACCAATATAAAACCGTGCG +ACAGATTGTTAATTATATGTACCAAAATCAACAATCATTAGTTGCATTACCGGATAATCTTTCGGAATTACAAAAGATTG +TTATGTCTCGTTATAACTTGGGTATTTTAGAGGATAGTCTAAGTCATCGACCTCTAGGAAATACACTATTGACTGGCGCG +ACAGGTTTTTTAGGTGCTTATCTGATTGAAGTACTACAAGGATACAGTCATCGCATTTATTGTTTCATACGTGCTGATAA +TGAGGAAATAGCATGGTATAAGTTGATGACGAATTTAAATGATTATTTTTCAGAAGAGACGGTTGAAATAATGTTATCAA +ACATTGAAGTCATTGTTGGTGATTTCGAGTGTATGGATGATGTTGTTTTACCAGAAAACATGGATACGATTATTCATGCA +GGTGCTCGTACAGATCACTTTGGTGATGATGATGAATTTGAAAAAGTAAATGTTCAAGGTACTGTTGATGTCATACGTTT +GGCACAACAACATCATGCAAGGTTAATATATGTGTCTACGATAAGTGTGGGAACTTATTTTGATATAGACACAGAAGATG +TGACATTTTCAGAAGCGGATGTCTATAAAGGGCAACTACTAACATCACCATATACACGGAGCAAATTTTATAGTGAATTA +AAAGTATTAGAAGCTGTAAATAATGGCTTAGATGGTCGGATTGTACGTGTTGGTAATTTGACGAATCCTTACAATGGAAG +ATGGCATATGAGAAATATAAAGACTAACCGTTTTTCAATGGTAATGAATGATTTGTTACAACTGGATTGTATCGGGGTTA +GCATGGCTGAAATGCCTGTAGATTTTTCTTTTGTGGATACGACTGCAAGACAAATTGTCGCATTAGCACAGGTCAACACA +CCACAAATCATTTACCATGTGCTATCACCTAATAAAATGCCGGTGAAATCTTTGTTAGAATGCGTTAAGCGCAAAGAAAT +TGAACTCGTCAGCGATGAATCATTTAATGAAATTTTACAGAAACAAGACATGTACGAAACGATTGGATTAACTAGTGTTG +ACCGTGAACAACAACTAGCAATGATAGATACAACATTAACATTAAAAATAATGAATCACATCAGTGAAAAATGGCCAACG +ATAACTAACAATTGGCTGTATCATTGGGCACAATATATCAAAACAATATTCAATAAGTAAGTAGGGAAAGTTATGACAGT +ATTTGTAATGCAATTACAGAGTAACTTGAAAAGTATTGAAGAATTAATATCACAAAGTCGTTGGTCATATAAAAAACCGC +GTACAGTCAACTATAGATACAATCAAGATAAACTCATGCACAGATTGGGAGATATTTTAGTGCAATATGGAATTCAACAT +GACACAGGTTTATTACCACATGAATGGCATTATCACATTTCGCCACGAGGTAAGGCAGATATTGTTCAACACAATCGTGA +TGGACAGCCCATCTATGTGAGCTTATCATATAGTTATCCTTATATCGTGTGTGTTGTCGATAAAGAACCAGTTGGTATTG +ATATCGAAAAGATATCACAACGTTTAGACTGGCGTACGTTAGTGACGTGTTTCTCTACAAACGAAGCACATCAAATATGT +AGTTTAAATGATTTTTATCAAATATGGACACAAAAAGAAAGTTTTACAAAATTGATTGGTGAAGGTTTAATCAAAGGATT +GGACATTTATGATATGACACAATCACACTTTTATCAATCACGTGAAGTGAAGTTCAAACAATTTATTTTTGATCAGTTTA +TGGTACAGGTATGTTTCTTAGGACAGGCACCCTGGGGTTATAAAAAAGTGTCTGTATTTCAGTTATTGAGTAGTTAATAT +GACGTCTGACAGTATCATTGCGCAGTGATTTTAAACTATACTGCCTAAATGTTTACTGTTAGATGTTTTTTTATTGGAAA +AATGAAAGGTGAATACACGATGGCATCATTCAAAATCATGACGAGATTAAAGTAAGTGATAAGGTAAAAATATACTGAGC +AATATTGAAATTACCGAAAATAATAGTTTGTTACGACGCATTTGAGACTTTAATGCTCACGTAAGTTGGCTGGAATCATA +TCATGTCAGTTAATAAAATAACGTGAGCATCAATCATCATACTCAAACGATGATTTAAAACTCACGTTAATGTTAGTCAG +TGTATTAATTTAATTTATAGCGATAGACAAAATGTTGTCGCACTAATTCAATTGTCATCATCCAGACGATATGGCCAAAG +AATTCTGACAGATGCTCTTGGAATGGTTGATCCCACACAGCAGGTACAGTATGCATGATTGGCATAATGATAAGGTGGAA +TAATACCCAAATAGCAATACCAAAAACAGCACCTTGTCCCATTGCTAAGTAAGCGTATTTTTTAACTAATATGCAGTAAA +TAATTGCAATGACGATAGAAAAACTAAAGTGGACAATAAAGCTTACCCAAGGCAATTCCATATTTGAAAATGTATATGTT +TGATGCGTAAACTCACTACTAAATCCTAATTGTTGCAATAACTCTTGAGGTGGGTTCGTTGCATTACGTTCTGGTGTGCG +AGGTGGAAACATGACCTCCCAACCTAATTTTACAATTCCAGATAACAAGCCACCGATAATTCCAGCATAAATGAATATAC +CCATTTGTCGTTTCGTCAACTTAAGGACTCCTCTCTATTTACTATCCTCATTATAGTATTTTGTGAAAATAATCACAATA +AAAAGCTTTGCAAAACTTAAAATATTTTGATAAACAAATTTGTAAAAGGTTATTAGTTTTTTACATCTCATATTGTATCG +TACTGTATTCAAAATTGTTAACGAGATGAACGGTAAATAAATGTTTTAGCGCAATGGATAATCGAACTGGAATCATCTCG +CGTGTCAAAACTTGTTAGGCCTTAATTTCATAGTTATGAATTAAGGATTGTTGTGCCAACAAAATCATTATTGTAAATAG +ATTCAATGATATTTGGCTTGTTTCCTGATGCAATGATAACTTTAGGACAGCCATTTTCAATCGCATTTTTGGCATCTAGC +ACTTTGGGAATCATACCTCCATAAATATCACCATGTTCAATATATTGATGAATATCGACTAATGGCAATTGAGGTATAAC +AACATCATTGATGAGTACACCTGCAATATTACTTAATACATAAATAGGCGCTTTTAATGATGATGCAATAAAATAGGCAA +GCGTGTCAGCATTAATATTGTAAAATTCTCCATCATGGTTATTGAAACCAATCGAATTGATGATAGGTACAAATTTAGTA +CATAAATACTGTAAAGCATCCTTATTTAAAGCGGTCGGAACACCGACATATCCATATTGTTGATCAAAAGATGTAATTTC +AAACAGCTGTGCATCCAAACCACATAAGCCTATTGCAGAACATTGGTGCTGGTTAAATTGAGCTACTAATGCAGTGTTAA +CGTCTGCAATGAGCGTGTGTTTAGTAATGGTCATGGTTGCTTTATCAGTCACTCTTAGGCCATTAACAAAGTGTGGCTCG +ATTTGCTGGTTTGATAATGCTTCATTAATAAATGGGCCACCGCCATGAACGATAATGGGGTAGATGTTGTTTGATCGTAA +ATGCTTAATGTTGTTAATAATTGATGGATGCATGTCACTAAGTGTACTGCCACCAATTTTAATGACAATAAATTTCATCT +AACCAACACCACCTTATGTTCGATATGATGCGTTGATACGCACATAATCATAGGATAAATCACAACCGTATGCAGTCGCT +GCAGCGTTACCTAAACCAAGCTGAACGTCAATTGTGACATTTTCATGAGTTAATGTATTCGACATAGCTTGCTCATCAAA +TAGTACAGCCATACCTTTATCAACGACAGGTATTTGGTTCAGTTGAACATATGTGCAGTTAGGATCAATTTCACATCCGC +TGTAGCCAATAGCTGTAATGATTCGACCAAAATTGGCATCTTCGCCAAAAATAGCTGATTTTACTAGATTTGAACTTACG +ATAGTTTTACCGATTTTTCTTGCATCTGATATTGATTTAGCGCCTGACACATTGACGCTGATTAACTTTGTTGCGCCTTC +GCCATCTCTGGCTATAGCTTTAGCTAAAAATGTACAGACAAAATTGAATGCATCAACAAATGTTTCCCATTGTGGATGGT +CTTGACTAAGTATTTGGTGTTCAACTTGGTGATTTGCCATGACTAATACCATGTCATTTGTACTTGTATCGCCATCAACA +GTAATCATATTAAATGTATGGTCAGTCGAAGATTTTAATAATTGATGAAGTGTATTCGATTCAATCGATGCATCGGTTGT +TATAAAAGCAAGCATGGTAGCCATATTTGGGTGAATCATACCTGAACCTTTGGTGCTACCACCAATTGTAACGGTTTTAC +CATCGATTTTTAGTGATACAGCGATATGTTTTGTACAGGTATCAGTTGTTAAAATTGCCTCGTTAAACGCACCTGGCGTT +GCAAAATTAGCATCCTTAATATGTTCGGTCCCAGTCTTAATTTTATCCATAGGCAAATATTCACCAATGACCCCAGTTGA +AGCAACAGCAACATGCTCAGATGGTATTTGAAGTTGTTGAGCAACCCATGTTTGTGTTTGTCGTGCATCATCTATGCCTT +GTTGACCGGTACAAGAATTTGCATTAGCTGAATTAACAACAAGTGCTTGTAATTTTCCTTTAGACTTTTGTAAAGTGTCT +TCAGTGACAATAAGTGGTGCAGCTTTAAACTGATTTAAAGTATATACGGCAGCTGCACTTGCCAAAGACGATGAGTAAAT +CCACCCAAAGTCTTTTTTGTTAGCGCGTAAACCGATGTGCATACCACCAGCCGTGAAGCCTTGAGGTGTACTGATATCGC +CATGTTTAATAATTGAAAAGTTATATTGTTGTGATGTCGTTTCTTGATGTTTCATTCTAACACCCCTTATGGATAAACTG +GTGATTGATTTAGGCCAGTCGTCACTTCAAAATCATATAATATATTTAAATTTTGAATGGCTTGCCCACTTGCGCCTTTG +ACAAGGTTATCAATCACTGATACTAAAATTGCTGTTTGCGTTGTTTCATCTACATAGATGCCGATATCGCAGTAGTTACT +ACCGAGTACTTCTTTTGTGGTTGGAAAAGTCCCAATATCTCTAATTCTGACAAATGGCTGATTAGCATAATAAGAGGTCA +TTAATTTATGTAATGATTCAGTCGTATATTCAGATGATAATTTGACATATATTGTTGATAAAATACCTCGTGTCATTGGT +ACGAGATGTGGTGTAAATATGACTGATACATCTTGACCCGCAATGATAGATAAATATTGCTCGATTTCCGGTTTGTGTTT +ATGGTTTCCGATTGCATAAGCGCTTAGATTTTCATTCATTTCTGAAAAATGAACACGTTGTGATAATGAACGACCAGCAC +CTGACACGCCGGTCTTAGCATCAATAATAATAGATGACAAATCTACTATTTTTTCGCTAATAAGTGGATGTAATGCTAAT +AATGTTGCTGTAGGGAAACAGCCAGGATTAGAAATGAGCTTCGTTCCATTGTTATCAAACGATTGCCATTCTGAAATGCT +GTAAATAGCATGATTCAAATCATCTTGTGCTGCAGCAGTTTCTTTGTAATATGCTTCATATATTTCACGATTCTTAATTC +TAAATGCGCCAGATAAATCGATAACATGAATACCTTTTTCTACTAAGGGAGGGATACATGTTTTACTTACGGGTGCTGGT +GTCGCAAAGAAAATTACATCACAGTCATTATTATCCACTGTAAGTGCTTCGAAATGTTGCATAATATGTTGTAAATGTGG +AAATGTTAATTTCAACGGTTCATCTACTTTTGAATGTGAGTAGATGTGTGCAATCGTTACATGAGGATGTGTTTGTAACA +ATCGAATTAATTCAATTGCGCCATAACCGCTACCGCCAACGATACCTACTTTAATCATCATGAATCCCTCCTATGTCATA +TATAAATGATTAATTGTTAATTTTTAAAAACGTCTTGAAAAGCTGCAACAATTTGATGGATTTCCTCTTTATCAATGACT +AGAGGTGGAGACAATCGAATGATAGTACGATGCGTGTCTTTGCATAAGATTCCACGTTGAATCAGTTGATCCACAAAAGG +TGCAGCATCTGTGTTAAGCTCTATGCCTATAAATAAACCACGACCTCTAATTTCTTTAATACTAGGATGTTTAAGTTGTA +GCAACGCTTTTAATAAAAATGAACCTAAGCGTTCTGATCGTTCAACCAGTTGTTCATCTTTAAGTACATCAAGCGCTGCC +GTCGATATTGCAATGGCTAAAGGGTTACCACCAAATGTTGAACCATGTGTACCTGGTGTTAGAACACGCATGACATCATT +ATTTGCAAGTACAGCAGATACAGGGTATAAGCCGCCACCCAATGCCTTACCTAAAATATAAATGTCTGGAACGACTTGCT +CCCATTCCATAGCAAACCATTTCCCAGTTCTACCAAGACCAACTTGAATTTCATCTGCAATCAATAATATTTGATGTTTA +TCACATAGTTGACGCACAGCTTGAATATATCCTTTCGGTGGTATATTAACGCCACCTTCACCTTGAATTGGTTCCAAAAT +AATTGCTGCTGTATTCGGTGAAATAGCTTGTGTTAATTGTTCAATGTCTCCAAAATCTACTGTTGTAGTGCCTTGAAGTA +GGGGGTGAAATCCTGCTTTATATGCGTCGTGGTTAGATAGTGATAATGAGCCAAGTGTACGACCGTGAAAATTGTTATTC +ATAGCGATGATTTCAACTTGTCCGTCAGTAATGCCTTTAACTTCAGAGCCCCATTTTCTAGCAATTTTAATGGCTGCTTC +AACAGCTTCAGTACCAGAGTTAAGGGGGAGTACTTTGTCTTTCTTAGCAAGATGACAAATTTTTTCTTCCCATTTCCCGA +GATTGTCACTATAAAGGACACGTGAAATGATAGACAACTTTGAAGCTTGTTCTGTCATCGCTTTAACAATTGTTGGATGA +CAATGGCCTTGGTTTGCAACTGAAAAACCCGAAATGCAATCTATATATTGTTTGCCATCAGTATCCCAAACTTTGACACC +TTTACCTTTAGAAATGACAAGCTTAAGTGGTGCATAATTATTAGAGCTATAATAATCAGTTAATTCAATGATTGAATTCA +TCGGTCTCCCTCCTTTATATTTATACATTTTAAAGAATTAATATTAATATATTACAGTTGTTCAATATAAGTGTCAAATA +TAAATTTAATAATATAAAGATAAGGTTGTGTTTGTATTAATGTGATTTGTAAAATGAAAGTACTAATATTTTTTGTGGAT +AATCATTGATATGTATGTATACGATTGCAGTTTAATGTAGTATGAATAAAAAATGCCACAAGCATATGTTTAACATATGA +CTGTGGCATAGTATTATGCTTGTGGAATTTTACGATGCTTAATTTTATAAATAATGAAGCCGATAATGAAACCAATCAAA +CTGAGAACAACCCAGCCCATACCAATGTCTGATAATGGTAAATATTTTTGGCTGAAATTAATCAAAGTTTGTGAGAATGA +TGTGCTTGAAATGAACTCTGGACTAGCTTTTAATCCATCTACTAATGCAGCAATCATTGTAAAGAAAATGGTACATTGAT +AAATAAGTTTTGAATGATGGAATTTGCTACTAAATAATGTTAGTACAATCAAGGCAATTGCTAATGGATATAAGAACATT +AACACTGGGACTGAGTACATAATAATCTTAGTTAAACCAACATTCGCGAATAAGAACGAAATAAAGCTTACAACTGTTGC +AATCGCTAGGTAATTCATTTTAGGGAAAAGGTGTTCGAATGTTTCTGAAAATGCCGTAATCAAACCGATGGCTGTTTTTA +AACAAGCAACCATAACGATAAGTGACAACAGGACGATACCGTAGTTACCTAAGTAGTATTGAGTAATTTGCGCTAAGGCA +ATACCACCATTTTCACTAAGTTTGAAATGACCAATACTTAATGTACCCATGATTGCTAGTAGGGTATAAATGATCCCCAT +CATAATGATACTGATAGTACCAGACTTAATTGTTTCTTTAGCGATATCAGTTGGATTTTCGATACCTAACTTTTTAATCG +TTGCAACAATGATAATACCAAATGCCAATGACGCTAGCGCATCTAAGGTATTGTATCCATCTAAAAAGCCGTTAAATAAG +GCATGTGATTGATATTGTTTACTAATAGGTGCATCAGATATGCCACCTAATGGATGGATAAAAGCAAATAATAAAATAAT +TGCTAATAATACTAAGAATACCGGATTTAAAAATTTACCGATATATTCTAAAATTCTTGATGGCTTTCTCGCAAAAAACC +ATGCAATCACAAAGAAGACGAAGCTAAAAATAAATAAATATAAAGTGATTTGCTTTGGTGATAAAAATGGCGAAAATGCA +ATTTCAAATGATGTCGTTGCCAGTCTAGGTAAGGCGAAAAATGGTCCGATAACTAGATATAAGGCAATCGTGAAAATGTA +AGCATATGTTTTATTAACACGCGATGCAATTTCAAATAAACCAGATGTCTTTGAAATGCCAATAGCAATGATACCTAGAA +ATGGTAAGCCAATTGCTGTAATTAAAAATCCTAAGTTAGCGATAAAAACGTTAGAACCAGCAGCTTGACCCAAGTGTATT +GGGAAGATAAGATTGCCGGCACCAAAGAATAAACCAAATAACATAGAACCTATAAACATGTTTTCTTTAAATGTTAGTTT +CTTCTTCATATATGTATTTGTTCTCCTTTTTAGAACTTATAAAAATCTACTTAATTAAGATAATATCAAAAATTAAACAT +AAAGTGATACATAATTTTGCAATAAATACTAAATTTTTTGAAAATTCCGGAATAAATACTGTTTTTTATGTAGTAGGGGC +GTAAGTTAAGAACGATTTAACAGATATAGAATATGTTAAATATAGAAGGTTAGACAACCCGACGTATTAATACTTTGTGA +AATAAAGATCCCCCTCAACATGATCGAAGGGGGGAAGTATATGTTTAGTTCAATATATTTAGAAAGTCGTTTGTAGTTAT +TGTTTGTCCCATCAATGGGAATACATTATCTATTGGAAATTGATGTAGCGTTTCGTTTTGTGCACTCATCATATCTGTAA +CAAAAAACTGATTGTAGTTTAATTGATAGGCATCTCGCGCTGTCGTATCTACGCCAATATGCGTTGCGACACCACCAAGA +ACAATCGTATCAATTCCTCGACGTCGCAATTGTAAGTCCAAATCTGTTCCTACAAATGCACTAAAATGTCGTTTGTCTAT +GACAAAATCGTCATCTCTCTTGTCTAATAAATGATGGAAACGACTGTAGTCGTCGCCTTCTTTTGGTGGTAATGAGATCA +TTACATTTGGTTGCAATACATCTTTACCATCATAGAAATTCACGCGAACAAAAGCGATAAAGCCATTGTTTTTTCTAAAA +ACATCTATTAATTTATTAGCGTTTTGAACGACATTTTCAGCTGTATATGGGGCATAATCCATTTTAAGAATACCTTCTTG +CAGGTCGATTAACACTAAAGCGGTTTTATTAAAATTAATCATCTTATATCAGAGTCCTATTCGTTTAATAAAGTATTCGC +TTAAACTTATACCCTGTATACGATGAAATTATTTATTTTGTTGTGAAAAAGCTTTAGCGATATCGATGAGTTTCTTCGGT +GCGTCTTCGACAGCCATTTTGACTTCGACAAAATGCATCACATCGGGATGACCATTAATTGCATTAAACGTGTCTTGTAA +ATCTTTTGATGATTCAACGTCATGAATTTCAACATTTTTACCACCAAATACAGCTGGTAAAGCTTTATAATCCCACATGT +GAATTTCATTATAAGGTTCATACATGCCGTGAATAAGTCGTTCTACCGTATAGCCGTCATTATTAATCACAAATAATACC +GGTTTAATATGCTGTCTAATCATAGTTGAAATAGCTTGAACAGTTAGTTGCAATGAGCCATCACCAATTAATAATAAGTT +ACGACGATCTTTGTCTGCTAATTGTGAACCTAATGTTGCAGGTAATGTATAGCCGATAGAACCCCATAACGGTTGCCCTA +TAAAAGTATTGTTTTTGTATAATGCTAAATCATAAGCACCAAAGAATGATGTACCTTGATCAGCAATGATGACATCATTT +GGTTTTAAGAAATTTTGCATCATTTTAAAATAAGTTTGTTGTGTTAATGGTTCTGTGCCAACAGTATAATCGGGTGATGT +TGGACGATGATACGCAGGGAACGTTGCGTTATTCGTATATGAAATATTGGATAACTGTTTTAACAATGATGGTAGAGATA +TTTCATCATTTGTAACATCGTCAATTTTGATATTGTGATGATTTAACATAACGACATCATCGATATTGAATTGGTATGAA +AAACCTGCTGTTGCTGAATCTGTTAATTTGGCTCCAATATTTAAAATTAAATCGCTGTTGTCCACATAATCTCGTATTTT +ATCTTCGGCAATTTTCCCATCGTAAATACCCATATAATATGGATTTTCCTCATTAAAAGCACCTTTTCCTAATGAAAGTT +GTGCTACTGGTATCTGTGTTTGATTTACAAAATCTTCTAATTCTTGATGGAGGTGAAAACTGTTAATTTCATGTCCAGTA +ATGATGATAGGCTGCTTCGCTTGATGCAGTTTAGTTGCTAATAACTCTATATATGTTGATGCATCCGTATATTTAGTTGC +CGTCACTTCAAATGGTGTCGGTATCTCAATTTCAGAGATTGCGACATCGATTGGTAAATGTAAATGAACTGGGCGTCTTT +CGGCGATTGCTGTATTAATTAAACGTGGTATTTCGGTTGTTGCATTTTCAGGTGTGATATAACCTTGTGCAACGGTTATA +TGTGCAAACATTTTTCGATAGTCGTCAAATGTACCTTCACCAAGTGAGTGATGTACATATTTACCGCCTTGTTCAACAGC +ACGTGTCGGCGCACCTGTAATCGCAATGACAGGTATGCGTTCAGCATATGAACCTGCGATACCGTTGACGGCACTTAATT +CGCCAACACCAAATGTAGTAACTAATGCAGCGAGTCCATTAAGACGGGCATAACCGTCCGCTGCGTAACTTGCGTTTAAT +TCATTTGTATTTCCTACCCAATCTACATTGGGATTGCTGATAATATCGTCTAGAAAAGCGAGATTAAAATCACCAGGAAC +ACCAAAAATTTTATCGACGCCTGCTCGATGAATAGCGTCAATTAAGTAAGCTCCAATGCGTTGTTTCATAAAAAAAGCCA +CCTTTTCTCTATATTTACACTTAAAATGATAGCCTCGAATATAGTGTAAGGTCAAGGAAGTTGACTTTGTTGTGAAAACG +CTAACAATAGTGAACAATTAATATTTTTTAGGAAATGAGTATGAAGGGGGGCTTTAATTGATGATCTTAAAATTCAAAAT +TTAAGGGGAGAGCGAACTATCAATGATGCTGCAAAATACAATAGCAAAGAAAAAACAGGCGCGTCACCTTATGAAGGTAT +ACGCACCTGTTTATAGTAAGCATTATTTAGCTTCAAATAATTGATCGCCAAATGAAATGTTGCCATGTTCACCTTGTTTA +AAATCAAGGTTTGTAATGTTTCCTTGTGTCACGATAATAGGCGTAATATCACTCTTTGCATGATTGCGGATGTAGTCTAA +ATCAAAGTTGATTAATAAATCACCTTGTTTAACTTCTTGACCTTCCTCAACATGTAAAGTAAAGCCTTCTCCGTTTAATT +TAACAGTGTCTAAACCGATGTGGATTAATAGTTCTAAACCACTATCTGATACAAGACCAATTGCATGTTTTGTTGGGAAA +ATCATTTGTACTTTACCGTTGAATGGTGCACGAACTTCACCTTGTGAAGGTTTGATAGCGATACCGTCACCCATCATTTT +TTCGCTGAACACTTGATCAGGCACTTCTGATAATGGTGTTACTTCACCAGTTAATGGTGCATGCACGATATGGCTCAATT +CGCTTGTTGCAGATTTATCTTCTGCAACAACAACAGTTTCGTCTTTATCGTCTTCCATAGTAGTAGGATTTTCTACTACT +TGACCATTCATAATCTGTTGCATTTCATGTTTGATTTGGTCAGATTTAGGACCAAAAATTGCTTGCATATTATTGCCGAC +TTCTAATACACCAGATGCGCCTAAATCTTTCAAACCAGGAACATCAACTTTAGATTTGTCGTTAACTTCAACACGTAGAC +GTGTGATACAAGCGTCTAAATGTTTAATGTTTGCTTTGCCACCCATAGCTTCTAATACTGCATATGGTAATTCAGTTGCT +GAAGCAGTAGCCGCTTGTGATTGTTTATCTTCACGACCTGGTGTTTTGTATTTTAATTTTACAATTAAGAATCGGAATAC +GAAGTAGTAAATAACTGCGTATACAAGACCTACAGGAATGACTAACCACCATTGTGTCTTATTAGGTAGTATACCGAGTA +AGAAGTAGTCGATGAAACCACCTGAGAATGTATAACCTAGATGAAGATCTAATAAGTACAATGTTAAGAATGATAAACCA +TCAAGTACTGCGTGAATAAAGAATAATAATGGTGCTACAAATAAGAATGAGAATTCTAATGGTTCTGTAATACCAGTTAA +GAATGATGTTAAAGCAGCAGAACCCATTAAACCTGCTACTACTTTCTTATTTTCAGGTTTAGCTGTGTGATAAATTGCTA +AAGCTGCTGCAGGTAAACCGAACATCATAACAGGGAATTCACCTTGCATGAATTTACCAGCTGTCAAATGTGCGCCTTCA +CGAATTTGTTCGATAAAGATACGTTGGTCACCGTGAATAATTTCACCAGCTGCATTTTTCCATGAACCAAACTCGAACCA +GAACGGTGCGTGGAAAATGTGATGTAGACCGAATGGAATTAATAAACGCTTGATGAAACCAAATAAGAATACGGCAACAC +CAGTATTTGAATCTAATAATCCTGTACTGAATGCATTTAATCCTGATTGAATCGTTGGCCAAATTAATGCCATTGGGAAT +GCTAAAATAAATGATGTTGTAGCCATCATAATAGGTACGAAACGCTTACCAGCGAAGAAACCTAAATAAGATGGTAAGTT +AATGTTATAGAACTTGTTATAACACCAAGCTGCCAGGGCCCCGATTATAATACCGCCGAACACACCTGTTTGTAATGTTG +GGATACCTAAAATGCTAGCGTAACCACTCGCTGGATCACCAATATTCTTAGGTGTAACTTGTAAAAAGTCGCCCATTGTT +TTGTTCATGATTATGTAACCGACGAATGCTGCGATAGCTGCTACGCCATCACCGCCAGCTAATCCGATTGCGACACCTAA +TGCGAAAATCATAGGCAAGTTATCAAAAATGATACCACCAGCACCTGTCATTAATTTAGCGACAGTTTGTACGCCACCAT +TTTGTATAAACGGCAAGTAGTGTTGTAATGATTCACCTTGCATAGCTGTACCGATAGCTAATAACAGACCAGCTGCTGGT +AAAATCGCAACAGGTAACATTAGCGCTTTACCAATACGTTGCAATTGACCGAAAAGTTTCTTCCTCACTTGTCCAACCTC +CAAAGTTGTATTTATTATTTAAGATTCAATAAAAAAAGACACGAGCAAATAGACTGTGTTGGTAACCATCACAGCTTAAT +TTAACTCATGCCTAATCTTACTTAGTAACACGTTGGTGTATGTAATTAATGTAAAAGAAGCAATTGACTAAGTAGTATAT +AAAACTGATACGTTAGTTTATCTAGCTTACCATCACATCTTATTGAATCTTATTTGTTTGCGACTCCTATTTTAGCATGG +TGAAGTATGCGTTTTCAATACAATTATTAAATTCAATTTCAAATCTCTTAATTGTTCACAGATTATTATTAAAACATAAT +ATAAGATAATCCTGAATAAAATGTATAAAAGTTATTGTAATAATCCAGCAGTAGCAGCTATTATCAGCACTTAAAAGAAA +GGATAATCTTTGATGGTGTAATAATTAACTTGTGACAGAAATATGTAAGATTATTACAATGAACAGGCTTTAACTGATTA +ATATTGTAATGTGTTTTCTCTTATAGTAAAATGTAAGCGATTACACAAATCAACTATATTTTCCTAATAATTATATTGTT +AAGGAGGGCTACTTTGACAGGCTTTTCAGTGTATTTAGGACAACCTTTAGATGAAGCGTATATTAAGCGAATGATTAAAC +AAGGTTACCAAATGATTTTTACATCTGTACAAATACCAGAAGAAGATGACGAGACAAAATATCATTATTTCACAAAACTA +CTCAATTTATTAAAACATGAACAAGTGACTTACCTCATAGATGCTAATCCATCTATTTTAACACCATCTTTTTATGACCA +TCTTCGACAATATGATGCACAATTTATGATTCGTATCGATCATAGTACATCAATTGAGGCAATCGAAGCGATAATGGCAC +AGGGTTTAAAGTGCTGTTTGAATGCAAGTATTATTTCCCGGGAATTGTTAACAAGCTTACATCAACAATTGAATGATTTT +ACATTACTTTCATTTTGTCATAACTATTATCCAAGACCAGATACGGGATTATCTGTTGACTTGGTCAATAAGAAAAATGA +ACTCATTTATCAATTTAATCCAAAGGCACAAATATATGGTTTTATTGTAGGGAGTGATTTGCGAGGTCCTTTGCATAAAG +GCTTGCCAACAATTGAAGCAACGAGACATAGTCATCCTGTCGTTGCAGCTAAATTATTACAAGAAACTGGTGTATCTGAA +GTGTTAGTTGGAGACTCATTGATTGAAATGAGGCAGGCAAAACAACTTATAGATTTTTGCAAGCATAGGCATTTCACGTT +ATGTATTGAAGAAGTGTTTGATACGACAGTGACTTACCTTTTCGATATGTGTCATAAAGTACGCCCGGATAATCCGGAAA +ATGTCATTCGTTCGGAAACGTCAAGACAAATATGTCCACATTCGATTCAACCACAGTTTACGACGCAACGACGCATTGGT +TCAGTAACCGTTGATAATTTGAATAACGGACGTTATCAAGGCGAAATGCAAATTGTGAGACAAACGCTTAGTGCACATGA +CAATGTGAATGTTGTTGCACAAATTATTAAAGAAGACTTACCACTGTTAAGTTGTATCGAGCCGAATGATACATTTGATT +TTCAAAAAACTAGGGAGTGTAAGAAGTGATGGAAAATAGTACGACCGAAGCGCGTAATGAAGCGACGATGCATCTTGATG +AAATGACTGTGGAAGAGGCTTTAATTACGATGAATAAAGAAGATCAGCAAGTCCCGTTAGCAGTTCGAAAGGCAATACCA +CAATTGACAAAAGTAATTAAAAAAACAATTGCACAGTATAAAAAGGGTGGACGATTGATTTATATCGGTGCAGGTACAAG +TGGAAGGTTGGGTGTCTTAGATGCAGCGGAGTGTGTACCTACATTCAATACTGACCCTCATGAAATTATAGGTATTATTG +CTGGTGGACAACATGCTATGACGATGGCTGTAGAAGGTGCGGAAGATCACAAAAAATTAGCGGAAGAAGATTTGAAAAAT +ATAGATTTAACATCAAAAGATGTCGTTATAGGAATTGCCGCGAGTGGCAAAACGCCATATGTTATAGGCGGTTTAACATT +TGCTAACACAATCGGTGCTACAACAGTATCTATTTCATGCAATGAACATGCAGTTATAAGTGAAATTGCGCAGTATCCAG +TAGAAGTTAAAGTTGGTCCAGAAGTATTAACTGGTTCAACACGTTTAAAGTCTGGTACAGCACAAAAGTTAATTTTAAAT +ATGATTTCAACCATCACAATGGTTGGTGTCGGAAAAGTTTACGATAACCTCATGATTGATGTTAAAGCAACCAATCAAAA +ACTGATCGACCGTTCAGTGCGTATTATTCAAGAAATATGTGCTATCACATATGATGAAGCAATGGCGTTATATCAGGTAT +CTGAGCATGATGTGAAAGTTGCGACAGTTATGGGTATGTGTGGCATTTCTAAGGAAGAAGCAACAAGACGGTTATTAAAC +AATGGTGACATTGTTAAACGAGCAATCAGAGATAGACAACCTTAGGAGGGATTTAAATGACCAAAGAACAACAACTTGCA +GAACGAATTATTGCTGCAGTAGGTGGTATGGATAATATAGATAGTGTCATGAACTGTATGACACGTGTGCGTATTAAAGT +ATTAGATGAGAATAAAGTAGATGACCAAGAACTAAGGCATATTGATGGTGTCATGGGTGTTATACACGATGAACGCATTC +AAGTTGTGGTTGGACCTGGTACAGTCAATAAAGTGGCTAATCATATGGCGGAATTAAGTGGTGTTAAACTAGGTGACCCA +ATACCACACCATCACAATGATAGTGAAAAAATGGACTATAAATCATATGCAGCTGATAAAGCAAAGGCGAATAAGGAAGC +GCATAAAGCAAAACAAAAGAATGGTAAGTTGAATAAAGTATTGAAATCAATTGCCAATATCTTTATACCGTTGATTCCTG +CATTTATTGGAGCTGGATTAATTGGTGGTATTGCAGCAGTACTGAGTAACTTAATGGTGGCAGGCTATATTTCAGGTGCT +TGGATTACGCAACTTATAACAGTATTTAATGTCATTAAAGACGGTATGTTAGCATACTTAGCTATTTTCACTGGTATTAA +TGCGGCTAAAGAATTTGGTGCGACACCAGGACTTGGTGGCGTGATTGGTGGTACAACGTTATTAACGGGTATTGCTGGTA +AAAATATTTTAATGAATGTCTTCACTGGAGAACCATTGCAACCTGGACAAGGTGGGATTATTGGCGTTATTTTTGCCGTT +TGGATTTTAAGTATTGTCGAAAAGAGATTACATAAAATTGTGCCAAATGCGATTGATATTATTGTAACGCCGACTATTGC +ATTGTTGATTGTAGGACTATTAACTATCTTTATCTTTATGCCATTAGCAGGTTTTGTTTCAGACAGTTTAGTTTCAGTAG +TTAACGGAATTATTAGTATTGGTGGCGTATTTAGTGGATTTATCATTGGTGCAAGCTTCCTACCGTTAGTTATGTTAGGG +CTTCATCATATTTTTACGCCAATTCATATAGAAATGATTAACCAATCAGGTGCTACTTACTTATTGCCAATTGCAGCGAT +GGCTGGTGCTGGACAAGTAGGTGCCGCATTAGCACTTTGGGTAAGATGTAAACGCAACACAACATTACGTAATACTTTAA +AAGGTGCATTGCCAGTTGGTTTCCTAGGTATCGGAGAACCATTAATCTATGGTGTGACTTTGCCATTAGGTCGACCTTTC +TTAACTGCTTGTATTGGTGGTGGTATTGGTGGCGCTGTAATAGGTGGAATTGGACATATTGGTGCCAAAGCAATAGGCCC +AAGTGGTGTGTCACTATTACCATTAATCTCAGATAATATGTATTTAGGTTATATTGCAGGATTACTTGCTGCGTATGCTG +GTGGATTCGTTTGTACATATTTATTTGGAACGACAAAGGCGATGCGACAGACAGATTTGTTGGGTGATTAATGATGACAA +ATATTTTATATCGCATTGATAAGCAGTTGAGTGATTTTACGAAGACAGAAAAGATAATCGCTGATTACATTTTAAAGAAT +CCACATAAAATCATTGATATGACTGTGAATGATTTGGCAGATGTTACGAATGTTAGTACAGCATCAATTGTTAGATTTAG +TCGGAAAATGACACATCAAGGTTTTCAAGAGCTAAAGATTGCGATATCTCGATACTTACCCGAAGATATTGCAACCAATC +CACATTTAGAATTGATTGAAAATGAATCTGTAGAAACTTTGAAAAATAAAATGATTGCTAGAGCAACGAATACGATGCGA +TTTGTAGCTACTAATATTATGGATGCGCAAATTGATGCAATTTGTGATGTGTTGAAAAATGCCAGGACAATATTTTTATT +TGGATTTGGCGCATCGAGTTTGACTATTGGTGATCTTTTTCAAAAGTTATCTCGTATTGGCTTAAATGTCAGGTTATTAC +ATGAAACGCATTTACTTGTGTCAACATTTGCGACGCATGATGATAGAGATTGCATGATTTTTGTGACGAATCAAGGTAGT +CATAGTGAATTGCAGTCAATTGCACAGGTGGCCACACATTACAGTATTCCCATCATAACTATATCTAGTACAGCTAATAA +TCCAGTGGCTCAAATTGCAGACTATGCATTGATTTATGGCAGAACTGATGAAAATGAAATGCGTATGGCGGCTACAACGT +CACTATTTGCACAGTTATTCACGGTAGATATATTGTACTATCGATTTGTAGCATTAAATTATCATGCGATTCTAGATTGT +ATAACCCAATCGAAAATGGCACTTGATAATTACAGGAAGCATCTTGCGACGATAGATTTTAAACATTAGATATTGTGTGC +GTTCTAACATAAAGTTAGTTGCAACGTAAACAATTGAAGTTTTTGATGTGATTCAGTTGCTTGATGAAAAATTGTTGCTA +CAAGTTTGTAAAACTGGCATAGAGGCTATTTTGAAAATGATAAAGGCGTGACAAATTAAAGCAAAGTTCAGATTTGAGCT +TTGCTTTTTTCATAGATGGAAATTAAATGCTGAAGAATAATTTTAATGCTAAATGAACTAATACGAAGGTGCTAAAAGCA +ATTATTATCAAATTAGTGATATGAATGAAATGTTTAAATCGTTTGCGTTGATTGTATTCGGGACTTTTATAGAGTAGGTA +GTAGTACATAAAGAGTATGGCTGCGAACATGCCGAATATTGAAGTGAAGTTACCTACAGTGACTGCCATGTAATGATTTA +AAGTTAAATAGTAACTTAGACTAATAATAAAAAATAAAATGATTTGATAGGTAATTAACACCATTCAAGTCCCTCCATTA +ATCGTAGACAAAAAATTTATCAATGATTTGATTATTTGTATTCAAATTTTAGTATACGACTTACCTCAAAGACAAATATT +AAAAGATAAGACAATAAAATTAGAATAAATTTACGTTTGAAGATAAAAAACAAACATTTTTCATTAAAGTTTATGATATA +TTTAGAGCAATAGAAAGTGTATGGAAGGGGATATGAATGGCATACCAAAGTGAATACGCATTAGAAAATGAAATGATGAA +TCAACTTGAACAATTGGGTTACGAAAGAGTAACGATACGTGATAATAAGCAATTGCTTGATAATTTTAGAACGATTTTAA +ATGAGCGTCATGCGGACAAATTAGAAGGCAATCCCTTAACAGATAAAGAATTTCAACGTCTGTTAACGATGATTGATGGA +AAAAGTATTTTCGAGAGTGCCCGTATTTTACGTGATAAATTACCACTTAGACGTGATGATGAGTCTGAGATTTATTTGTC +GTTTTTAGATACGAAAAGTTGGTGTAAAAATAAGTTTCAAGTGACGAATCAAGTATCTGTCGAGGATACATATAAAGCAC +GTTATGATGTAACGATATTAATCAACGGACTACCCCTTGTCCAAGTTGAATTGAAACGTCGAGGTATTGATATTAATGAG +GCGTTTAACCAAGTAAAACGTTACCGCAAACAAAATTACACAGGCTTATTCCGCTACATACAAATGTTTATCATTAGTAA +TGGTGTTGAAACGCGATACTTTTCTAATAATGATAGCGAACTATTGAAGAGTCACATGTTTTATTGGAGTGATAAACAGA +ATAACCGTATCAATACATTGCAATCGTTTGCTGAGTCATTTATGAGACCTTGTCAATTAGCTAAGATGATATCGCGCTAT +ATGATTATTAATGAAACAGATAGAATACTGATGGCAATGCGTCCGTATCAAGTGTATGCGGTAGAAGCACTTATTCAACA +AGCGACTGAGACAGGGAATAATGGATATGTATGGCATACAACTGGAAGTGGTAAGACGTTGACTTCTTTTAAAGCGAGTC +AGATTTTATCACAGCAAGATGACATTAAGAAAGTTATCTTTTTGGTTGACCGTAAAGACTTGGATAGTCAAACAGAAGAG +GAATTTAATAAATTTGCTAAGGGTGCTGTAGACAAAACTTTTAATACCTCGCAACTGGTACGCCAACTAAATGATAAAAG +TTTGCCACTTATTGTAACGACGATTCAAAAAATGGCTAAAGCGATTCAAGGGAATGCCCCTTTATTAGAACAGTATAAAA +CGAATAAAGTTGTATTTATTATTGATGAGTGTCATCGCAGTCAATTTGGTGACATGCATCGTCTAGTTAAACAACATTTC +AAAAATGCCCAATACTTTGGATTCACTGGTACGCCACGTTTTCCAGAAAATAGTAGTCAAGATGGTAGAACAACTGCAGA +TATTTTCGGTAGATGCTTACATACGTATTTAATTAGAGATGCCATTCATGATGGTAATGTACTTGGTTTCTCAGTTGACT +ATATTAATACTTTTAAAAATAAAGCTTTAAAAGCAGAAGATAACAGCATGGTTGAAGCAATTGATACGGAAGAAGTATGG +TTAGCGGATAAACGTGTGGAATTAGTAACACGACATATCATCAATAATCATGATAAATATACACGTAATCGTCAATATTC +AAGTATATTTACAGTCCAAAGTATTCACGCGCTTATTAAATATTATGAGACATTTAAGCGACTTAACAAAAAGTTGGAAC +AACCGTTAACGATAGCTGGTATATTTACGTTTAAACCTAATGAAGATGATCGTGATGGTGAAGTGCCATATCATTCACGT +GAAAAATTAGAGATAATGATTAGTGATTATAATAAAAAGTTCGAGACGAATTTTTCAACAGACACAACTAATGAGTATTT +TAATCATATTTCAAAAAACGTTAAAAAGGGCGTTAAAGATAGTAAAATTGATATCTTAATCGTTGTTAATATGTTCTTAA +CTGGTTTTGATAGTAAAGTACTGAACACTTTATATGTTGATAAGAATTTAATGTATCATGATTTAATTCAAGCGTATTCA +CGTACAAATAGGGTTGAAAAAGAATCAAAGCCATTTGGTAAAATTGTAAACTATCGTGACTTGAAAAAAGAGACAGACGA +TGCACTGAGAGTATTCTCACAAACAAATGATACGGATACAATTTTAATGCGCAGTTATGAAGAGTATAAAAAAGAATTTA +TGGACGCTTATCGTGAGCTTAAAATGATTGTGCCGACACCACACATGGTTGATGACATTCAAGATGAAGAAGAGCTAAAG +CGCTTTGTTGAAGCTTATCGTTTATTAGCTAAAATAATATTACGTTTAAAAGCATTTGACGAGTTTGAGTTTACAATTGA +TGAAATTGGAATGGATGAACAAGAGAATGAAGACTATAAAAGTAAATATTTAGCTGTGTACGATCAAGTAAAAAGAGCGA +CGGCTGAGAAAAATAAAGTATCCATTTTAAATGATATTGATTTCGAAATAGAAATGATGCGTAATGATACGATTAATGTG +AATTATATTATGAATATATTGAGACAAATTGATCTTGAAGACAAAGCGGAACAACGTCGTAACCAAGAACAAATTAGACG +CATTTTAGATCATGCAGATGATCCGACATTGAGGTTAAAACGAGATCTAATTAGAGAATTCATCGACAATGTTGTACCTT +CTTTAAATAAGGATGATGATATCGATCAAGAATATGTTAATTTCGAAAGTATTAAAAAAGAAGCGGAGTTCAAAGGATTT +GCTGGAGAGAGATCTATCGATGAACAAGCCCTAAAAACAATTTCAAATGACTACCAGTATAGTGGTGTTGTAAACCCACA +TCACCTTAAAAAAATGATTGGTGATTTGCCATTGAAAGAAAAGCGTAAAGCAAGAAAAGCCATTGAATCTTTCGTGGCAG +AAACAACTGAAAAATACGGTGTGTAATGATTCAGCCCCCTCGCTAGATTAGTGTAGGGGGCATTATTATTTTCTATCTTT +TGTTAGTTACCATGTGTTTGGCAACCATCATTTGGTTTCAATACAATTTTATAATCGTATGTGTATAGAAATAATGTCTA +TAGGCATTTAAAATAGTTTATAATATTAACAGGATAAGCATTAAATGTATGTTGAAGGAGGTGCGTAGCTATGGATAATA +GAAATATGATTAATCGTGTTTTTAGTCAAAAGATATTACATCAAATTGCAATCAAAAATAAAAGTGATGTTGTTGATGAG +GCATATGATTTTTATATACAGGGACCTAAAAATATCAATGTAATACAGAAGATGAAATCTTTATATAACTATCTTAAAAA +GTCTTATCGTAACGAATATTTTTACAAAAACACAATACTTAATAAACTCCTTCTAGGACGACATTCTATTAATACAACTA +CTGCACTTTCTGAGATGCCCATAGGGAAAAGTATTGCTGATTTTATATTGTTAAACGGCAAAGGCGTTGTCTATGAGATT +AAAACAGAATTAGATAAGCTGGATAGATTAGATAATCAAATTAATGATTATTATGAAGTGTTTAATTATGTCGTAGTTAT +TACAAATGACAAACATCTGAATAAAGTTATGGCTAGATACAAAGATACAACAGTTGGAATTTTAGTGTTAACAACTAGAA +ATACACTGAGTGAAGTTCAAAAACCTAAAGAAAACAATAGTCTCTTAAACACAAAAGCGATGTACAACTTTTTACGAAAA +GAAGAAAGAAAAAGAGTTATTGCACAAAATCATATGGATGTGCCAACTTATAATGATTTCACAGAGTATGATGTGTTATT +TGACGTGTTTAAAGAAATACCAATGACGAAACTGCATAACAATATGATTTCTGAGTTGAAAAAAAGAGGAAACATGAAAG +AATACAAAGATGAATTTTTAGCAGCGCCGGCTGAAATTAAGTTCTTGTTATATTTCGCAAAAATGACAAAGAAAGATAAA +AATAAACTATATCATTTTCTTAAGGAGGATTAATATGTATTATCCTTATTTGCGTGGGAAACAAAATGAACTATTTGCAA +TTAGAGAATTGTTAGAGAAAGGTTTGATTGGTGATTGTATTCAACCTATAATTGAACCAATTAAATATACAACCACATTT +AAAAATACTTTGCAATACTGTGGTGAAAAAGCATTCTCTATAAATTTAGTAGTAAATTCAAAGTTAACTGAAGAAGAGAT +TAGTAACGAAACTGTTGCACATTTAACTGAAATAATAACAAAAAACAAAAGTGTTATTCAAAAAGCTTACTTGGGTCCTT +CTGATGAAGGCAATGATAGGTTGAAACAGCAATTTTCAAGTAATAGTTTAGCTATTTTAACAAGTGTAGATGATTGGGAA +ATGTTTGGAGATAAAAATAAACTTGAAATGGTTTTTGTACCAGATGATAGACACATTAAACGTAAATTGCGTAATATTCC +AAACAAAGGCATCATTATGGATCCTTTTAATAAACTAAGTCGTAATGTTGATTATTTAGATAATGATGACGAGTTTTATA +GCGACGATCACCTTTATTATAAGGAAGATGGATACGTAGCATTTTCAGACTATTCTGTTATAGGTGGAGAATATGTAGAC +GGTGGCTTTTCGCCATTAGCTATTGCGATACATATTGTCTATTTTGATGAGGCTAATGAGCTAAGAGTTAAGCATTTTGT +CTCTGATTCTAATAATGATAGATCAAATCCAGGTAAAAAGTTTTTTGAGGCTGTAGATAAATTAGTAACATGGTCAAAAA +ACTTAGATATTAAAAATAGATCTTATGCGCTTGGACAATTTGAAGAATTAAATGAAAATAATAAGTATCCAGGATTAGGT +TTAATTAAAAGGTTATCTATCATGCATCACCTAGAAATTATGAATAGATACTTGGAGTCTCAAAATGAAAATATGTGAAA +AATGTTTTAATAATACTGAAATCGTAGAAATCATTGCAAATGATAATAGCAAATTTGACAATTGTGATATTGATAACAAT +CATCTTGGTGTTAAAATATTTGATACGACTAAGGACATAGATAAATTAGAACTTATTAGAGATTATTTAAGACCAGCATT +AGAATTATATGATATTAGTATAAATTTACCAGATACTTTTAGCCCAAAAGAAGGTAAAAAGATTGAAATAGCATTAAAAG +ACGACTGGAGTATATTTAATGTTGAGGAAGCTCAAATAAGTTGTATTTTAAATGAACTTTTTAAAGATGATGAAAATTTA +GATAGACGAGTGTTGGAAGATTTAGTAGGCGCTAAAATCATAAACGATAAAAAATATACAAATAAAAATCTAATTGTAGC +AAATAATGACTGGGATGGATTTTGTGAGAGCTTGAAGTATAAAAATAGATTTCATAAAAATATGATCAATTTAGAGAATT +TAGCGTTCTTTCTCGATATCACTACTAATTACTATAGTATTGAGGAATTTAGAGAAAGATTTGAACCATTATATAGATCT +AGAATAGTTAAGTTCATTACAGAGAAAAGTGAACTAATGTCACCACCAAAGGGATTTGCAACGGCAGGAAGATTGAATTC +TAAATGGATAAGCGTATTATACCTTAGTACAAAAGAACAGGTCAGTATAGAAGAAGTTAAACCTAAGCATAATGACATTA +TTTATATAGGTAAGGTTAAGTTACAAAAAAACGTGACAAAAGAGAAATTGAAAATTGCGAATTTAACTAATTTAACTAGT +AATGCAATCAAAGCTGGCGATGATGGATTTAGAAAGTATTTTGTAAATTACCAAACATTAAAAAAGATTCATAAAGGAAT +AACAAATCCAAGCGATGAACAAGGAATAGATTATTTGCCATTTCAATATTTAGCTGATTATATTCGAAGCCTTAAATATG +ACGGGATAATGTATGAGAGCATATTACAAGATGGTACTTTTAACTTTGTGTTTTTTGATCAAAGTTTATTTGAATGTGTT +AATTATGAAAGAAAAAGAGTAAGTGACGTTAAATATACATTGACTAAGCTAAGTTGACACACTCAAAAATCTGAATATTC +TAATAAAATATAAATAATCCCCCACACTTCACATAGTGGCGGGGGATTTATTCTGGCGTTAAAATGCCCAAAGTATGGGG +GTGACTCTTAAATCATGGCAAAGCACCTAAAACTTGATATGGTGCCGAGCCCTAACTTAGCACAATATAGTTAGTATGAC +AAAGTCATCTAACATAACACATCAGAAGTCGCGCAATTTATTTCAGACCATACAGTGATAAAGTTGCACAACGCATGACT +TTTATTTAGCAATAACTGCTACTTCTGAAATAAGTTGCTTTGCATAGTCTGACTGCGGATGTTTGATAATATCTTCTGTG +TTATTCAGTTCAACGATTTCGCCATTTTTCATAACTGCAACGCGATCACATATTTCATTGATAACACCCATGTCATGTGT +GATGAATAAATAAGTGATGCCGAAGTCTAACTGTAATTGTTTTAATAACTCGATGATATCTTTTTGAATTGAAACGTCTA +AAGCGGACACTGCCTCGTCGCAAACAATCACTTTAGGTTCAACAGCAAGTGCTCTCGCGATACTTACACGCTGACGTTGC +CCACCAGATAATTCGTGTGGATAGCGATATAAGAAAGTTTGATCTAGGCCAACCTTTTCTAACAACGATACGACAGTTTT +AATAATGTCATCATTATCTTTGACTTTCCCATGAATGATTAGTGGTCGTTTAATCACATCAATGACTTTAAATCTTGGAT +TAATAGATGCGAATGGATCTTGAAAAATCATTTGTATCTCTTGTCGTAAAGATTTCAATTCATCATCTTTAAATAAACTT +AATGGTAATTCGTTATACCAAATAAAGCCTTCTGACACTTCCTTTAGACCGACGACCGTCTTAGCTAATGTCGATTTCCC +TGACCCTGATTCACCGACAATGCCTAATGTTTCGCCTTTTCTAATAGCCAAGTTAATATCATTAACTGCTCGGTATAGGC +TGCCACTCGGTGATGTGTAATCCACGCTCACGCGATCGAATTTTAATAAAATATCATTGTTTAACGGTCTTGGCGGACGC +GTTTGATGAATATCAGGAATCGCATCTATTAAGCGTTTTGTATAGGTATGTTGTGGCGATTTAAAAATACTTTCAACCGT +GCCACTTTCAACGACACTTCCATCTTTCATTACAATCACATCGTCGCAAAATTGATACACAGCGCCTAAATCGTGAGTGA +TAAAAATAATAGATGTTTCTGTGTACTCATAAAGGGACTTCATTAACTGCAGTAATTGATTTTGTGTACTGGCATCTAAT +GCCGTTGTTGGTTCATCTGCGATTAAAATTTGTGGCTTTAAAATCAATGCCATTGCTATCATGACACGTTGACGCATACC +ACCAGAAAGTTCATGTGGATAAGCATCAAATTGTCGAGTTGCATGTTTTATACCTACTTTTTCTAAAATGTCTATTGTCA +TCGACTTTGCTTCAGATTTAGATACACGTTTATGTTGAAATATTACTTCTGTAATTTGTTTGCCAATCGTTAATCTTGGA +TTCAACGAAGAGAGTGGATCTTGAAAAATCATTGAAATATCCTTACCTCGAATTTGTTGTAACGCTGAAGTTGATAAATT +ATTTAACGATTGCCCATTAAAAATAATTTCTCCTGTTAATGTGTGATCTGGATAATCTGGTAGTAGCCCTAAAATAGATT +TAGCGGTAATACTTTTTCCTGATCCTGATTCACCAACAATACCTAGGATATGTTTTTTTCGTAATTCGAAAGAGACGTTT +TTTACCGCTTGAACTGTAGTTTCATCATAATTGAATTGTACATTCAGACTGTTGACTTCTAATAAATTTGACATGATGTT +GTGCCTCCCTATTCACAAATGTGTAAATTTATTCTATACTTTTGTATATCATAGTAATCTACTCAAGATTATTTTACAAG +GAGTGATTATCATAATTACTTATTTAGAATCTTTTAAGTCGTTGTATCACAAGGCATTTTACAAGTTTGTATTATCGGTA +TTAAGCTTGCCAATATTTTTATATGCAGTGATAAAGTTTTTCTTTTCTGCTAAGAGGAAAAATTTTTACGCTAATAATTC +TGAAATTTCAGAAATTGAACAGGCGTTACATCAAAAATATAAATATTTATCGCAGCAAAAGTCATCCACACAAATACATA +AAGAAGCATTAAAAATATTCAAGGCACAAAGTTCTAATACGAGTTCAAAGAATATTGAACAAGCACATTTTTCAACATAC +TTTGAAAATGTATTATTTCATAAGTTCATCATGATCAAAGTGATATTGGCCTTGCCGATGTTCATCTTATTGACTTTTTA +TTTACAGCCATTAGTTAGATATATTTTTGAACGAATTGTCATGGCTGTGATTGTCATCATTGGTGTTATTGTCAGTGTGT +TTACCATTCTGTATTTTTCACCGCTTGATGCGGCTTATAGCATACTGGGACAAAATGCAACAAAGGCACAGATACATCAA +TTCAATGTATTACATCATCTTAACGAACCTTATTTTATTCAATTGTGGGATACCATCAAGGGTGTTTTTACCTTTGACTT +AGGTACGACTTACAAAGGGAATGAGGTTGTGACTAAAGCAGTTGGCGAAAGAATTCCAATTACAATAATTGTCGCAGTAT +TAGCGCTAATGGTGGCATTAATTATTGCAATACCAATTGGTATTATCAGTGCGATGAAGCGAAATAGTTGGCTTGATATC +ACGTTAATGATAATTGCATTAATTGGTTTATCTATTCCAAGTTTCTGGCAAGGGCTATTATTCATTTTAGCGTTCTCATT +GAAATTGGATATTTTGCCACCATCTTATATGCCAGAACATCCAATATCGTTGATTTTACCTGTACTTGTCATTGGAACAA +GTATTGCTGCTTCTATCACGCGTATGACAAGGTCTTCTGTACTTGAAGTAATGCGCAGCGATTATGTTTTAACTGCTTAT +GCAAAAGGATTATCGACGACACAAGTTGTTATTAAACATATTTTGAAAAATGCCATTATTCCAATTGTAACGTTAGTTGG +TCTTCTAGTGGCAGAGTTACTAGGCGGTTCAGCAGTGACGGAACAAGTATTTAACATTAATGGTATCGGGCGTTATATCG +TCCAAAAACAACTAATACCTGATATTCCAGCAGTCATGGGTGGGGTCGTATATATATCAATTGTAATATCTTTAGCAAAC +TTAATTATTGATATATTTTATGCTTTAATCGATCCAAAATTACGTAGTGAAATCAACGAAAGGAAGTGAGGCATATGGCA +CAACTTAATTCAAAGATAGCTTCCTTAAAATTATTCGCAAGTTACGCCATAGCAACTTATATTTTAGTTATATTAACGAG +TGCATTAAATCTTTTTAAAGGTTATGTGGCCGATACGTTCTATATTGCTGAAACATTGCTAATCGTTTTAACCATCATTT +TAATTATTATTTTAACAACGGAACAAACATGGAAGCATCATGACCTATGGCGACGTATCGTCGAAGTGTTGTTATTGTTG +ATGACATTAACAGGCAACGTATTTACATTATTAATGTTTGTAAGTATTAGACGTTACCAACGTACATCGCAAATACATAG +TTATAACGGGTGGGAATCGTTTATACGAAAAACTACTAGACATCGTATTGCGATTATCGGGTTACTTATTTTAGTCTACA +TGCTGACATTATCAATTGTGTCACAATTTACATTTGATACGACATTGGCTACTAAAAATCAGTTCAATGCACTGTTACAT +GGACCGAGTCTAGCCTATCCGTTTGGTACTGATGATTTCGGTAGAGACTTATTTACACGCGTAGTTGTAGGAACGAAGCT +GACATTTTCAATTTCAATTATTTCAGTAGTTATTGCAGTTATTTTTGGTGTGTTACTAGGCACTATCGCAGGTTATTTTA +ATCATATTGATAATTTAATAATGCGAATTTTAGATGTAGTGTTTGCAATTCCATCATTATTGTTAGCGGTGGCAATTATT +GCATCATTTGGAGCAAGTATTCCAAATTTAATTATTGCTTTAAGTATCGGTAATATACCATCATTTGCACGGACAATGCG +TGCCAGTGTTTTAGAAATTAAACGCATGGAATATGTAGATGCAGCACGTATCACTGGTGAAAACACTTGGAATATCATAT +GGCGTTATATTTTACCGAATGCGATTGCGCCTATGATTGTACGTTTTTCATTAAATATAGGTGTGGTTGTATTAACAACA +AGTAGTTTAAGTTTCCTAGGACTTGGTGTTGCACCTGATGTAGCTGAATGGGGCAACATTTTACGTACCGGTAGTAACTA +CTTGGAAACGCACAGTAATTTAGCTATTGTACCTGGTGTTTGTATTATGTTCGTCGTTTTAGCATTTAATTTTATAGGTG +ATGCAGTGCGTGATGCACTAGATCCAAGAATTCATTAAAAAGGTAGGGATAGATGTGAAGAAAATCATTAGTATCGCAAT +TATAGTTTTAGCGTTGGTATTAAGTGGTTGTGGTGTCCCTACGAAATCAGAAGTGGCTCAAAAGTCATCGAAAGTTGAAG +TGAAAGGCGAGCGACCAACAATACATTTCCTAGGACAAGCAAGTTATGAAAATGATATGAATATCGTTAAAGATCAATTG +GAAAATGCAGGATTTAACGTGAAGATGAATATCCAACCAGATTATGGTAGCTATCGTACACAACGTCAAGCCGGCAATTA +TGATATCCAAATTGATGACTGGATGACAGTGTTTGGTGACCCGAACTATGCTATGACGGCATTATTTAGTTCTACAGGAT +CAAATAGTTTATTGAAAGATAAACATGTAGACCAGTTGTTAAATAAAGCTTCTACTCAAAATGAAGCAGATGTTAAACAA +ACATATAAGCAAATTGAAGATGAAGTTGTATTTGATAAAGGGTATATGGCGCCTTTATATGGATCAAAAAAGAATTTAGT +ATATGACAATAAAGTGTTAGATAAAAATAGTGTTGGATTGCCAAATTCACGTGCATTAATATGGCAACAATTTGATTACA +ACAATAGTAGAGAACGAGATACGCGGCCACTTGTGATGACACAACAAGATGGTGAAATTCCTACATTGGATCCAATACGT +TCAATTGCGCCGTCAGTATATTCAATTAATATGAATATGTACACAAGGTTATTATTATTAGATGAAAATGATCATTTAAC +AACGAAAGGTTCGTTAAGTCATGATTATGCTGTGAATAAGGACAATAAAGCATTTTATTTCTTGTTAAGAGATGATGATT +ATTTTGCGAAAGTGGTCAATGGACAAGCACGTAATACTGGAGAGCGTGTATCGGCTGAAGATGTTAAGTTTTCTTTAGAT +AGAGCACGTGATAAAAAGTCTGTGCCTAACAATAATACTTACAATATGCACAAACATATAAATGACATCAAGATATTAAA +AGATGAGGACATCGATCAGTTGCGTAAAGAGAAAGACAAGGACGATAAATCAATCTATGATAAGTTGATTAAAGCTTATA +ACGTCAAATCGTTAACGACAGATGGTCAAAAAGTAAATAATAAAGACGGTATTTATCAAATTGTTAAAATTACGACAGAT +CAATCGATGCCTCGAGAGGTAAATTACTTAACACACTCTTCGGCAGGCATTTTATCTAAAAAATTTGTTAATCAAGTAAA +TCAAGAATATCCAAAAGGATATGGGGATAGCAGTACAATTCCTGCAAATTCAGATGGGAAAAATGCGCTGTATGCAAGTG +GCGCATACATTATGACACAGAAAAATGCATATCAAGCAACGTTTCAACGTAATCCAGGATTCAACGAAACAGAAAAAGGT +AGTTATGGACCAGCTAAAATTAAAAATATTACATTGAAGTTTAATGGTGACCCGAATAATGCATTGTCAGAACTTAGAAA +TCATTCAATTGATATGTTGGCAGATGTGAATCAAAAACATTTTGATTTAATTAAGTCGGATAAAAATTTAAGCATTATTC +GCAAAAATGGACGCAAGTCAGTCTTTTTAATGCTAAATATTAAAAAAGGTATATTTAAGACGCATCCAAACTTGAGACAA +GCAGTAGTTAATGCGATAGATCAGGATCAATTTATTAAGTTTTATCGTGGCGATAAATTTAAAATTGCATCACCGATTAC +ACCACTTGTCGATACTGGTAACGAGCAACGTCAAGATTTAGAAAAAGTAGAAAAAGCCATTAATCAATAATGTTTTAAAT +ATTTAACAGAAAGTAGGAGGATATAGTATGGTCATTAACTTAAATGACAAACAGACAAAAACATCTAAAGAAGGGTTAAT +TTCCGTATCACATCCTCTTGCGGCTAAAATTGGTAAGGATGTATTAGATCAAGGTGGCAACGCCATGGATGCAGTGATTG +CAATTCAACTGGCATTGAATGTGGTAGAACCATTTGCATCAGGTATTGGTGGTGGCGGGTATTTGCTATATTATGAGCAA +AGTACTGGCAGTATAACTGCGTTTGATGCACGTGAGACAGCACCTGAACATGTAGACAAACAATTTTATCTAGATGATTC +AGGCGAATATAAATCATTTTTTGATATGACTACACATGGTAAAACTGTCGCTGTGCCAGCAATTCCAAAGCTGTTTGATT +ATATTCACAAGCGTTATGCTAAATTGTCATTGGAAGATTTAATTAATCCTGCAATTGAACTAGCCATTGAAGGTCATGCA +GCCAATTGGGCTACTGAAAAATATTCGCGCCAGCAACACGCACGATTGACAAAGTATCATGAAACGGCACAAGTATTTAC +GCATGAAAATCAATATTGGCGTGAAGGTGATTGGATTGTACAACCCGAATTAGGTAAGACATTTCAAATATTAAGAGAAC +AAGGGTTTAATGCATTTTATAAAGGTGACATTGCGAAACAATTAGTCAATGTTGTCAAAGCATGTGGTGGGACAATCACT +TTAGAGGATCTAGCCAAATATGACATTCAGATTAAAGCGCCAATCAGTGCAACATTTAAAGACTATGACATTTATTCAAT +GGGACCATCTAGTTCTGGCGGTATCACGGTAATTCAAATATTGAAGTTATTAGAACATGTCGATTTACCATCTATGGGTC +CAAGATCTGTTGATTACTTGCATCATTTGATACAAGCGATGCATTTAGCATATAGTGATCGTGCGCAATACTTGGCGGAT +GATAATTTTCATGAGGTGCCTGTACAGTCATTAATTGATGATGATTATTTAAAAGCACGCAGTACGCTCATTGATAGCAA +TAAAGCAAATATTGATATAGAGCATGGTGTTGTGTCTGATTGCATTAGTCATACAGATGTTGAAGAAAATCATACCGAAA +CAACTCATTTTTGTGTGATTGATAAGGAAGGTAATATTGCTTCATTTACGACATCAATTGGTATGATTTATGGTTCAGGT +ATCACGATTCCAGGCTACGGTGTGTTATTGAATACGACAATGGATGGCTTTGATGTAGTAGATGGTGGTATTAACGAAAT +TGCACCATATAAACGACCACTAAGTAACATGGCTCCAACGATTGTGATGTATCACGGGAAGCCAATATTAACAGTAGGTG +CACCTGGTGCCATAAGTATCATTGCTAGTGTTGCGCAAACATTAATCAATGTATTAGTGTTTGGCATGGATATTCAGCAG +GCTATAGATGAACCTAGAATTTATAGTAGCCATCCTAATCGCATTGAATGGGAGCCTCAATTTTCACAATCTACAATATT +AGCATTGATTGCACATGGACATGCAATGGAACATAAACCAGATGCTTATATTGGAGATGTACATGGACTACAGGTTGACC +CAACAACGTATGAAGCTTCGGGTGGAAGTGATGATACTAGAGAAGGCACCGTAATGGGTGGCGAGGTGTTAGTGATTAGA +AAACAGCCATTACCATATCGGCAAATGTATGATAGTGACGGCTTTCGTCTATATTTCAATGATGTGCAGTTACCTTTATT +GGCAGATCAAGTGCGATGGATGCATGACAAATATTGGGTTGATGAAAGTGTTGTTAGAATCATTTTCCCTGAAGTTAGTG +CACATATTGAAGATTTAAGAAGTTATGAAAACGCAGGAGAAAATTATATAGATATTGCCTGGTTAGCACGGAAATATGCT +TATCAAGTAACATTGAAGGATGACGGTTTATACTTAACTGATGATACATATACTTCAGTGAAACGGAACACAAATGCATA +CTATAGATATGATCGAGATAGTATCACAAGATAGTTTATACGCTTGATATGAAGTTTGTAGTGGATATGAGCTTTTGTTG +AATGAAAGGATTCGATTTCGAACTTTAGCGTGACGCTTGAGTGTAGTAATGTTGGAAATGCTATACGAAACGCTAGTCAC +CTTTGTAAAATATACAACGCCTCACAAATTCGTTTTTGATGTACATCACAATAGTTAGCCTAAAATCTAATGTAATGTGG +TTTGGTACAGTTGAACGAGTTATGTGAGGTGTTTTTATAATATAGGATTTTAATAGATGATTTGGATGTTTCTATAATTA +CAGCATAAGTATTAATGTTCACTTATTTTATTATAAGTAAGGCTTTCTTTATAAATGATTTGGTCTTTTCCAGGACTTGA +AAAATAAAAATAAAGTTTCTGATCTTGATGATTATTGCCTTTTAAATCACTCATTCCCTTTAATTCCACCTTTGTTGATG +CGTTTTTAGGAATATTATATCTACGTTTTAATTGTTTAACGTTTGGATTATCGTTCGACAACTTACCAGATAAATTATAA +TAATCAGTGGTAGGATCATGTGTGACTTTCAAATCATTGCTATTTAAAATGCTTGTTAAATCACCACTTTGAATCAAAAA +TTGATTGTTTTCGATTTTTTGTTTCAGCGCGGGATCTTTTACGTCTTTTGTGAAAACGATTTTATTATTAACTACTTTTA +CTGGATAACTTTTGTATGTCGAGTCAGTAGCATTTTTTCTATCGTTTGTAGTTGTGTCATATTCACCAGTTATTTTATGT +GTGTTCTTATCTACCTTTAACAACATACGGTCTTCTTTTAAAAGCTCATCTGATCCAACAACTGAATAAGAGGATTCTAT +ATACCATGTGTCTTGATCATTATTTTCATAATGGGGATTATCGTGACCATCAATTTCATAAAGCGTTTCTAAGTTTTTAA +TAGGATACGTACTTAGTACTTTTTTAAGACCATCTTTCAAATGAATTTGTTCCCACTTCATTGCCAAAAACATATCGCCA +CTGACTACAATTGAAATAATAATAATTGCTGCTAAGTTTAACCAGAAAATTTTATGTGCTTTCATACATTCCCACCGTTT +CTCAAAATACTTCATTAACACTATAATAATATATTTTGAAAAATATTTACATCAGTATTAAAGTGAATATCAAATTTTAA +ATTTATGAAAATAATAGATATTTATAAAAAGCGGAAAAGAGATACAATAAAAAACTGCATGACGTTTGAGACGTCACACA +GTGTAACTAAAAATTTAAAAAGTTGTTGCTAATTTTTCAGCATTATTAATACTAGTTGCTTTAATTTCTTCAGTCTTATG +AGGTTCAGCATTGTGTCCTTCAATAATGATTGTTTCATATGATGGCACACCTAAGAATGTCATAATTGTTCTTAAATAAC +GGTCACCCATTTCAAAATCAGCAGCAGGTCCTTCAGTATAATATCCACCACGTGATTGAATGTGTAATACTTTTTTGTCA +GTTAGTAAACCTTGTGGTCCTTCAGCAGAATATTTAAAAGTTTTACCTGCAATTGAAATAGCATCAATATATGCTTTAAC +TACAGGTGGGAAAGAAAGGTTCCACATAGGCGTTACAAATACATATTTATCTGCACTTAAAAATTCTTCTAAAATGTCAC +TCAATCTTGAAACTTTCATTTGTTCATCATCAGTTAACGTTTCGCCATTACTCATTTTTCCCCAACCAGTTAATACATCT +TTGTCAATAACTGGAATATAAGTTTCAAATAAATCAATATGTTTCACTTCATCATCAGGATGTTGTTGTTGATATGTTTC +GATAAATGCTTTACCAGCCGCCATAGAATTTGATACCAGTTCATTAAAAGGGTGTGCTGTAATATATAATACTTTTGCCA +TTTGAAAATTCTCCTCTGTTTCTGTTATTTTCTTAAGTATAATTATTATACTCGATATAAAATTTAATATCAATCAAAAT +ATTCAAATTACCATCATTTTCTTCATCTATATTTGGCAGTACTACTAAAGTATGAGTGCATTTAATTATGAAATAGTTGA +TTTAGAATATATACTTAATACCCAAAATATATGAAGGATGGATGCCACTATGACAAAGCGACCAAAACGTATTTTGGCAA +CAATTATCATTTTTCTTTCACTATTATTTACGATTATTTATATAGATGACATTCAAAAATGGTTTAACCAATATACCGAT +AAATTGACACAAAATCATAAAGGACAAGGACACTCAAAATGGGAAGACTTTTTTAGAGGGAGTCGGATTACTGAGACTTT +TGGTAAATATCAACATTCACCATTTGATGGTAAGCATTATGGCATTGATTTTGCATTGCCAAAAGGTACACCAATTAAAG +CGCCGACGAATGGTAAAGTAACACGTATCTTTAATAATGAATTGGGCGGCAAGGTATTACAGATTGCCGAAGACAATGGA +GAATATCACCAGTGGTATCTACACTTAGACAAATATAATGTCAAAGTAGGTGATCGAGTCAAAGCAGGTGATATTATTGC +ATATTCAGGCAATACAGGTATACAAACGACAGGCGCACATTTACATTTTCAAAGAATGAAGGGTGGCGTAGGTAATGCAT +ATGCAGAAGATCCAAAACCGTTTATCGATCAGTTACCTGATGGGGAACGTAGCCTATATGATTTGTAGTTATAGAAGGGT +GCCCGCAGTCTAAAAAATTAAGCAATCATTGTGTGAGTATGATACTTACATAATGGTTGCTTTTTTCAATGAAAATCGTA +ATGCTAAGTCATACTTGTTTGATTTAGATATTACTTAAAATGTAAGACAAGGTTGTTAGCATTGGCAGTGAAATATCGCA +CATAAAAAACATTATTGTCACACTAGAAAATAGTTGTGCACTATATCAATTTTCTGTATAAAAGTTTAATTCTGACAGTA +ATGTAAACGTTTACAATTTATGATTGACATTAATAATGACTGAATATATGATTTATGTAAGTATTTGTGCAAACGTTTTC +ACAAAGTGTATTGCACAATCAAACTGTAAACAAAGTATGGGAGGCATAACATGGCAGAACTAAAGTTAGAGCATATTAAA +AAGACGTATGATAACAACAATACTGTAGTGAAAGATTTTAATCTACATATTACTGACAAAGAATTCATTGTATTTGTTGG +ACCATCGGGATGTGGTAAATCAACAACATTACGAATGGTTGCTGGACTAGAGTCTATCACATCTGGAGATTTTTATATTG +ATGGGGAACGCATGAACGATGTTGAACCAAAGAATAGAGATATTGCGATGGTATTTCAAAACTATGCATTATATCCACAT +ATGACTGTTTTTGAAAATATGGCATTTGGGCTAAAGCTACGTAAAGTAAATAAAAAAGAGATTGAACAAAAAGTTAATGA +AGCAGCTGAAATATTAGGATTAACTGAGTATCTTGGTCGTAAACCAAAAGCGTTATCTGGCGGACAGCGTCAACGTGTTG +CTTTGGGCAGAGCTATTGTTAGGGATGCGAAAGTCTTTTTAATGGATGAACCATTATCGAATCTTGATGCGAAGCTTCGA +GTACAAATGCGCACAGAAATATTGAAATTACATAAGCGACTTAATACTACGACAATTTATGTTACACATGATCAAACTGA +AGCATTGACGATGGCTAGTCGAATTGTTGTTTTGAAAGATGGCGACATTATGCAAGTCGGCACACCTAGAGAAATATATG +ATGCCCCTAATTGCATATTTGTGGCGCAATTTATCGGCTCACCAGCAATGAATATGTTGAATGCTACAGTTGAAATGGAC +GGATTGAAGGTAGGAACACACCATTTTAAATTACATAATAAAAAATTTGAAAAGTTAAAAGCTGCTGGCTACTTAGACAA +GGAAATTATTTTAGGTATTCGAGCTGAAGACATTCATGAAGAACCAATATTTATTCAAACTTCTCCAGAGACACAATTTG +AATCTGAAGTAGTTGTATCCGAACTGTTAGGTTCAGAAATTATGGTACATAGCACATTCCAAGGAATGGAATTGATTTCT +AAATTAGATTCAAGAACTCAAGTGATGGCGAACGACAAGATTACACTAGCATTTGATATGAATAAGTGTCACTTTTTTGA +TGAAAAAACAGGAAATCGTATCGTCTAAGGGGGAGTATTCATGTCTAAAATTTTAAAATGTATCACGTTAGCCGTGGTAA +TGTTATTAATCGTAACTGCATGTGGCCCTAATCGTTCGAAAGAAGATATTGATAAAGCATTGAATAAAGATAATTCTAAA +GACAAGCCTAACCAACTTACGATGTGGGTGGATGGCGACAAGCAAATGGCGTTTTATAAAAAAATTACGGATCAATATAC +TAAAAAAACTGGCATCAAAGTAAAGCTTGTAAATATTGGTCAAAATGATCAACTAGAAAATATTTCGCTAGACGCTCCTG +CAGGAAAAGGTCCAGATATCTTTTTCTTAGCACATGATAATACTGGAAGTGCCTATCTACAAGGCTTAGCTGCTGAAATC +AAATTATCAAAAGATGAGTTGAAAGGTTTCAATAAGCAAGCACTTAAAGCGATGAATTATGACAATAAGCAACTAGCATT +GCCAGCTATCGTTGAAACAACCGCACTTTTTTATAATAAAAAATTAGTGAAAAATGCACCGCAAACGTTAGAAGAAGTTG +AAGCTAATGCTGCCAAACTAACTGATAGTAAAAAGAAACAATACGGTATGTTATTTGATGCTAAAAATTTCTATTTTAAT +TATCCGTTTTTATTCGGCAATGATGATTATATTTTCAAGAAAAATGGCAGTGAATATGATATTCATCAGCTAGGACTAAA +TTCAAAACATGTCGTCAAGAATGCTGAACGATTACAAAAATGGTACGACAAAGGGTATCTTCCTAAGGCAGCAACACATG +ATGTCATGATTGGTCTTTTTAAAGAAGGAAAAGTAGGACAATTTGTCACTGGACCGTGGAACATTAATGAATATCAAGAA +ACGTTTGGTAAAGATTTAGGAGTAACAACATTACCTACAGATGGTGGCAAACCTATGAAACCATTTCTAGGTGTACGTGG +TTGGTATTTATCTGAATATAGTAAACATAAGTATTGGGCTAAAGATTTAATGCTGTATATCACTAGTAAAGATACATTAC +AAAAATATACAGATGAAATGAGCGAAATTACTGGACGTGTTGACGTGAAATCATCTAATCCAAATTTAAAAGTGTTTGAA +AAGCAAGCACGTCATGCTGAACCGATGCCTAATATTCCTGAAATGCGACAAGTTTGGGAACCGATGGGCAATGCAAGCAT +ATTTATTTCAAATGGTAAGAATCCTAAACAAGCGTTAGATGAGGCGACGAATGATATAACGCAAAATATTAAGATTCTTC +ATCCATCACAAAATGATAAGAAAGGAGATTAGTTATGACGAAACGTAACCCTAAATTAGCGGCATTATTATCTGTTATAC +CTGGTTTGGGACAGTTTTATAATAAAAGACCCATTAAAGGGACGATATTTTTTATCTTTTTCATCAGTTTTATTTCTGTT +TTTTATAGCTTTTTAAATATTGGTTTTTGGGGATTGTTCACATTAGGGACAGTACCTAAGTTAGACGATTCTCGTGTCTT +ACTTGCACAAGGTATTATTTCTATCTTACTCGTTGCTTTCGCAATCATGCTATATATCATTAATATTTTAGATGCATATC +GTAATGCTGAACGATTTAATCGCAATGAGGAAATAAAGGATCCGAAGGCGCGTATGGTGGCAACATGGGACAAGACGTTC +CCATACTTACTAATCTCACCAGGTACATTCTTATTGATATTTGTAGTTGTATTTCCATTAATATTTATGTTTGGAGTAGC +ATTTACAAATTACAATTTATACAACGCGCCTCCGAGACACACATTAGAATGGGTTGGTTTAGATAACTTTAAAACGTTAT +TCACAATTGGCGTTTGGCGTAAAACATTTTTCAGTGTTATTACTTGGACATTAGTATGGACGCTTGTTGCAACGACACTT +CAAATTGCATTAGGGCTGTTTTTGGCAATTATTGTAAATCACCCTGTCGTCAAAGGTAAGAAATTTATCCGTACTGTGTT +AATCCTACCTTGGGCTGTACCATCATTTGTGACAATTTTAATATTTGTAGCGTTATTTAATGATGAATTTGGTGCGATAA +ATAATGATATTTTGCAACCTTTATTAGGTGTAGCACCAGCATGGTTAAGTGATCCGTTTTGGGCAAAAGTGGCATTAATC +GGCATTCAAGTATGGCTTGGATTCCCATTTGTCTTTGCACTGTTCACTGGAGTACTGCAAAGTATTTCATCAGATTGGTA +CGAAGCAGCAGATATGGATGGTGCGTCTAGTTGGCAAAAGTTTAGAAACATCACATTCCCGCATGTCATTTACGCCACAG +CGCCATTGTTAATTATGCAATATGCAGGTAATTTCAATAATTTTAATCTTATTTATCTATTTAATAAAGGCGGTCCACCA +GTGTCAGGGCAGAATGCTGGTAGTACAGATATCTTGATATCTTGGGTGTATAATCTGACATTTGAGTTTAACAACTTCAA +CATGGGTGCAGTTGTGTCATTAATTATTGGATTTATTGTTGCTATTGTCGCATTTATTCAATTCAGACGTACAAGTACGT +TTAAAGATGAGGGAGGTTTATAAGATGACAAAGAAGAAAAACATATTAAAAGCAATCGGTATTTACAGTTTTATAGCGAT +GATGTTTGTCATCATTTTATATCCACTACTGTGGACATTTGGCATTTCCCTTAATCCAGGTACGAACTTGTATGGTGCCA +AAATGATACCAGACAATGCAACATTTAAAAATTATGCATTCTTACTATTCGATGACAGTAGTCAATACCTGACTTGGTAT +AAAAATACGCTTATCGTAGCATCTGCAAATGCACTGTTTAGTGTGATATTTGTCACGTTAACAGCATATGCTTTTTCTAG +ATATCGCTTTGTTGGTCGTAAATACGGGCTGATTACATTTTTGATTTTACAAATGTTCCCTGTATTAATGGCAATGGTCG +CAATCTATATTTTGCTAAATACAATTGGATTATTAGATTCTTTATTTGGACTAACACTGGTATATATTGGTGGATCAATA +CCGATGAATGCCTTTTTAGTGAAAGGTTACTTCGATACGATTCCAAAAGAACTTGATGAATCTGCCAAAATTGATGGTGC +AGGGCATATGCGTATTTTCTTACAAATTATGCTTCCATTAGCTAAGCCGATTTTAGCAGTTGTTGCTTTGTTCAATTTTA +TGGGGCCATTTATGGACTTTATATTACCTAAAATACTATTAAGAAGTCCTGAAAAATTCACATTAGCAGTTGGATTGTTC +AACTTTATTAATGATAAGTATGCAAATAATTTCACAGTGTTTGCAGCAGGGGCAATTATGATTGCAGTACCTATAGCAAT +CGTATTCTTGTTCTTGCAACGCTATTTAGTATCAGGTTTAACAACAGGTGCGACAAAAGGTTAGTTTGAAATTAGGAGTG +GGGCAGAATTGATAAAGAACCACTAATGACGATAAAGATTAAAAGGAGGACGTTATGATGACGATTAAAGTTGGAATCAT +TGGGTGTGGTGGTATTGCGAATGGCAAGCACATGCCAAGTTTACAAAAAGTTGAAAATGTTGAAATGATCGCATTTTGTG +ACGTAGACATTTCGAAAGCAGCGAGTGCGGCAGAAGCATACGGAACTGACAATGCAAAGGTTTATGATGATTACAAAGCA +TTGTTAAAAGATGACACGATTGATGTTATCCATGTTTGTACGCCAAATGACTCGCATTGTGAAATTACTGTAGCAGGGTT +GCATGCTGGTAAACATGTGATGTGTGAAAAACCAATGGCTAAAACGACAGCAGAAGCTCAAAAAATGATAGATACAGCTA +AATCAACAGGTAAAAAATTAACAATAGGTTATCAAAATCGTTTCCGAGCAGATAGTCAATTTTTACATCAAGCAGCGCAA +CGTGGCGACTTAGGAGACATTTACTTCGGAAAGGCACATGCCATTCGTCGTCGAGCAGTACCAACATGGGGTGTCTTTCT +AGACGAAGAAGCTCAAGGTGGAGGACCATTAATCGATATCGGTACACACGCTTTAGATTTAACGTTATGGATGATGGATA +ATTATGAACCAGAATCAGTGATGGGTTCAACATTCCATAAATTAAATAAACAGCATCATGCGGCAAACGCTTGGGGTTCA +TGGAATCCAGATGAATTTACAGTTGAAGATTCTGCGTTTGGATTTATTAAAATGAAGAATGGAGCGACGATCATTTTAGA +ATCCGCTTGGGCGATTAATTCTTTAGAAGTGGATGAGGCAAAATGTTCATTATCAGGAACTAAAGCAGGTGCTGATATGA +AAGATGGTCTACGTATTCATGGTGAAGACATGGGTACACTTTATACCAAACACGTTGAATTGGAAAACAAAGGCGTCGAC +TTTTATGAAGGTAATGAAGTGGATGAAGCTGAAGAAGAAGCAAAAGCTTGGATTGATGCAGTTGTAAATGATACTGAACC +AGTTGTGAAACCGGAACAAGCAATGGTAGTTACAAAAATTCTTGAAGCGATTTATCAGTCTGCAAAATCAGGCAAAGCAA +TTTACTTTGAATAACATCATACGGTAAGGAGGCACATCATGACAAAATTAAAAGTTGGTGTGATAGGTGTTGGTGGTATT +GCACAAGACCGTCATATTCCAGCATTGCTGAAACTCAAAGACACAGTCTCATTAGTTGCAGTACAAGATATTAATACAGT +GCAGATGATTGATGTTGCGAAGCGCTTTAATATACCTCATGCAGTTGAGACACCTAGCGAGCTGTTTAAACTTGTTGATG +CGGTGGTCATTTGTACACCTAATAAATTCCATGCTGATCTTTCTATAGAAGCATTGAACCATGGTGTCCATGTATTGTGT +GAAAAGCCAATGGCGATGACGACGGAAGAGTGTGATCGCATGATTGAAGCGGCTAATAAAAATCACAAATTATTAACTGT +CGCATATCATTATCGTCACACAGATGTGGCAATTACTGCTAAAAAAGCAATTGAATCAGGTGTGGTTGGTAAACCTTTAG +TAGCACGTGTACAAGCGATGCGTAGGCGTAAAGTGCCTGGCTGGGGTGTTTTTACCAATAAAGCGTTGCAAGGTGGCGGT +AGTTTAATCGATTATGGTTGCCACTTGTTAGACTTATCTTTGTGGCTACTAGGTAAAGATATGGTGCCGCATGAAGTGCT +AGGAAAAACATATAATCAATTGAGCAAACAACCGAATCAAATTAATGATTGGGGAACATTTGATCATACTAAATTTGATG +TCGATGATCATGTTACTAGTTATATGACATTTGCCAATCGAGCAAGCATGCAGTTTGAATGTTCGTGGTCTGCAAATATC +AAAGAAGATAAGGTTCACGTTAGTTTATCAGGAGAAGATGGCGGTATCAATTTATTTCCATTTGAAATATATGAGCCCCG +CTTTGGAACTATTTTTGAAAGCAAAGCTAATGTTGAGCATAACGAAGACATTGCTGGTGAGAGACAGGCGCGTAACTTTG +TCAATGCGTGTTTAGGGATAGAAGAGATTGTGGTGAAACCGGAAGAAGCACGCAATGTAAATGCCCTTATAGAAGCGATT +TATCGTAGCGATCTTGATAACAAGAGCATACAACTTTAATGATTATCATATATGATACAAAATTCTCAATATAAAAAGAA +GGAGTGCTTTTCAATGAAAATAGGTGTATTTTCAGTATTATTTTACGATAAAAATTTTGAAGATATGTTAGATTATGTCT +CAGAATCTGGATTGGATATGATTGAAGTTGGAACAGGTGGTAACCCAGGAGATAAATTTTGTAAGTTAGATGAGTTGTTA +GAAAATGAAGACAAGCGCCAAGCATTTATGAAGTCAATCACAGACAGAGGCTTACAAATAAGTGGTTTCAGTTGTCATAA +CAATCCAATTTCTCCAGATCCGATAGAAGCGAAAGAAGCCGATGAAACGTTACGTAAAACAATCCGTTTAGCAAATCTAT +TAGACGTGCCAGTTGTTAATACATTTTCTGGCATTGCAGGATCAGATGATACCGCTAAAAAGCCTAATTGGCCTGTTACA +CCTTGGCCAACAGCCTACTCTGAAATTTATGATTATCAGTGGAATGAAAAGTTGATACCATATTGGCAAGATTTAGCTGA +GTTTGCAAAAGAGCAAGATGTAAAAATTGCCATAGAGTTGCATGCAGGATTTTTAGTGCATACACCATATACAATGTTGA +AGTTACGTGAGGCTACAAATGAATATATCGGTGCTAACTTAGATCCTAGTCATCTATGGTGGCAAGGTATTGACCCAATT +GCTGCGATTCGCATATTAGGCCAAGCAAATGCAATTCATCACTTCCATGCTAAAGATACGTATATTAATCAAGAAAATGT +AAATATGTATGGTCTAACTGATATGCAACCATATGGTAACGTTGCGACAAGAGCATGGACATTCCGTACAGTTGGTTATG +GACATAGTCCATATGTATGGGCAGATATCATAAGTCAACTTATTATTAATGGATATGATTATGTATTAAGTATTGAACAT +GAAGATCCTATTATGTCAGTAGAAGAAGGTTTCCAAAAAGCTTGTCAAACTTTGAAATCTGTTAATATTTACGACAAGCC +AGCAGACATGTGGTGGGCATAATACGAACTCGAGGTTAGTCTGAAGTTTGTCTGAAGTAAGACTGGTGGCAGTGTTGAAT +AAATGCATATGTCGCCAAGCCATTGCCAAAAATTTCACACCTTAAATCAAGTCATTGTTTGTAAAGAAGGTGTACTTTAT +ATAAGTATATAGCGATGGTCATACCCATTCACAGTAACAATCCTCACCATTGAAAAGAGTATATAACCTTTTCAATAGTG +AGGTATATGATAATAAAAAAAGCCTGTTGTCACAATGGTCATAGACACGACATACTTTAAAGGTTTCTGAATATAATATT +TCAGAATGCACTTTAAAGATGGACGTCGATGTAGACTAAAGTGATGACAGGCTTTCATCTTTTTAAATATTCATTAATTT +CTCTTCTTGTTTAATACGTACATATAAGAAATACGCATACGGTACTAATAAAATAGTTGTATATGTTGCGTGTGTTAATA +ATAATACACCGATTAATTCAGGAATGATGTTTAAGAAGTAATTTGGGTGTTTTGTAATTTTATATAATCCAGATTTAATA +ATAGGATGGTTAGGTAAAATGAATAATTTTAATGTCCAAATACCACCTAAAGTTTTAATAACCATAAATAACATGATATA +AGCAAAGATTAATATAACTAAGCCAATACCATTTGCAAAGCTAAATGTATCTTTATTAATAAATGCCTCTACACCAGCCA +ATACATAAATTAAAACGTGTGTTATTGCTAAAAACTTCGAATTTTTAACGCCATATTCAACTGCACCGTCTGCTTTTAAT +TGTTTTGAGTGATTAATAGATATCTTTAAGCTGACAAGTCTGATACAGAAAAAGATAAGTAATATAGATAGAATCATGAT +GTCCTCCGTCATTATGTCATATGTATAAGCGTTGATTTTGACAACATAAAGTATTTTATAGATAAAGCTTGTCAAATACT +ATTAACTATTTATTAATTTTAGTACATAAATATGTTTCTAAGTATGTGTTTATGTTCAGTATTTTGGATAATTTAATAAT +TTTAAGGATATTAAGCGCTTACACCGACGTGATATATTTGGCTTAACGAAAATGATTGAGGTGACAGAGATGAACTTTTT +TGATATCCATAAGATTCCGAACAAAGGCATTCCATTATCGGTACAACGTAAATTATGGCTTAGAAACTTCATGCAAGCTT +TCTTCGTAGTGTTCTTTGTTTATATGGCTATGTATTTAATTCGAAACAACTTTAAGGCGGCACAACCGTTTTTAAAAGAG +GAAATTGGATTATCTACATTAGAACTTGGTTATATCGGATTAGCATTTAGTATCACGTACGGTTTAGGAAAAACATTACT +TGGATATTTTGTCGATGGACGTAACACAAAACGTATTATCTCGTTCTTACTTATCTTATCTGCGATTACAGTTTTAATTA +TGGGATTTGTTTTAAGTTACTTTGGTTCTGTAATGGGATTATTAATTGTACTTTGGGGACTTAACGGGGTGTTCCAATCA +GTTGGTGGACCTGCAAGTTATTCAACGATTTCAAGATGGGCGCCAAGAACGAAACGTGGCCGATACTTAGGATTCTGGAA +TACATCACATAATATCGGTGGTGCCATAGCAGGTGGTGTTGCACTTTGGGGTGCTAATGTATTCTTCCATGGAAATGTTA +TAGGGATGTTCATTTTCCCATCGGTGATTGCATTACTTATTGGTATCGCAACATTATTTATCGGAAAAGATGATCCGGAA +GAATTAGGATGGAATCGTGCTGAAGAAATTTGGGAAGAGCCGGTCGATAAAGAAAATATTGATTCTCAAGGTATGACGAA +ATGGGAGATCTTTAAAAAATATATCCTGGGAAATCCTGTTATATGGATTCTATGTGTTTCAAACGTCTTTGTATACATTG +TACGAATCGGTATTGATAACTGGGCACCGTTATATGTGTCAGAGCATTTACACTTTAGTAAAGGCGATGCAGTTAATACG +ATATTCTACTTTGAAATTGGTGCATTAGTTGCAAGTTTATTATGGGGCTACGTATCAGACTTATTAAAAGGTCGTCGTGC +AATTGTAGCTATTGGCTGTATGTTTATGATTACATTTGTTGTCTTATTCTACACAAATGCTACAAGTGTCATGATGGTTA +ACATTTCATTGTTTGCATTAGGTGCGTTAATCTTTGGTCCGCAATTATTAATTGGTGTATCATTGACTGGTTTTGTTCCT +AAAAATGCCATCAGTGTAGCAAACGGAATGACAGGTTCATTCGCGTATCTATTCGGTGACTCAATGGCGAAAGTTGGTTT +GGCGGCTATTGCTGATCCAACACGTAACGGTTTAAACATCTTTGGATATACATTAAGTGGATGGACAGATGTTTTCATCG +TCTTCTATGTTGCATTATTCCTAGGCATGATTCTATTAGGAATCGTTGCTTTCTATGAAGAAAAGAAAATTAGAAGTTTA +AAAATTTAATATAAATCGGATTAAAAGTATCGCCAATCTATTGCAATATAGTTGGCAATCCTGCCCCGACGGCATGTGCG +TGAAGAGATGAAAGATACTGCTTCTACCCTTGCAAATATATCATCTCTATGTCTCGGGGCAGATCATAATTCCCTGTTAT +GAAGTATCCTTATTTGCCCGACTTAGGGTGACTCAATGAATTTACTCCTTACAATAAAGACATATAGCGGTGTCAATATT +GTAGGGAGTATTGTTTTATATTTAAACTCTCTAAAAAGCGGACTGAAAGAAAAGTGAAAACTTCTCTATCAGTCCGCTTT +TTCATAGAACAAAATGGAGGCGCCATAATCATTAGTTATGTGCTAATCTATTTTGCTTGCTTACAATAATCACTTGGCGA +CATTTGTAAATATTTTTTAAAATGATAGCTAAACATTTTATACTCTGAAAAGCCTACTTTGTCTGCAATTTCATAGTGTT +TGTAATGTCGATCTAACAATTGCAGAGATTGTAAAATACGATAGCGATTTAAATAATCGACAATTGTAATACCAACATGA +TCTTTAAATGTTCGCATCGCATACGATTCACTAACATCGATATGTTGAATTAAATCTGAAACAGTCACTTTCGTTTGATA +AGATTGCTTAATTTGATCCACAATCTGGTTTACATAATAATCATCGTATTCTACTTTTAATAGTGGTTGGAAGGCATCAT +GACAAGATGCTAAGCTACGGCCGTTCTGTGATTGTTGCTCTAATAAGGTACGGACAAGTCTTCCTAAAATAACTTCTAAT +TGTGCATGGTCTACTGGTTTTAATAAATAATCAAGAACATGATGTTGAATGCCGGCTTTCATATATTCAAAGTCATCGTA +ACTCGATAATATGATGACATTACAATCTAGATGCGCAATATCATTGAGTAAATCGACGCCATTTTTACGTGGCATACGAA +TATCAGTAATTACTAATTCTGGCTGATGTTGTTGAATTAGTGATAATGCTTCAACACCATCTTTAGCAGTGTATATTGTA +TTGAAATGATAGTCTCCCCAAGGAATGATTTGCTTTAATCCTTCTCGAATAATTCGTTCATCATCACAAATAACTACCTT +AAACATCTACATTCCCCCTTGAAAGTGGTATTTTATAACAAATTAACGTACCTTGATTACGCTTTGAAAAAATATGGAGT +CGTGCATGTGAACCATATTGAATCATTGCTTTATTGTGTAAATGATTTAATCCCAAATGCTTAGTATCAAATACATCATT +ATTAAGAGATTGGCGTACATATTGCAGGCGAGATGACGACATCCCGATACCATTGTCGCAAACTAAAACATGTAAATTCT +GACGTGCCAATGTCAGGCGTATAGTAATGTCCAATGACTCAGTATCTCTACCATGTTTAATAGCATTTTCTATGAGTGGC +TGAAGCATCATTTTACCAATTGTCTGGTGACGCGCTTCTTCAGAACTTTCAATATGGAGCTTAATCATGTCATCAAAACG +GATGTTTTGTATTGCAACATACTGTTCAATGTAGTTCAACTCTTCGTTTAATTCCACTGTATGTGAGTTTGTACGTAATG +AGTAACGTAACATTTGCGATAATTGTTGGACCACAGTTTGTGCTAATTTCGGAGATAACGTAATTAAATATTGTATTGTT +TGCATCGTATTGAATAGGAAATGAGGCTGGAATTGGCGTTCTATTTCCTTTAACTGAATATCACGCAAGCGACGTTCTGT +ATGCTCGATAGAATGGATCAGTTGCTCATTTGATTCAAATAAATCGTAAATATAATTATTAATTTCTTCTAGTTCACTGT +TGTTTTTTAAAGGCGTATATGTACCTAGATGACGATTTTTGGCATAGTAAATTTTTTGAATAATCGTTTCGATATCTTTT +GTTTGTCGTTTAGCCATATTATCTGCGCTAATGAAACCAAATATTACTAGTAAAACAAGAACTACGGCCATAACAATTAA +CAACGTGATACCATCTTCAATGTTTTCATGTATATCTTTATAAATAATGAGACGATGGTCAGCATGGTTTAATTTTACAG +ATTCATTCATAAATCCGAATTGTTGTGGTCTATACTTTTCACCTATAGTAAAACGGTCATCGTTGGCGTATAAAATATTG +TCATATTGATCAACGATAAGTGCGAATTGTCGGTTATCTTTCTTAATTTCACTTAAACGTGGGGTGTTAGCCATATAAAT +TTTAAGCATATATGTACTATTTTTGAATTTAAGCTGATGCGTTGAAAATAAATACATATTTTTAGTGTTTAAATGTTCAT +AATTATTGGTTATAAACTGATTTGGTCCAGATAATTCATAATAAAGTGTTGCGGGCTGTTGGTGTATTAATTTTAATAAT +TCACGTTTTGTAGCGGTCACATCATGATGATTTGTTAAATCGAGCTCTTGAAACGAATTATTATGCTGTGTAATAAATGT +CTGAATCTGCTTTTCAGTATGATGTAAAGATGACTGACTTTCATCAACATGTTGATGAATCGTACGATGCTCAATCCAAA +TATAGATGGCATAGAAGCTTACTAGTCCAATAATAATGACTAAAAATACTGGAAAAATAGTAGACGCAAATAACGATCGT +CTTAATTGATGTCTATAAGGTTTGTATGCCGTCATTGAATCATCTCCAAAAATTTATGATGTGGAATATCCGGTAATTTA +GATTTCGGTATTAAAGGTATGTTCTTAAGATTTTCGATAGACTGATCGCTTTGTTCACTAACATCCTTTCGAATTGACTT +GGCATCGAACTCTGCAACTAATCGTTGTTGTACTGAGCGGCTTGTTAAATATTGCACTAACTTTTTACGCTTAGGATGAG +GGTGTGCATTTTTAACTAAAGCAATACCATCAACATTTAACATTGTTCCTTCAATTGGATAAACGATTGATACAGGATAA +CCTTTGTTTTTCCATGTGCGTGCATCTTGTTCGTAGCTTAGACCTGCGTAATATTTACCTTTTGCAACATCTTCAATGAC +TTTAGACGTCTTTGACAGTTGCATCGCATGGTTTTGGAATTGATGCACATCACTTACTCGATGATGCATGCTATAAATAG +CACGCATATGTTGATAGCCTGTCGTTGTTGTATTTGGATTTGAGTACGCAATTTTACCTTTAAGTATAGGTTGTAATAAA +TCTTGATAACCTCGAATCTTAATATCTCCTTGTAAATCTGAATTCACTACTATAACTGTTGGCATTAATAGAAAACTAGT +AACATATTTATTGTTCGAGCGATAATCCTCTAATTGCTGTGTTACAGATGTATCTTGATAGGGAACAAAATCTTCTGGAT +GATCAATTGTTTCTGACAACACACCACCCATAAAGACATCACCACGCTCCGAAAAATCTTCGTTATGCAAGTTTGAAAGC +AGTACTTGAGTAGATCCGTGTTTAATTTCAATTTTGACATGCTCTTGTTTTTCAAATTCATTTAAAATTGGACGAATCAA +GTTTGATTGATACGGAGAATAAACTGTTAATACATTTTTATCGGATTCAGAGTGACGCGTATTAGCGCATGCTGATAAAA +AAATGAGAAATAATAGCAAGATATAAATTTTTGATTTCATGATATCCCATCAATTCTATGTATATTTTAATACAATAATT +TTAGCAATAAATGACGCATAAGTAATGTTAAATATTTAGAAATGTTTATAGATGACTTGTTAAGACGTTGCAAATGTTGT +GATAGCACAAAATTTTTGTTTGTCAAGACGATTTACCGAGGCTGTAAAATCAAACTGTTATATTTTATTTGTAGCTGTTA +TATAAAAATCGGCAAGATATTGAACGGTTCAAAAGTGAATTTTTACGTCAATAAAAGTATTTAATCCAGTCTCTTCATAT +ATAAAAGTAAATCTTTCTAAGTGTTGATTTAACGCTTATCAACAATCATTTTTTATAAACAAATATATACTCCTAAATTA +ACTTTTAAAGCAATGAAAATAGTGAACATTATAACTGTTGTGTAACAGAATGCAATTAGCATATTACTGTTACACAAATT +AGTACAGTTTCTATGTTTTGACATACATTTGATGAAAATTGTACATAATTTATGTGAAAAAAATCACAACAAACATGCTA +CAATGACTATGAAAACGTTAACATAGCATTTCAAATTCACAACATTATACAGATGGAGGCGTTTAGTATGTTAGAAACAA +ATAAAAATCATGCAACAGCTTGGCAAGGATTTAAAAATGGAAGATGGAACAGACACGTAGATGTAAGAGAGTTTATCCAA +TTAAACTACACTCTTTATGAAGGTAATGATTCATTTTTAGCAGGACCAACAGAAGCAACTTCTAAACTTTGGGAACAAGT +AATGCAGTTATCGAAAGAAGAACGTGAACGTGGCGGCATGTGGGATATGGACACGAAAGTAGCTTCAACAATCACATCTC +ATGATGCTGGTTATTTAGACAAAGATTTAGAAACAATTGTAGGTGTACAAACTGAAAAGCCATTCAAACGTTCAATGCAA +CCATTCGGTGGTATTCGTATGGCGAAAGCAGCTTGTGAAGCTTACGGTTACGAATTAGACGAAGAAACTGAAAAAATCTT +TACAGATTATCGTAAAACACATAACCAAGGTGTATTCGATGCATATTCTAGAGAAATGTTGAACTGCCGTAAAGCAGGTG +TAATCACTGGTTTACCTGATGCATACGGACGTGGACGTATTATCGGTGACTATCGTCGTGTAGCTTTATATGGTGTAGAT +TTCTTAATGGAAGAAAAAATGCACGACTTCAACACGATGTCTACAGAAATGTCAGAAGATGTAATTCGTTTACGTGAAGA +ATTATCAGAACAATATCGTGCATTAAAAGAATTAAAAGAACTTGGACAAAAATATGGTTTCGATTTAAGCCGTCCAGCAG +AAAACTTCAAAGAAGCAGTTCAATGGTTATACTTAGCATACCTTGCTGCAATTAAAGAACAAAACGGTGCAGCAATGAGT +TTAGGTCGTACATCAACATTCTTAGATATCTATGCTGAACGTGACCTTAAAGCAGGCGTTATTACTGAAAGCGAAGTTCA +AGAAATTATTGACCACTTCATCATGAAATTACGTATTGTTAAATTTGCTCGTACACCTGATTACAATGAATTATTCTCTG +GAGACCCAACTTGGGTAACTGAATCTATCGGTGGTGTAGGTATTGACGGACGTCCACTTGTTACGAAAAACTCATTCCGT +TTCTTACACTCATTAGATAACTTAGGTCCAGCTCCAGAACCAAACTTAACAGTATTATGGTCAGTACGTTTACCTGACAA +CTTCAAAACATACTGTGCAAAAATGAGTATTAAAACAAGTTCTATCCAATATGAAAATGATGACATTATGCGTGAAAGCT +ATGGCGATGACTATGGTATCGCATGTTGTGTATCAGCGATGACAATTGGTAAACAAATGCAATTCTTCGGTGCACGTGCG +AACTTAGCTAAAACATTACTTTACGCTATCAATGGTGGTAAAGATGAAAAATCTGGTGCACAAGTTGGTCCAAACTTCGA +AGGTATTAACAGCGAAGTATTAGAATATGACGAAGTATTCAAGAAATTTGATCAAATGATGGATTGGCTAGCAGGTGTTT +ACATTAACTCATTAAATGTTATTCACTACATGCACGATAAATACAGCTATGAACGTATTGAAATGGCATTACATGATACA +GAAATTGTACGTACAATGGCAACAGGTATCGCTGGTTTATCAGTAGCAGCTGACTCATTATCTGCAATTAAATATGCACA +AGTTAAACCAATTCGTAACGAAGAAGGTCTTGTAGTAGACTTTGAAATCGAAGGCGACTTCCCTAAATACGGTAACAATG +ACGACCGTGTAGATGATATTGCAGTTGATTTAGTAGAACGCTTCATGACTAAATTACGTAGTCATAAAACATATCGTGAT +TCAGAACATACAATGAGTGTATTAACAATTACTTCAAACGTTGTATACGGTAAGAAAACTGGTAACACACCAGACGGACG +TAAAGCTGGCGAACCATTTGCTCCAGGTGCAAACCCAATGCATGGCCGTGACCAAAAAGGTGCATTATCTTCATTAAGTT +CTGTAGCTAAGATCCCTTACGATTGCTGTAAAGATGGTATTTCAAATACATTCAGTATCGTACCAAAATCATTAGGTAAA +GAACCAGAAGATCAAAACCGTAACTTAACTAGTATGTTAGATGGTTACGCAATGCAATGTGGTCACCACTTAAATATTAA +CGTATTTAACCGTGAAACATTAATAGATGCAATGGAACATCCAGAAGAATATCCACAGTTAACAATCCGTGTATCTGGTT +ACGCTGTTAACTTCATTAAATTAACACGTGAACAACAATTAGATGTAATTTCTCGTACATTCCATGAAAGTATGTAACAA +AATTTAAGGTGGGAGCACTATGCTTAAGGGACACTTACATTCTGTCGAAAGTTTAGGTACTGTCGATGGACCGGGATTAA +GATATATATTATTTACACAAGGATGCTTACTTAGATGCTTGTATTGCCACAATCCAGATACTTGGAAAATTAGTGAGCCA +TCAAGAGAAGTCACAGTTGATGAAATGGTGAATGAAATATTACCATACAAACCATACTTTGATGCATCGGGTGGCGGTGT +AACAGTCAGTGGTGGCGAACCATTGTTACAAATGCCATTCTTAGAAAAATTATTTGCAGAATTAAAAGAAAATGGTGTGC +ACACTTGCTTAGACACATCGGCTGGATGTGCTAATGATACAAAAGCATTTCAAAGGCATTTTGAAGAATTACAAAAACAT +ACAGACTTGATATTATTAGATATAAAACATATTGATAATGACAAACATATTAGATTGACAGGAAAGCCTAATACACACAT +CCTTAACTTCGCGCGCAAACTGTCAGATATGAAACAACCTGTATGGATTCGACATGTCCTTGTGCCTGGTTATTCTGATG +ATAAAGACGATTTAATTAAACTAGGGGAATTTATTAATTCTCTTGATAACGTCGAAAAGTTTGAAATTCTGCCATATCAT +CAGTTAGGTGTTCATAAGTGGAAAACATTGGGCATTGCATATGAATTAGAAGATGTCGAAGCGCCCGATGATGAAGCTGT +TAAAGCAGCCTACCGTTATGTTAACTTCAAAGGGAAAATTCCCGTTGAATTATAAATACAATTCAGACCGAAAAGAAAGC +ATATGCAACTTCAAGAGTGAAGGGGCATATGCTTCTTTTTCAATTGAGTATTGAGTATTAGCAAGACGTAGTAAGTATAT +GAGACAACTTCTACAATGGTTGAAGGAAGACGTTTTTGTAAGTAGCTATGCTGATAAAGAATGTGATGTCTTGTTAAAGG +TGGGGTTCCAATATCATCATTTAGCTGATGTTGAATGGGTTATTATTTGCTACTTGCATATGAATATGAGTCTTTTCAAA +TTTTTATTGACCCTGAGTAATGAAAAATATTAAGATGAAACTTAATATTAAAGCAATGCGGAGCGTGATTATGAAGAGAA +TTAGTAAAGATATATGGGCAGTATTTAAATTACTGTATCAAAATAAAGGGCGTTTTAGCATTAATGCCTTACTATTGCAG +TTAATCATGATTTTTATTAGTAGTACATACTTAATTTTACTATTTAATATGATGTTAAAAGTAGCTGGGCAAAGCCAACT +TACGATTAACAATTGGACGGAAATCGTTAGTCATCCCGCCAGTGTGATACTTCTTATTATATTCATATTAAGTGTTGCCT +TTCTGATTTATGTAGAGTTTTCATTGTTAGTTTATATGGTTTATGCCGGCTTTGATCGACAGATTATTACATTTAAATCC +ATTTTTAAAAATGCCTTTGTAAATGTGCGTAAACTCATAGGTGTACCAGTTATTTTCTTTGTCATTTATTTAATGTTAAT +GATACCCATTGCCAACCTAGGACTAAGTTCAGTATTAACAAAAAATATTTACATACCTAAATTTTTAACGGAAGAACTTA +TGAAAACGACGAAAGGTATAATCATTTACGGTACCTTTATGATTGCTGTATTTATATTAAATTTTAAATTAATATTTACT +CTACCGTTAACGATTTTAAACCGCCAGTCGTTATTTAAAAATATGAGACTAAGTTGGCAAATTACGAAGCGAAATAAGTT +TCGGCTTGTTATAGAAATAGTTATATTAGAACTCATCATTGGTGCGATTTTAACATTAATTATTTCAGGAGCAACATATC +TTGCTATTTGTGTAGATGAAGAAGGAGATAAGTTTTTAGTCTCATCAATTTTATTTGTTGTATTGAAAAGCGCATTGTTC +TTCTATTATTTATTTACGAAATTATCATTAATCAGTGTGTTAGTACTGCACTTAAAACAAGAGAATGTATTAGACCAACC +GGGCTTAGAATTTAAATATCCAAAACCGAAACGGAAGTCTAGGTTCTTTATAATTTCAATGGTGCTTGCAGTGACATGTT +TTATCGGTTATAACATGTACTTACTTTACAATAATACTATCAATACAAATATCTCCATTATTGGCCATCGTGGTTTCGAA +GATAAAGGTGTTGAAAATTCTATTCCGTCATTGAAAGCTGCTGCAAAAGCGAATGTCGAATACGTTGAGTTAGATACAAT +TATGACGAAAGATAAACAATTTGTTGTTAGTCATGATAACAATTTGAAACGTTTAACAGGTGTTAATAAAAATATTTCTG +AATCTAATTTCAAAGATATCGTCGGTTTGAAAATGCGTCAAAATGGACATGAAGCAAAATTTGTATCCTTAGACGAATTT +ATTGAAACGGCTAAACAATCAAATGTGAAGCTACTAGTAGAGTTAAAGCCACATGGTAAAGAACCAGCAGATTATACACA +ACGTGTTATTGATATTTTGAAAAAGCATGGTGTTGAACATCAATATCGTGTGATGTCTTTGGATTATGATGTGATGACTA +AGTTGAAAAAAGAAGCGCCATATCTCAAGTGTGGTTATATCATTCCGTTGCAGTTTGGTCATTTTAAAGAAACATCATTA +GATTTCTTTGTCATCGAAGATTTTTCTTATTCGCCAAGACTTGTTAATCAAGCGCACTTGGAAAATAAAGAAGTCTATAC +TTGGACTATTAACGGCGAAGAAGATTTAACGAAATACTTACAAACCAATGTTGATGGTATTATCACAGATGACCCAGCAT +TAGCTGATCAGATTAAAGAAGAAAAGAAAGACGAAACATACTTCGATCGTTCTATAAGAATTTTGTTTGAATAATATAAA +CAAAGACCTCTAAAGTTATCAAGATGATACCTTCAGAGGTCTTTTTAATGTTGCCATCTATGGGATAGGCAATCGTTTCA +TTCGTTTATATTCATATGACAAGTATTTGTATGGCAATTTGGCGTCACAAACACTTACATGATTTATTGGTGAATTATTA +ATTGTTTTGTGAATGCAAAGGGTTAGAAATTGAATTGTAAATACTTTCTAATCTTTGTTTCGCTTTAGTCATTTGATCCA +AATTTTTAGTGCGTATAGCGGATTTTGCAATATAGTGCGCAGCTAAAATATCGCGTTTTTGAAACGCATCTAAATTTAGG +TACGATAATTTATTTAAGTCAGTGTTTGCTATTAATTCATGTAATTGATCTACAAGCGCTTGATGTTGATACGTATGTGA +TGTAGTTTCAGATTTGCTTGCTAATTTAATACCAGTCGTATCAAGGAGCGCCGCTTTAATACCAGCAACTAAATATGTTT +TGATTTTCATTTGTGTTGTCATGCTTTGTTACTCCTTTGATGTACATTAATCAAAAAAATTATACACTATTGTATATTGC +AAAGCTAATTAACTATAACAAAAAGATAGTTAATGCTTTGTTTATTCTAGTTAATATATAGTTAATGTCTTTTAATATTT +TGTTTCTTTAATGTAGATTGGGCAATTACATTTTGGAGGAATTAAAAAATTATGAAAAAGCAAATAATTTCGCTAGGCGC +ATTAGCAGTTGCATCTAGCTTATTTACATGGGATAACAAAGCAGATGCGATAGTAACAAAGGATTATAGTGGGAAATCAC +AAGTTAATGCTGGGAGTAAAAATGGGACATTAATAGATAGCAGATATTTAAATTCAGCTCTATATTATTTGGAAGACTAT +ATAATTTATGCTATAGGATTAACTAATAAATATGAATATGGAGATAATATTTATAAAGAAGCTAAAGATAGGTTGTTGGA +AAAGGTATTAAGGGAAGATCAATATCTTTTGGAGAGAAAGAAATCTCAATATGAAGATTATAAACAATGGTATGCAAATT +ATAAAAAAGAAAATCCTCGTACAGATTTAAAAATGGCTAATTTTCATAAATATAATTTAGAAGAACTTTCGATGAAAGAA +TACAATGAACTACAGGATGCATTAAAGAGAGCACTGGATGATTTTCACAGAGAAGTTAAAGATATTAAGGATAAGAATTC +AGACTTGAAAACTTTTAATGCAGCAGAAGAAGATAAAGCAACTAAGGAAGTATACGATCTCGTATCTGAAATTGATACAT +TAGTTGTATCATATTATGGTGATAAGGATTATGGGGAGCACGCGAAAGAGTTACGAGCAAAACTGGACTTAATCCTTGGA +GATACAGACAATCCACATAAAATTACAAATGAACGTATTAAAAAAGAAATGATTGATGACTTAAATTCAATTATTGATGA +TTTCTTTATGGAAACTAAACAAAATAGACCGAAATCTATAACGAAATATAATCCTACAACACATAACTATAAAACAAATA +GTGATAATAAACCTAATTTTGATAAATTAGTTGAAGAAACGAAAAAAGCAGTTAAAGAAGCAGATGATTCTTGGAAAAAG +AAAACTGTCAAAAAATACGGAGAAACTGAAACAAAATCGCCAGTAGTAAAAGAAGAGAAGAAAGTTGAAGAACCTCAAGC +ACCTAAAGTTGATAACCAACAAGAGGTTAAAACTACGGCTGGTAAAGCTGAAGAAACAACACAACCAGTTGCACAACCAT +TAGTTAAAATTCCACAGGGCACAATTACAGGTGAAATTGTAAAAGGTCCGGAATATCCAACGATGGAAAATAAAACGGTA +CAAGGTGAAATCGTTCAAGGTCCCGATTTTCTAACAATGGAACAAAGCGGCCCATCATTAAGCAATAATTATACAAACCC +ACCGTTAACGAACCCTATTTTAGAAGGTCTTGAAGGTAGCTCATCTAAACTTGAAATAAAACCACAAGGTACTGAATCAA +CGTTAAAAGGTACTCAAGGAGAATCAAGTGATATTGAAGTTAAACCTCAAGCAACTGAAACAACAGAAGCTTCTCAATAT +GGTCCGAGACCGCAATTTAACAAAACACCTAAATATGTTAAATATAGAGATGCTGGTACAGGTATCCGTGAATACAACGA +TGGAACATTTGGATATGAAGCGAGACCAAGATTCAATAAGCCATCAGAAACAAATGCATATAACGTAACAACACATGCAA +ATGGTCAAGTATCATACGGAGCTCGTCCGACATACAAGAAGCCAAGCGAAACGAATGCATACAATGTAACAACACATGCA +AACGGCCAAGTATCATACGGAGCTCGTCCGACACAAAACAAGCCAAGCAAAACAAACGCATATAACGTAACAACACATGG +AAACGGCCAAGTATCATATGGCGCTCGCCCAACACAAAACAAGCCAAGCAAAACAAATGCATACAACGTAACAACACATG +CAAACGGTCAAGTGTCATACGGAGCTCGCCCGACATACAAGAAGCCAAGTAAAACAAATGCATACAATGTAACAACACAT +GCAGATGGTACTGCGACATATGGGCCTAGAGTAACAAAATAAGTTTGTAACTCTATCCAAAGACATACAGTCAATACAAA +ACATTACGTATCTTTACAACAGTAATCATGCATTCTATGATGCTTCTAACTGAATTAAAGCATCGAACAATCGGAAGCAT +ATTTCTAAATTATTTATTCATTATAGTCTTAAACATAACATGACCTAATATATTACTAACCTATTAAAATAAACCACGCA +CATCTAAGTGATATACGACAATCACAGCAATAATAATTGCTTTAGAAAGTCGTGCCGAACTGGAACTTACAAGTCTAGTT +CGAACACACACTGATGTGAGTGGTTTTCTTTATTTTAAACATGAACAATCAGATAAGTTACTAGCATTAGCAAATATTAT +TAAATCAAAGGGCTTCGATTCATAAAATTTAAAACAATGATTAAAATTAGACGTGTAAATGTTAAATTCTAAAACGGAAA +TAACCACCATCCCATTAAACCACTTTTTTTGTTCAATCACTATATTTCACACAGCTTCATTAATAAAACGAAATTGCTTC +AACCCGCTTCAACTTCAACTGGCTTCAACTTCAGCCTACTTCATTCAATAACAAAACGAATCCGCTTCATCCAAAATCAA +CCATTCTAACGCACATATTCAAATATAGCAGCTGCACCCATGCCGACACCAATACACATCGTAACCATGCCGTAACGGCT +ATCGGGACGTCTACCCATTTCATTAAGTAAACGCGCGGTTAACATTGCGCCTGTAGCACCTAATGGATGACCTAAAGCAA +TAGCGCCACCATTCACATTCGTACGTGATATATCTAGACCTACTTCTTTAATAGATGCAATCGTTTGAGAAGCAAATGCT +TCGTTCAATTCGATCAAATCAATGTCTTCAACAGATAGATTGCTGAGTGACAATACTTCAGGAATCGCATATGCAGGCCC +AATACCCATAATTTTCGGGTCAACGCCTACTGCCTTAAAACCAACGAATCGTGCAATAGGTGTCACGCCGAGTTCTTTCA +CTTTATCTCCAGACATTAAAACTACAAATCCTGCACCATCAGAAAGTGGGGCAGATGTTCCTGCAGTCATAGTGCCGTCA +GCTTTAAATACTGTACGTAATTTGGCTAATGCCTCCATCGTGGTGTCAGGGCGTATAAATTCATCTTGGTCAAAGATATT +TGTGTGTACTTTTGGTCCTGCGTTTGTATATTCAACTGAGTTTACTTGTATTGGAATAATTTCATCTTTGAACCGACCAT +CACGTTGTGCGTCATAGGCACGTTGATGACTTCTGACAGCATAAGCATCTTGATCTTCGCGTGATACGTCAAATTGGGAT +GCTACATTTTCAGCAGTTAAACCCATAGGATATGACGCACCTATATCATCATATTGTAAGGTTGGATTGTTTGTGGGCTC +GTTGCCACCCATTGGTACGGCACTCATCAATTCAACGCCACCAGCTACAAGTATATCTCCTTGACCAGCCATAATTTGAT +TGGCTGCAATCGCGATGGTTTGTAATCCTGATGAGCAGTAGCGATTCACTGTTTGACCCGGTACCGTGTCAGATAATCCC +GCACGCAATGCAATCGTTCGTGCAATGTTTTGGCCTTGTAATCCTTCTGGAAAAGCCGTACCAACAATGACATCTTCAAT +CATATTCTTATTGAATTTTCCGTCAATACGTTTCAATACGCCTTGTAATACTTTGGCTGCGACATCATCAGGTCTTTCGT +GGAATAATGCGCCTTGCTTTGCTTTCGCTGCGGCTGAACGCCCATAAGCTACAATGTATGCTTCTTGCATGGTTATCATC +CTCTCTTAATGACTATCTTTTAATTACGTAATGGCTTACCAGTTTTTAACATATGTGCAATTCTTTCATATGATTTTTTA +GATTTTAGTAAGTCAATAAAGCCAATTTTCTCCAACGATTGAATGTAACGTTGATTGATAAATGTATTTCTTGGTAAATC +ACCACCCGCTAAAATTGTGGCGATATTTAAGGCAATATGATAATCATGGTCGCTAATAAAATGACCCCGTCTTTGCGCAT +CTAATTGTCCTTGGATCAATGCTTTGAAGTCTTCACCTAAAGCGATATATTGATGTCTAGGATTCGGAATATAGTTTGTT +TCTGCTTCATATTTCGCACGTTTGAGCGCAACTTCGACACGTTGTGCTGTATTGAAAATAATCGTATCTGTATCACGTAA +ATAACCATAACGACGTGCCTCAAAGGCATTTGTAGAGACTTTCGCAAATGCGATATTCGTCAGTACTTTTGTCATGGAAG +CTTGTTTGTCATCAAACTTATGCGATGTGCGTAATATGCGATCAGCCATTTCTGCAAGGCCACCGCCACTCGGTAATAAG +CCAACACCTGCTTCAACAAGACCGATATATGTTTCACTTGCAGCGACAACAATAGGTGAGTAAAGTACAAGCTCACAGCC +ACCGCCTAAGGCACGACCTTGAACAGCTGTGACTACTGGTTTCAAACTATACTTCAAACGATTAAAGCTATAATGTAATT +TATCAATTGATTGTGCAACGACATCATCTACAAGACCGTCTTCATGCGCCTTTTTCATTAAGAAAAGGTTAGCACCCACA +CTGAAATTGTTACCATCTGCATAAATAACCATACTTGTGTAATGGTCATTTTCCAGTAAATCAATCGCATCAACTAACGC +ATCGTTGAATTCATCGGTAATGACATTATTTTTACTTTGTAATTTCAGTAACAGTTGATCATCATGAGTTACGGAAAGTT +TGGCATCACCTTTATCCCAAAGTTCATCTTTTACGAAGTGAGAAATAGGTGTTGCATATTCAATGGTCTCATCTTGTTTA +TAAAAGCCACCATCTAAATCACTAATCCATTGTGGTAAGTCTCCAAGTTCGTCTTCCATACGTGTTTTAACACGTTCGTA +TCCCATTGCATCCCATAATTGGAATGGACCAAGTTTCCAGTTGAACCCCCAGACAAGCGCACGGTCTATGTCTCGGAAAT +CATCGGTAGCTTTAGGTACATTGATAGCAGAGTAATAGAAATTATTACGTAATGTCTCCCATAAAAATAGTCCCGCTTCG +TCTTGCGCATTGAATATGGTATCAAGGTTATGCACTAAGTCTTTATTAAATTCATTTAAAATTGGTAATTGTGGTTGCGA +TACAGGTACATAATCTTGTTTTTCAACATCGTAAACAAGTCGAGCTTTAGTTTCTTTATCCTTTTTGTAAAATCCTTGTT +TCGTTTTACGTCCGAGTGCGCCATTGTCAAACAACGTATTTACAATTTTGACATCATGAAAATAAGGTGTTTCTTCAGGT +ACTTGTTGCATGCCTTTAATTACAGACACTGCAATATCTAAACCGACTAGGTCAGATAGCGCATATGTACCTGTTTTAGG +ACGACCAATCGCTTGCCCAGTTAAAGCATCCACATCTACAATGCTTATCTTGTGTTGCTCGGCGCGATACATAATATCAT +TCATTGTTTGCGTGCCGACTCTATTTGCGACAAAGCCAGGCACATCATTGACGACAATGACACCTTTACCTAACACATTT +TGCGCGAAATTTTTTACATCTAATATAATAGATTCCTTCGTGTGTGACGTAGGTATTAACTCCACTAATTTCATAATACG +TGGTGGGTTAAAGAAATGTAGACCAAAGAATCGTTCTTGATCCTTCTCGTTAAATGCTTGAGCAATCGCATTAATTGGAA +TACCTGATGTATTTGTAGCGAATAAAGCATCTTCTTTAGCATGTTGTAGAACTTGTTGCCAAACAGCATGCTTAATTTCA +ATATCTTCTTTGACTGCTTCGATATATAAATCAGCATCATCATTTACCAAGTCATCATCAAAATTACCATATGTTAAATG +ACTCGCTAGATTTAAGTCGAATAGTAGCGGCCGTTTCTTATCTGTAATTTTATCGTAAGATTTTTTCGCAATGAGATTTG +GATCGTTTTTGTCCACTACAATATCTAATAGTTTTACTTTAAGTCCAGCATTCACAAAAAGTGCTGCCAGTTGAGCGCCC +ATTGTGCCTGCGCCAAGAACGGTTACTTTATTAATTGTCATAGTGATTCCTCCAATTTAGTTGAGGATAAGATAACCATT +AAGATAATTGGAATAACGTTGCTATTTTATAAAATTAATTAAGTATCTTTGACAGTCATCTTAGCCTCTTATTTAAGGAA +AAAGCTTTATGCTTAAAATAAGTCTTTTTTAGTGAAATTAATGCATCTCATATAATTATTTGCTATTTATACGAAAGCAG +AATCTCCAGTCAAAGCGCGTCCAATTACTAAGGCATTAATTTCATGTGTACCTTCGTACGTGTAAATCGCTTCTGCATCA +GAGAAGAAACGTGCAATATCATAATCGTCAGCTAGTATGCCATTACCACCTGTAATACCGCGGCCCATAGCTACTGTCTC +ACGCAAACGTAAGGCATTCATCATCTTCGCCGTTGAAGTTGCAACCTCGTCATATTCACCATGTGCTTGCATATTAGCTA +ATTGAGCACATGTTGCCATTGCTTGAGCTAAATTACCTTGCATCATTGCTAGCTTTTCTTGTATTAACTGATATTTACTA +ATTGGTTTGCCGAATTGCTTACGCTCAGTGACATAATCTAATGTGGCACGTAAAGCGCCAGCCATACCACCTGTAGCCAT +ATAAGCAACGCCTGCTCTCGTTGAATAAAGAATTTTGGCAATATCTTTAAAGCTTGTTATGTTTTGTAAGCGATCCGCTT +CATCTACTTTGACATTAGTTAATTTAATTAGGGCGTTAGGAACAATGCGAAGTGCGATTTTATTATCAATGACTTCAATA +TCGACGCCATCTTGTTCTGGTCTGACTACAAAGCAATGGGGTTTGCCAGTTTCTTTATTTACTGCGAATACTGGAATGAC +ATCAGATACATGTGCACCACCAATCCATTTCTTTTCACCATTGATAACCCAAGTATCGCCTTGGCGTTCAGCGACTGTTT +CAAGACCTCCCGCAACGTCCGAACCGTGTTCTGGTTCAGTTAAAGCAAAGCATGTACGCAGTTCATGTGACTGTAATTTA +GGTACATATTTCGCAATTTGTTCTTTGCTACCTCCGAAATAGAAAGTGTTATGCCCTAAACCTTGGTGAACACCGAGTAG +GGTAGCTAAGGAAATATCAAATCGCGCGAGTAGGTAAGACATGAAAAACTGAAATAGTTGACTAGGCATTTTGGCGTTTG +GACGATCCTTGTAAAGTAATGGATTGTTAAAATAATTTAATTCTCCCAGATCTTTAAAATAGTCCTCGGGTACAGTAGCG +TCTATCCAATGTTGATTAATATTTTCACGGTACTTACTTTCTAGCAATGAATCTACTTGTTGTAAAAATTCGACTTCACC +GTCTGTTAAACCTTTAGCAATACTAAGTACATCTTCAGGAAATAATGTTTTTAAGACCGTTTCTTTTTCAAATGTCATAT +AAATTCCTCCTAAAAATAATATGAATACTAATGTGAAATGCATTTAATTCAAAAACAACACGCTTTATTTGTAAACGCTT +ACACTAAATGTCAAAAATTTTTATCACCTTTAAAGTGTTTGCGAGACTTTGTCATTCATCATTTGTCGAATCGCAAGTTT +ATCTGGTTTCTGCGTACTGTTTAACGGCATATGTGTCACTGGTACATACATTCTTGGGACTTTATAACCTGCTAAACGAC +TTCGCATATGTTGATTTAAAATTTCAGCGTAATGAGGTTCATCTTCGCGAAGTATAATGGCTGCAGCAATTGATTCACCA +TATTTTGGATGATCATAGCCAACGACCACACACCGGTCTACTAGTGGATGCTCAGCTAAAGCATTTTCGACTTCGGATGG +TAAGACATTTTCGCCACCAGTTATGATTAATTCTTTTTTGCGGTCAATAATAAATATATCGCCATCGTTGTCCATCTTCG +CTAAGTCACCAGTTAATAAATATCGACCATGAAATGCTTTGGCAGTCTCTGCTGGTTTATTCCAATATCCTGGCGTGACA +TTTTTAGCCTTAATTGCAAGTTCGCCAATCTCACCAGTAGGTACTTCCTCACCGTTATCATCAAGGATACGTGCATCAAC +GAACATGACTGCTTTACCAATACTCATTGGCTTACGTTTTGAATTTTCCGGTGTATTAACAAGTACAAGAGGTGCTTCAG +TTAAACCATAGCCGTTAATAATGTTTATGCCATATTGTTTAAAAGCTGCTTGGATACTTGGTAATGGTTGTGAACCACCT +TGGATGATATAATCCATAGCTCTAAAATTTTCAGGATTAAAATTACTAGCACGTAGCGTACTATAATACATTGTCGGAAT +CATGATAATAAATGTAGGGTGATATTGTGCAATCATGTCATTCAATTCTTCGCCGTTAAAGTAACGTTGAAGAATAAGTG +TGCCACCTGACATTAATACTGGTAATACAGTATCGTTAAACCCTAAAACATGGAACATTGGTGTTGATACAATCGTAATA +TAGTTTGAATTGAACTTATACGTCAGCTCTAAGTTTGCACCGTTATGAACAAATGATTCATATGAGAACATCACACCTTT +AGGTGATCCGGTTGTACCACTTGTATAAATTAATGCTGCAAGATCTTGTGGTTCAACAGGTGTTGCTTGAAAAGGTTGGT +GATAATCTGGATTTACGATTTCATCATATTGCGCCACATCAATATCCATATGCAATAAGTTTTGGTCAATATCGGTGAGT +GAACTTAAATGTTTTTCAGCATAGAAGAGCAGTTTTAATTGTGCATCTTCCACAATGGCTGCAATTTCTTTTGGGTTAAG +CCGCCAATTCAATGGTAAAAAAACCGCACCTGTTTTAAAACAAGCAAACAATAAATCTAATATTGCAATATCATTTGGCG +CAAAAATACCGATAACATCGCCTTTTTTAACACCTTGAGATGTTAAATAATGTGCCATATTATCAGCGCGTGCATTGAGT +TGTTGGTATGTCCAAGATGTTTGTTTTGCGTGATCAATAACGGCAGGCTTGTCATCATCGAAGTCTGAACGCGTTTTTAT +CCAATCGAAATTCATTAGTATACCCCCTTTAGCTTCACTTTCATACTTTATGAATTGATTGTTTAAGTTGTCCCCATTTT +TCTTTGTAAATGCTGGTATCAATTAATTTTAAATGATCAGCAATAATTGGTTTAAAAGCCATTTGATTCAAAATATCTTT +ATGCAAATCAAGACCTGGTGCAATTTCAATTAGTTTCAAGCCTTGATTGGTGAGTTCGAATACTGCACGATCAGTAACAA +AATAGATTTCTTGCTCGAGTGATTGTGAATATTGTGCATTAAAGTCGATATGGCTCACATCTGATACAAATTTCTGGTTT +TGTCCTTCAGTTTCAATGTTTAATCGTTGATTATGGCATGAGACATGACTGCCAGCTACAAAAGTACCTGAAAAGATAAT +TTTATTTACAGATTGCGTAATGTCTATAAAGCCACCACATCCATTTAGTCGGTCATTGAAGTAAGACACGTTGACATTGC +CGTATTGATCAACCTCAGCAAAGCTAAGATAGGCAACTGATACACCATTGTTATAAATAAAATCCCATGCTCGATCATGA +GGCATGCGCACATCTGCATTGTAATTCATACCAAAATGTTCACGACTCCCAACGAATCCACCGAAAATGCCAACATCTAA +AATCGGTTGCACATCATGTTCAACACATTCTTCATGCAATAAATTAGAGAGTTCATTATTGATGCCATAACCGATGCTAA +TTGTATCGCCATAAGTTAAAAACTGAGCAGCACGTCGGAGAATCAATTTGCGACTATTAAAAGGTAATGCGGGTTCAGGT +ATTCCATCAATTCGTTCTTCTCCAGACAAGGCTGGTAAATAATGACTTTGAATTACTTGGCGGTGATTCTTTTCATCTTC +TGTGACGTATACATAATCGACAAGATTTCCTGGGATAACAACTTCATTCGGTTTTAGTTGATAGTCGTCAACTAAAGCTT +TAACTTGTACAATAACTTTCCCATGATTGGCTTTCGCGTTTAATGCGACATGATAACACTCGCTCAAGTACGCTTCTTGA +GTTAAATAAATGTTACCTTGTTGATCTGCGTATGTTCCTCTCAGTAGTGCCACATCAACGCTAGGGAATGTGTAATGTAA +GTATGTTTCATCGTTGATGGTTACTAATGAAACTAAATCATCCGTTGTTCGTGTATTTACTTTACCGCCACCGTATCTAG +GATCAACAGCTGTGTTTAATCCGATTTTAGTAATAACTCCAGGTAATAATTGATTACTCTGACGATAATGAGTTGCAATG +ATACCTTGTGGTAAAAAATAAGCTTCAATGTCATTATTTTTCATTGCTTGTGCCGTTTTGGAAGAAGCCGTTAAAATACT +CATAATGACACGTTTAATCATGCGACGTTCTATAAAATCATCTAAATCCGGTGCGGCACCTAAACTATGAATATCATTCG +CTAATATAAACGTTAAATCATTGGGCGTATGATATGTGTCATGTTGCGCTAACACAGCACGTAGAACTTCGGCGGGTAAG +TTGGCTACAGCTAATGCTGGTAAACCAATCACATCACCATCTTTAATGATATGTTGTAAGTCGTGCCATGTGATTTGTTT +CAAGCAAGTCACCTCCATCACATTTGATAAAATATAGCGTTTTTACACTTTGTGTAAACCCTTACAAGAAATATAACATA +ACGACGTTTAAAATCAATTAGAAATATCTTTTTATTCTGATAATAGACACAGTATAGACACATTTTGATGGTCGATAACA +ATTGTAATATCAAGGGTTTGTAATGAATTGAATATCATTAAAATACTTATATAAAAATATTGTTCGGAATATAAAAAGTT +AAATAGGTTTTGATTTTTAAATATGAAATACAAAGTGCCCAATCGAACAAAGTATTTATATTAAAATATGGAAAATCCAT +CAATATTAAATTAAAATAGTTTTATTATGAAAAGTGAAAGTAGGTAAGTCTATGGAAGGTCTTAATCATCGAAGAAATAC +AGAAAAAGAAGAGACAACACAAACGCAATCAGTTGCACCTAATACAGGTGAAGAGGGGATGTCATCAGCAAGTACACAAT +CAACTAAGACGTCCGACATACATAATGAATCTATCGATAAACAAATGGAAGCTAAAGCGCATGAAACAGCGCAAAATACA +GATTTAAAAAACGAAGCAAGAAGTTTATTTGATAATGCAACCAAATCAATCGGTAGACTAGCGGGCAATGATGAAAGCTT +AAATCTTAATTTAAAAGATATGCTTTCTGAAGTATTTAAGCCGCATACTAAAAACGAAGCAGATGAAATATTTATAGCGG +GTACTGCTAAAACTACGCCAGCAATTTGTGACATATCAGAAGAATGGGGGAAGCCATGGCTCTTTTCTCGAGTATTCATC +GCTTTCACAGTAACATTTATTGGATTATGGGTCATGGCAGCAATTTTTAATAACACTAACGCGATTCCGGGTCTCATTTT +TATAGGGGCTTTAACAGTACCATTATCGGGTTTGTTCTTCTTTTATGAATCAAATGCGTTTAAAAATATTAGCATTTTTG +AAGTTATTATCATGTTCTTTATTGGCGGCGTATTTTCATTACTAAGTACGATGGTATTATATAGATTTGTCGTTTTTAGT +GATCAATTCGAAAGGTTTGGTTCTTTAACATTTTTCGATGCATTTTTAGTAGGATTAGTTGAAGAAACTGGAAAAGCACT +CATTATTGTTTATTTCGTCAATAAATTGAAAACAAATAAGATTTTGAATGGATTATTAATCGGTGCTGCTATTGGTGCAG +GGTTCGCAGTTTTTGAATCAGCAGGTTATATTTTGAATTTCGCTTTAGGAGAAAATGTCCCATTATTAGATATTGTCTTC +ACACGTGCGTGGACTGCGATTGGTGGTCATTTAGTTTGGTCAGCGATTGTTGGTGCTGCAATAGTTATTGCGAAAGAACA +GCATGGCTTTGAATTCAAAGATATTTTTGATAAACGCTTTTTAATATTCTTTTTATCAGCCGTTGTTTTACATGGCATTT +GGGATACATCTTTAACTGTACTTGGCAGTGATACGTTGAAAATATTTATTTTAATCGTTATTGTGTGGATACTTGTATTC +ATTTTAATGGGGGCAGGTTTAAAACAAGTGAATTTACTGCAGAAAGAATTTAAAGAACAACAGAAAAAAGTAGACGAATA +ATAATTAAAGCTTATGTTGCTCATATGTTTGTGACATAAGCTATTTTTATAATTTGTCTTTAAAAGAGTGGAATAGGAAT +ACTTTTTGGAGTTAAAAAAGTGTTTCACGTTAAACAAATAGTGACAATTAGATTTATATAAAATGAACATGATTCACTGA +AAGTATGTAATAATCATTTTATTGAAATTCATCAAACAGAAATTAATACAATCATATAAGCAAATTAAACCACGCCATAA +TCATATTGGATGACTTCGGCGTGGTTTTTATAGTTGAAGCAGGGCTGAGACATAAATCAATGTCCCACACTCCCTTATCG +TTCAATCGTTGTTCGATAATCGATTAAATAGATACCTTCAGGTGTTACTTTATAATTTTTAACCTTAGAGTTAGCAGCGA +CTATTTGATCGTTGTAAGCAATATAACTGTTTGGTACATCTCGACTTGATAATTTAATAATATCATTAGAAATATTGTGA +CGTTCCTTAACATCTACAGTATGATTCAATTGATTAATTAAATCATCGACGTTGCTATTATTGTAGTCTCCTTTATTAAT +AGCACCATCTTTTTTATATGCTTGATTAAAGAAATAACCTGTATCTCCACGAGGAATTGTTCCGAAACTATACATCGTTG +CATCCCATGCAGAACGGTCTTTTAAGTAACCTTCTATGTCATCAACACTTTTAATGTCGATTTCAATATTTGCTTTTTTA +GCATCTGATTGTAATACTTGCGCAATTTTCGATAGCTCTGGACGACCGTCATACGTAATTAACTTAATTTTTAAAGGGTG +TTCTTTTGTATAACCATCTTTAGCTAATAACATTTTTGCTTGTTCGATATTTTGTTTGGTTAACTTAGGTTCTTTAATAT +ATGGAATTTTATCATTAAATGGACTCGTTGCAGGTTTCGCATAACCTTGATAAATATGATCTGCAATACCTTGTCTATCA +ATGATATGATCTAATGCTTCACGAACGGATTTAGTCATTTTTTTATTAGTATGATTATACATAAGTAAAGAAGTTCTAAA +TCCAGATTCTTTTGACACTTTTAAATTTTGATTATTTTCTATGTCTTGAACTTTATTAACTGGGACATCAGTTATTAAAT +CATCTTTTTGAGATTCTAAATTTCTGACGCGATTATTGCCGTCTTCTTGGTACGTCACAGTAATATGATCAAGTTTCGGT +TTACCTTGCCAATAGTCCTTAAAATTCGACAATGATATTTTTCGAGATTGCTTATAATCTTTTATTTGGTAAGGGCCTGT +ACCAACAGGAGTTTGATTAACATCTGATTTAGCATCTGTATCATAAATTGCCATAAAAGGATTAGCTAATTCAGATACAA +GTTCAGGGTAAGCGGAGTTGGTTTTAATTGTCAGTTTTTGACCTTTAGCGGTAATTGATGATATTGGTAATGAATATTTG +ACCAAGTCGCTTTTTTTCATGCTATTTTCAAGGCTAGATTTCACTTTTTCTGCAGTCAATTTTTGACCGTTTTGAAATTT +AATATTATCTTTTAATTCTATATCTAACGTTGTATCATTTGGTTGATGATACGATTTCACTAATGCTTTTTCTATTTTTC +CTTGATCATTTGTTTTAAATAATGATTCTGCAGCACCAATCTTAACTGGTACATCTGTTTCATAAGGTGCAATAGACTTT +GTTTTTAACGGTAACGAAATATTTAAGTCTTTGCCAGATGAATGCATTGAGCCACATCCTGATAACACTAATACTGCTGA +AAATATAGTTGCTAGTCTTTTAAACTTCATTTCATTAACACTCTCTTTCTAATTACTATGTAAAACCCAACAATTAATAT +TTTAAAACTTTATTTTGTTAAAGTAAAATGTTGTTCAAGTTTAGTAATTATTAAAGTTCAATTAATTGTAGTAATTATGC +TTTTTAAAAATAAATATTAGAAATGAAGTTAGCGACATTTATAGTGATTCACGATAAACATATATAACTAAGTATTGAGC +AACTGCTGTAGTACTACAGCTTGGTTATGTTTAGTATCTTTTGCTGCATATAACAATAGAACATGATTATGCTGATTTAC +AATATCCTTTAATTTTTCAAAAGCATCTTTTTGCGCATCCTGATCACGTAATTCTTTTTCATATTTTTCTTTAAAAGCTC +CAAAAAGTTTAGGATCATGTTGGAACCATTGTCGCAACTCAGTAGAAGGGGCAATGTCTTTTAACCAATAATCTAGGTTA +GCAGTTCTTTTCGAAATACCTCTCGGCCAGACTCTATCGACTAGGATACGAATAGCGTCGGTATTATCTTTATTGTCATA +AATCCGTCCAATATCTACGGTCATCTTGTGAACTCCTTTCTTATGAAATTCAGTGAGCATACATCAATGCATGTTGTGGT +GGGACGACCAAATAAATTTTGCGAAAATATCATCTCTGTCCTACTCCCAATTAAAAAACAGCCATGACAAAGTAAAGTCA +TAGCTGTTTTGGTATAGATGTCATTTATTTTTACGTTTAGTTAAATACTTCAAACCAACTGCAAAGACCGTGTACCCGGC +TATGGTTTGTATCAATGTTTTAATTAAATTATTATTTTTAACAATAATATTTGCAGTAACAATACTTACGAAATATAATG +CAAATACTTTTACGTAACGTTGATTAAGTTTCATATGAGCACTAAACCTCTTTTCTCAGTTATACTGCAACGCTTAGTCT +TGGAATAAATGTTTCGTAGTGTACGCGATCCATATCGTAATTTAAAGATTTAAGTGCTTCGATCATAGATTGTAAGAATT +TTGTACCACCACAGATATAAATTTCAGGTTTATTTGCTAAAAATACTTGTAATTCTTCAGCACCAATATAGCCTTGTTTA +TCTTTTAAGTGTGTATATAATTTAGCGTTGTCATGATGGCTTGCGATACTGTTGAAGTTGTCTTTGAAAGGTAAATGTTG +TTCATTTTCAGCAACTTGAACCATCTGTGTATCTAAACCTTTGGCAGAGGCAGCTTCATACATAGCTACTAAAGGTGTAA +CACCAATACCTGAACCTAAGAAAAGTTGTGGTTCAGTCGTATTCTCTAATACGAATCCACCTACAGGCGCAGCTAAATTA +ATCATATCGCCTTCTTTAATCTCATCGTGTAAAATTGTTGAAACTTCGCCTTCATGTTCTGTTGTGACATCACGTTTAAC +GCCAAAAGTTAAATGGTTTTTTTCACCTGATACGATAGAATAGTGACGTTTAGCTCTATATGGAAGTTTATCACTAGAAA +CATCAACTGTGATGTATTGGCCTGGTGTAAATTCACTAAAGTCATATTCTTCAGTTTCAACTGTAAATGATTTAATGTCT +TCAGATTCTTGTTTAATATTGGTAATTTTGAATGGTTTAAAACCAATCCACATCATTTGATCATAAATTTCTTTTTCAAT +TTGGATGAACACATCCGCAATAACGCCATATGCTTTTGCCCAAGCTTGAATGACAGGGTCATTTTCTTCTAATCCTGTCA +CGTCTTGAATGGCTTTTAATAAATTTTTCCCCACAATTGGATAATGTTCAGCATAAACTTGTAGTGCGCAGTGTTTATAT +GCGACTGGCATAATGACTGGTTTAATAACACTTAAGTTATCGATATTAACCGCTGCGGCCATTACAGCTTGTGCTAATGC +TGAAGATTGCATGCCTCGTTTTTGGTTCGTTTGATTAAACATGTTTAAAAGTTCAGGATGCGCTTTAAACATTTTTGGAT +AAAAGATTGACGTAATTTCTGTCCCTTTCTCTTTAAGTAAAGGCACCGTTTGTTTGATAATGTCTTTCTCTTGTTCTGTA +AGCATGATACTCCTCCTTTAATCTGTATATTTTGATTATTCTACTAAAAATTTCGATATTCAATTAATTGGTTTGAGAAA +ATATAAATAAAATGGCAAATTTGATAATTGTATGACATTTTTAATTTTTTAAATACTTATCAATAAGCATTGTGTACAAT +TGTCTGTTTGCACACCGACGATTGAGCGCATTTATTTGACTAATTCAAAAAACATTGTTGTTTTCCTAGAAAAAAGTAAA +CATGATAATAAAAATGTGAAAGTGTAAATAATCACTGGCGAAGTACGAAGACTAAAGACATCTAAGATGTAATCGTATAC +AAATTAAAAAGGTGTAAAAATTAAAATAAAATGTGAAATAAATCACAATTTAATATTGACCCAGTACTTAATGCATGTTA +CATTTTATATGTGAAATAAATCACAAACTTAAAAGCGGATGACACATGACCTTTTAAGTTATGCGTTGAAAATAAAAGAG +ATGTTTATTTGCTTTTGTATCGTCAATAAGCAGCATTAAACTAACATATTTGAAGCTACATGTATGTTAGTGAATTAATC +ATAAGGGAGTTTTTGTAATGAACAAATTTAAAGGGAACAAAGTTGTATTAATAGGTAATGGTGCAGTAGGTTCAAGCTAC +GCATTTTCATTAGTGAACCAAAGCATTGTTGATGAATTAGTCATCATTGATTTAGACACTGAAAAAGTTCGAGGAGATGT +TATGGATTTAAAACATGCCACACCATATTCTCCAACAACAGTTCGTGTGAAAGCTGGCGAATACAGTGATTGTCATGATG +CGGATCTAGTTGTCATCTGTGCTGGTGCTGCACAAAAACCTGGAGAAACACGTTTAGATTTAGTATCTAAAAACTTGAAA +ATATTCAAATCAATTGTTGGTGAAGTAATGGCATCAAAATTTGATGGTATTTTCTTGGTAGCTACAAATCCTGTTGATAT +TTTAGCGTATGCAACATGGAAATTCTCTGGTTTACCTAAAGAACGTGTTATAGGTTCTGGTACAATTTTAGACTCTGCAC +GCTTTAGATTATTGTTAAGCGAAGCGTTCGATGTTGCGCCACGTAGCGTCGATGCTCAAATTATTGGTGAACATGGTGAC +ACTGAATTACCAGTATGGTCACACGCTAATATTGCGGGTCAACCTTTGAAGACATTACTTGAACAACGTCCTGAGGGCAA +AGCGCAAATTGAACAAATTTTTGTTCAAACACGTGATGCAGCATATGACATTATTCAAGCTAAAGGTGCCACTTATTATG +GTGTTGCAATGGGATTAGCTAGAATTACTGAAGCGATTTTCAGAAATGAAGATGCCGTATTGACTGTATCAGCATTATTA +GAAGGCGAATATGAGGAAGAAGATGTTTATATTGGTGTTCCAGCAGTCATCAATAGAAACGGTATTCGCAACGTCGTAGA +AATCCCATTAAACGACGAAGAACAAAGCAAGTTCGCACATTCAGCTAAAACATTAAAAGATATTATGGCTGAAGCAGAAG +AACTTAAATAACTTTTTATAAAATCTATACCATCCCAAAAATTGTAAAACCTTACCCAAAAAATTGTATAAAGGGCTATT +TGATACAACTATATGTGTCGAGTGGCCCTATTTTTAATGTAGTGAAAGTCGTTGTTGAAATTAAATTCAAATCTGGCACT +TAAGCTTTAATCATAGCATTAAGATGATTGTCTAGCAGAGGCGATTTGCGGGCTCACTACAGTGCATGATGAACTTAATG +CTTCAAATGTAACATTAAAAATAAAAGCAACGATGTCACTTCTTACTTCGTACATCGTTGCCATTAGTTCTGTTGATATT +TCGGTTAGTCTTAATCCCCGAGCAATTCTTCAATTTCATTTTTGATAACTGTAACGTGAGGCCCATAAATTACTTGCACA +CCAGTGCCTTGCTGGATTACACCTTTGGCACCAGTACTTTCGAGTAATACTTTATCGACTTTGTCATTTTGATGAAGTGT +GACGCGTAGTCTCGTTGCACAACAGTCAACGATTTCAATGTTATCTTTGCCTCCCAAACCAGCAACAATAGTTTGTGCTC +TTTCAGTAGCCTCAACTTGTTGTGCTGCAGCTTTATCTTCTCGACCAGGTGTTTTGAAATTAAATTTCGTAATTAAGAAT +CTGAAAACGATGTAATACAAACAGAACCACACAATTCCAATAGGTATGACGTATAGGTAGTTTGTTTTACTATTACCTTG +TAGCACACCAAAGAGTAAGAAATCGATAAAGCCTCCACTGAAGGTTTGACCAATTGTAATGTTGAAAATGTCTGCCATCA +TAAATGCTAATCCATCAAAGAAGGCATGGATTACATAAAGAATAGGTGCGACAAACAAGAAACTAAACTCTAAAGGTTCG +GTAATACCTGTTAAAAATGAAGTGAGTGCAGCGGATAACATTAAACCGCCGACAACTTTTTTATGTTCAGGTTTAGCTGT +GTGATAAATTGCAAGTGCGGCACCACATAAGCCGAACATCATCGTAATAAAACGGCCTGACATAAAGCGTGACACACCTG +AATAATACTTCGTCACATCTGGATCACCAAGTTGAGCAAAGAAGATGTTCTGCGTACCTTGAACTAAGTGCCCTTTGACT +TCTAAAGTACCACCAAGTGCCGTCTGCCAAAACGGTAAGTAAAAAATATGGTGTAAACCGAGTGGACCTAACAATCTTAA +GATGAAGCCATAAACAAAAGTACCGATGGCACCTGTTTTCGTTACAAATCCACCAACATGATAAATGCCGGCTTGTATGC +TTGGCCAAATGAAAAACATCAATACACCTAAAAAGATTGCGGCAAATGCTGTGACAATAGGGACAAATCTAGAGCCACCA +AAGAAACCTAAATACGGTGGTAATACCACTTTGTGATATTTGTTGTGAAGTATTGCGGTCATAATACCTGTGATAATCCC +GCCAAAAACACCGGTTTCAACCGTTTGTATACCGAGCACCATGCCTTGTCCATTTTGTGCAAGCTGATCTTTTGCCAATG +TGCCCGTGATAGTTAATAAGCCATTCATAGTTGCGTTCATAATTAAGAAACCGAGCAGCGCAGCTAAACCTGCAGTACCT +TTATCGCTTCTAGATAATCCGATTGCGACACCAATTGCAAAGATGACCGGTAAATTTTGGAAAACAATACTACCTGCAGC +TGACATTAATGTAAAAATATTTTGTAATAAGGTAATATCTAAAATAGGGTATGCTTTAACGGTGTTTGGATTACTTAATG +CACCACCGATACCCAACAATAGACCTGCAGCTGGTAAGATTGCGATAGGTAACATAAAGGACTTGCCGAACTGCTGTGCT +TTTTCAAATAAAGATTTCATCAACATCCCTCCTAATTATTCTCAATATAGCTTTTGAGAAATTTAATATCAATATATATT +CTGTGTATGAAAATATTTTTCATAAAAATTGTTTGAATCATGTAACAATCATATAAATTGCTGTTTATATTGTTGTGAAA +ATGAGTTGACAAAAGTCGGTATAGATATGTAGACATCCTATTTTTAGCGAGGTAGGTTGATAAGGCATATCGGATAAATT +TATAAGCCATAAAATAGATAAATAGTGTTTGTATCCAAAAATATGAGAAAGTTAAAACTATTTTTCAAAATAAACATGAT +TCACCACATAAATAAATATACATAAGCTATCGATAACAACTTAAGAAAGGTGGATATATAAATGAAAAGAAAGATTATTA +TGGATTGTGATCCAGGACACGATGATGCAATAGCATTAATTTTAGCGGGGGCAATTGACAGTCCACTAGAGATATTAGCT +GTAACAACAGTCGCAGGTAATCAATCAGTTGACAAGAATACGACAAACGCCTTGAACGTATTGGATATTATGGGACGCCA +AGATATAGCAGTAGCGAAAGGTGCGGATAGGCCGTTAATTAAACCAGCTGCCTTTGCTTCTGAAATACATGGGGAATCTG +GATTAGATGGTCCGAAACTACCGTCGACACCATCACGTCAAGCAGTTGCAATGCCAGCATCAGATGTGATTATAAACAAA +GTGATGACGAGTGATACACCTGTAACAATTGTAGCGACAGGTCCTCTTACGAATGTAGCAACGGCATTGATTCGTGAGCC +AAGAATCGCTGAGCATATTGAATCTATTACTTTGATGGGTGGTGGTACATTTGGAAATTGGACGCCTACAGCAGAATTCA +ATATTTGGGTAGATGCTGAAGCAGCGAAGCGTGTTTTTGAAAGTGGGATTACTATAAATGTGTTTGGTTTAGATGTAACA +CATCAAGTTTTAGCCGACGATCACGTGATTGAACGCTTTGAAAGTATCAATAATCCTGTTGCACAGTTCGTCGTAGAATT +ATTGCAATTCTTTAAGAAGACATACAAGACTCACTTTAATATGGATGGTGGTCCAATACATGATGCTTGTACAATTTTGT +ATTTGTTACAACCAGAATTGTTTACAATGGTACCCGTTAATATCGACATTGAACATCAAAGTCCACTAACTTATGGCACT +ATGGCTGTCGATTTAAATCATGTTACAGGTAAGCCTGCCAATGCTTATTTTGCTACAGCAGTTGATGTTGAAGAAGTGTG +GAACTTGATAGACCATAAGTTACGTACATACGAATAATAATACTTAATTAAATAGATACAGTTAACCCTAAGGCGCCTGA +TATAAGCGTCCTTAGGGTTTTTGTATTGGGTTACAATGGTCATGACAATTTGATAATGATTGAGATATGTATGTTAAAAA +TGCTTCAAAAAAACACAAAACACAAAAATATATGCAAAAATTTCAATTGCTTTATTTTAAAAGATTAAAATCACAAAAAA +TATAGATATAATTGAAATGTTGTTAAAACATCACAATGATGTACATGCTCATTTTGAAAATATGTGTTCAAAGTGTGTAC +ATCGGTGTGACGATTGATGATAAATTGAGCTCAGATGCTCAAAGGAGAATCGAACATGAACGGGGATAATCAGCAAATAC +TCAGAGAAATTGTATTGAATCCTACTATTCATGGTAAAGAACTTGAATCGATATTTGGTTTGTCTCGTAGACAACTAGGA +TATCGCATTCAAAAAATCAATTTGTGGCTTGAACAAGAGGGTTATCCAAAACTTGAAAGAACAAGCCAAGGAAATTTTAT +TGTAAGTTCTGAAATCATGACGTTATTCAAGCGAGATGTATCGGAGCAGCAAATGTTAAACGGCAACAATGTCATTTTTA +GCATAGAAACACGTCGTTATTATTTAATGCTCATGCTTTTTAGTAAGGAAAACGCAATGTCTCTAAACCATTTTTCAATT +GATTTACAAGTCAGTAAAAATACTGTCATTCACGATATAAATCATGTGAAAGAGCAATTGGAAAATCATGGTTTGTCACT +TAAGTATTCTCGAAAACATGGTTATGAAATTGTTGGTGATGAATTTGAAGTTCGCCGTTTCTTCATTAAGTTGATTGATC +AAAGGTTGAATCATGATATTACTAAAAGTGAAGTTTTAAAGGCGCTCAACTTAACATTCGAAGATATCGCATATCAAAAA +GACAAGATCAAACAGGTAGAACAATTTTTGAAGAGTCGCTTTATAGACAAATCACTTAGTTCATTGCCTTATGTCCTTTG +TGTGATTCGTAGACGAATTCAAAGTGGTCATGTGATGAATCCATTAAATATTAATTATCAGTATTTGAGGGATACGAAAG +AATATCAAGCAACGGAGATTATGACGCAACATGAGCCGGATTTGCCAGAAGCGGAAAAGTTATATTTGACATTACACTTA +CTTTCAACAAGTGTGCAATGGACTGATTTGCAAGAATCAGATAGCATATCGAATTTAACGATGGCCATCGCTCAAATGAT +TCACCATTTTGAACAAATCACTTTTATTAACATTGAAGATAAGGAGAAATTATCACAGCAACTCTTGTTACATTTAACGC +CTGCTTTTTATAGGATTAAATATAACTTAACGGATCGTGATGAATTAATAAATCCTTTACAAGGAAATTATCAATCCTTA +TTTCATATGGTGAAACAATCATGTCAATCGTTAACTGAATATTTCGGAAAATCGTTGCCTGATAATGAAATAGCATATTT +AACCATGTTGTTCGGAGGTAGTTTGAGACGTCAAGATGAAAACTTCGATGGCAAGATAAAAGCTATTATCGTGTGTACAC +AAGGCACGTCAGTATCACAAATGATGTTATACGAGTTGCGAAACTTATTTCCAGAAATTATTTTCTTAGATGCGATTTCA +CTTAGAACATTTGAAAATTACACATTAGATTATGACATCGTCTTTTCACCAATGTTTGTCCTAACACATAAAAAATTATT +TATCACAAAAGTAGCTTTATCTGAAAATGAGCAACGAAAGTTACGTAAAGAAGTGATGAAGTACATTAATAAGGAATCGG +CTGACATTGATAAGGAAATAAACAAGTTAATGGCATTAATTGAACGCACAACGACAGTTAATGACATTACAGAACTACGT +GATGGTTTAGAAGATTTTATTGCCAATTATAATTCAATTTCAACCATTAATGGATCGATTGTCACACAAAATAAGACATT +AGATTTAGCTGACTTGATACCGGCAAGGCACGTGAAAAGAATGCATCATGTTGAAAATATTGATGAAGCTATTGCTAAAG +CAAGTGATGTGTTAGTTGCTAATCATTTTATTGATATTAAATATATTCATGAGATGCAACAGGTATTTGATGATTCGTAT +ATGGTTATCATGCAAAATATTGCTATTCCACATGCATACTCTGAAAAGCATGTACATAAAACAGCGATGAGTATGTTGAT +ATTACAAGAACCAATATACATGTCAGATGGCACAGCAATCCATATTATTGTACCTATTGCTGCTGTTGATAAAGTGACAC +ACTTAAGAGCGTTACTACAATTGAGAGATGTGGCGCAAGACAATGACGCAATTAAGCGCATCATACAAAGTCGCAAAAAT +TCTGATGTAAATGAGATTTTAAAAAATTATTCAAATAAAGAAGCGAGGGAAAATGGATGGGACAGCAATTAGTGCATAAA +GAAAATATAATGCTCAATTTGTCGGCAACTGATAAAGAATCCGTATTGTCACAAATGTCAGATGTGTTATTTCAAAATGG +GTTCGTGAAGTCAACGTTTAAAGATGCAGTCATCGACAGAGAAAAAGAATTTGCTACTGGTTTACCAACGCATCTATGTT +CGGTCGCTATACCGCATACAGATGTCGAACATATTAACCATAGAACGATAGGTGTGGCTGTTCTAGAAAAAGAAGTGCCG +TTTATTGAAATGGGAACACTTGATCAACAGACAGAAGTGAAAATCGTTTTTATGTTAGCAATGGATAAAGTAGATGATCA +ACTTAAGCTGTTACAACAGTTGATGCAAATTTTTCAAAGTGAAGAAAAATTGGAGCAGATTCTACGAACGAAAGATGAAA +CAATTTTAGCAACACTAATCAATGATTATTTGGAATATAACTAAAAATTAATTGGAGGAATTGAAAATGAAACAAGTATT +AGTAGCGTGTGGTGCAGGTATTGCAACGTCAACAGTAGTAAATAATGCAATTGAAGAAATGGCAAAGGAACACAATATTA +AAGTAGATATTAAACAAATCAAAATTACAGAAGTTGGACCTTATGAAGACACTGCAGATTTATTAGTTACAACTGCAATG +ACAAAAAAAGAATATAAATTCCCAGTTATCAACGCACGTAATTTCTTAACTGGTATTGGTATTGAAGAAACAAAACAACA +AATCTTAACAGAGTTACAAAAATAACGGAATTGATATGTAACGTGGGTCGATAACATATTAATATATGCAAATTGCATAT +AAATGACTATCACGACAAATGATTGTTGATGACATAGAAACGCAGTGACTGTAATTCAAAAGGACTGCAGTCGAATTTAG +CAAGGGTTGTCGTTGAAATGACTGTAACGTCAAATGTAATCACAATCGGAAAGTATCGTGACAATGCATATATAACAGGG +AGGGTTTAAATATGAGTTACTTCACTGATTTTGTAAGGGGATTTTTAGATTTAGGTGCAACTGTTATTTTACCGGTTGTC +ATATTCTTGCTTGGCCTATTCTTTAGGCAGAAAATTGGAGCGGCATTTAGGTCTGGTTTAACAATAGGTGTGGCTTTTGT +AGGGATTTTCTTAGTCATCGATTTATTAGTTAAAAATTTAGGGCCAGCAGCACAAGCGATGGTTAAAAATTTAGGCGTCA +GTCTGAATGTGATTGATGTAGGTTGGCCAGCAACATCATCTATCGCTTGGGCATCATCTGTCGCAGCATTTATTATTCCA +CTCGGAATCATAGTTAACGTTGTATTGCTAGTAACTAAAGTGACAAAGACGATGAATGTAGATATTTGGAATTTTTGGCA +TTATACGTTTACAGCAGCAATGGTTTATGCCGTATCAGGCAGTATTTGGCAAGCGTTATTAGCAGCAGTTATTTTCCAAA +TTATCTGTTTGAAAGTAGCAGATTGGACAGCACCGATGATGAGTGAGTTCTTTGATTTACCAGGTGTATCGATTGCTACA +GGAAGTACAATTTCTTATGCACCAGGTATTTACTTAGTTAAATTGTTACAAAAAGTACCCGGTCTGAATAAGTTAGATGC +TGATCCTGAAACAATTCAAAAACGTTTTGGCGCATTTGGAGAGTCTATATTTGTCGGCTTAATTTTAGGTTTAGGTATTG +GTGTGTTAGCAGGTTACAAACCTGGAGACATCATTAATTTAGGAATGTCAATGGCTGCAGTAATGGTATTAATGCCTAGA +ATGGTAAAAATCTTAATGGAAGGTTTAATGCCAGTTTCAGAGTCTGCAAGAACATGGCTAAATAAACGTTTTGGCGAACG +TGAAATTTATATTGGATTGGATGCGGCTGTAGCATTAGGTCATCCAGCAGTTATTTCGACAGCATTAATTTTAGTACCTA +TCACTGTTTTATTAGCCGTTATTTTACCAGGAAACCAAGTACTACCTTTTGGTGACTTAGCAACGATACCATTTGTTGTC +GCGTTTATTGTTGGTGCAGCAAGAGGAAACATTATTCATTCTGTCATTGTGGGTACGATTATGATTGCAATTTCACTATA +TATTGCAACAGACGTAGCACCCATTTTCACAGATATGGCGAAAGGTACGAATGTACAAATGCCAAAAGGTTCATCTGAAA +TTTCAAGTATTGATCAAGGTGGTAATATCGTTAACTATCTTATCTTTAAACTATTTAGTCTATTCAATTAAAAATCGAGG +TGTTTGTGGTGAAAGCTTTAGTAAAAACAAGAGAAGGACATGGCAACTTAGAACTTCTTGATAAAGAAGTTGCAACACCG +CTAGATGATAAAGTAAAGATTAAAGTACATTATGCAGGAATTTGTGGCACAGATATTCATACTTATGAAGGTCATTATAA +AGTTAATTTTCCAGTGACATTAGGTCATGAATTTTCTGGTGAAATCGTTGAAGTTGGAGCAGACGTTAAAGATTTTAAAG +TTGGTGACCGTGTCACATCTGAAACGACATTCTATGTTTGTAATGAGTGTGAATACTGTAAATCAAAAGACTATAATTTA +TGCAACCATCGAAAAGGTATTGGAACACAAGTTGATGGCGCATTTACTAATTATGTCATTGCACGTGAAGAAAGTTTGCA +TCATATTCCAGACGAAGTATCGTATCAGTCTGCAGCTATGACAGAACCATTAGCATGTGCACATCATGGCGTTTCTAAGA +TTCAAGTCAATTCAGGCGATGTAGCAGTTGTAATGGGACCTGGGCCAATCGGATTACTTGTAGCACAAGTGTTAAAAAGT +AAAGGCGCAACTGTTGTGGTAACTGGATTGGACAATGACAAAGTCAGATTAGATAAAGCAGAAGCATTGCACATGGATTA +TGTAGTCAATTTACAACAAACAGACTTAAAAACGTATATCAATGGAATTACAGACGGTTACGGTGCAGATGTTGTTGTTG +AATGTTCAGGTGCAGTTCCAGCAGCACGACAAGGTTTGGATATTTTACGCAAAAAAGGTTTCTACAGTCAAATAGGTATT +TTTAAGGATGCTGAAATTCCATTTGATATGGAAAAAGTGATTCAAAAAGAAATAACAGTTGTTGGTAGTAGAAGTCAAAA +GCCAGCAGATTGGGAACCTTCATTGCAACTTATGGCGGATGGTTTAGTAAATGCTGAAGCTTTGGTGACAAAAATATATG +ATATTTCGAAATGGGACGAGGCGTATCAACATTTAAAATCCGGCGAAGGTATTAAAGCATTACTTAAGCCGCTCGATTTA +GATGAAAATGAAGGAGAGAATTAATATGGTAGAATCAATGCTAACTTTTATGCTTGGGCCATTAAGACAAATCACTGATT +TTTATATGGAACATTTACTCGTAAGTAATTCCATTGTCATTGCAGGTTATTTTGCGACAGGTATTTTTAAAAAGAAAAAA +GTTGTGAATTAAATCAAATTTGAGGTGATTTACAAGTGAAAGCATTGAAATTATATGGCGTGGAAGATTTACGGTATGAG +GATAATGAAAAGCCAGTCATTGAAAGTGCGAATGACGTTATTATTAAAGTACGAGCGACTGGCATATGTGGTTCAGACAC +GTCACGATACAAAAAAATGGGGCCATACATTAAAGGTATGCCATTTGGTCATGAATTTTCAGGTGTAGTAGATGCCATTG +GAAGTGATGTTACGCATGTTAATGTGGGCGACAAAGTGACAGGTTGCCCAGCAATACCTTGTTATCAATGCGAGTATTGT +TTAAAAGGTGAATATGCACGATGTGAAAAGTTATTCGTCATTGGCTCATATGAACCTGGATCGTTCGCGGAATATGTCAA +ATTGCCAGCGCAAAATGTTTTAAAGGTTCCAGACAATGTTGATTACATTGAAGCAGCAATGGTTGAGCCATCAGCCGTTG +TTGCGCATGGGTTTTATAAATCGAATATACAACCTGGTATGACTGTTGCAGTAATGGGGTGTGGCAGTATAGGTTTGTTA +GCTATTCAATGGGCACGAATATTTGGTGCTGCACATATCATCGCTATAGATATAGATGCGCATAAACTAGATATTGCAAC +ATCATTGGGCGCACATCAAACAATCAATTCAAAAGAAGAAAATCTTGAGAAATTCATCGAAAATCATTACGCCAATCAAA +TCGATTTAGCTATAGAATCATCAGGTGCTAAAGTTACGATTGGTCAAATATTGACGCTACCTAAAAAAGGTGGCGAGGTG +GTATTACTCGGAATACCATATGATGATATTGAGATTGATCGCGTTCATTTTGAAAAAATTCTGCGTAACGAGTTGACAGT +ATGTGGCTCTTGGAACTGTTTGTCCAGTAATTTTCCGGGCAAAGAGTGGACGGCAACCTTACATTATATGAAGACGAAAG +ATATTAATGTAAAGCCTATTATTTCTCATTTTTTACCGTTAGAAAAAGGCCCGGAGACATTTGATAAATTAGTTAACAAG +AAAGAACGATTTGATAAAGTCATGTTTACGATTTATTAGTATGCACCTTTGAGGACGAAAACGCTGGTATAGTTATAGCT +ATGAAAGTGCGAATGCCGTCTGGTCTATAGATACTATCGAAATAATTCATCTTCGAATATACGTTGATAAATAGCCGGTT +TACTTGTGTGAAATATGCTTGTGAATCGGTTGTTTTGCATTTTGTATACTTAAAATGAGATGGCAATATTTGATAATTTT +TAAAGTGAAAATCAAGTACAGCCACTTAATAAGATAAATTTATTATAATATATGGTAAAATGATGGCAGTAATAATGAAT +TTGAAAAAGAGTAAACATTAATACCTTTAACAATTTAATATCGTCAGAGTTAATGATTAACTGCATGGCAAAACAACTTA +GAATGGTCAGTTACAAAAATACATTTTTATAAAAAATTATCACACTATTGTGACAACTATCTTTGGATTAATAAAAGAGG +CAAGTGAGCAATAGGTTAGGCTTATGTGCGGGCATAGGTCAGTAATGTATAAATGGAAATGATGTAATGACAGAATGGAG +GACAACATGATTTATGCAGGTATTTTAGCAGGAGGTATTGGTTCGAGAATGGGGAACGTGCCATTACCAAAACAATTTTT +AGATATTGATAATAAACCGATTTTAATCCATACAATTGAGAAGTTCATTTTAGTGAGTGAATTTAATGAGATTATTATCG +CAACGCCAGCACAGTGGATTTCCCATACACAGGATATTTTAAAAAAATATAACATTACAGATCAACGTGTCAAAGTAGTT +GCAGGTGGTACGGATCGAAATGAAACAATTATGAACATTATCGACCATATTCGCAATGTAAATGGAATTAATAATGATGA +TGTGATTGTAACTCATGATGCCGTAAGACCATTTTTAACTCAACGTATTATTAAAGAGAACATTGAAGTAGCAGCAAAAT +ATGGTGCAGTAGATACAGTCATTGAAGCAATTGATACGATTGTAATGTCTAAAGATAAACAGAACATACACAGTATCCCT +GTAAGGAATGAAATGTATCAAGGCCAAACACCACAATCATTTAATATTAAATTATTACAAGATAGTTATCGCGCCTTAAG +TAGTGAACAAAAAGAAATCTTATCAGATGCATGTAAAATCATTGTCGAATCTGGACATGCAGTTAAATTGGTACGTGGAG +AACTATACAACATTAAAGTGACAACACCGTATGATTTAAAAGTAGCAAATGCCATTATTCAAGGTGATATTGCCGATGAT +TAATCAAGTATATCAACTCGTTGCACCGAGACAGTTCGACGTCACATATAATAATGTTGATATTTATGGTAATCATGTCA +TCGTAAGACCTTTATACTTGTCTATTTGTGCAGCTGATCAAAGGTATTACACAGGTCGAAGAGATGAAAATGTACTACGC +AAAAAATTACCAATGTCATTAGTTCATGAAGCTGTTGGTGAAGTTGTATTTGATAGTAAAGGGGTATTTGAAAAAGGTAC +GAAAGTAGTAATGGTACCGAATACACCTACAGAGCAACATCATATTATTGCGGAGAATTACTTAGCCTCTAGTTATTTTA +GATCTAGTGGTTATGATGGTTTTATGCAAGACTACGTTGTGATGGCACATGATCGTATCGTTCCGCTGCCTAATGACATT +GATTTGAGTACGATTTCATACACAGAGTTAGTGTCAGTAAGTTATCATGCTATACAACGATTTGAACGTAAATCTATACC +TTTGAAAACCAGCTTTGGTATTTGGGGTGATGGTAACTTAGGTTATATTACTGCTATTTTGCTACGTAAGTTGTACCCAG +AAGCTAAAACTTATGTATTTGGTAAGACAGACTATAAATTAAGTCATTTTTCATTTGTAGATGACATCTTTACAGTAAAT +CAAATACCAGATGATCTTAAAATTGATCATGCATTTGAATGTGTTGGAGGTAAAGGAAGTCAAGTTGCACTTCAACAAAT +AGTTGAACATATTTCACCAGAAGGCAGTATTGCTTTGTTAGGCGTAAGTGAATTACCCGTGGAAGTGAATACACGATTAG +TACTTGAAAAAGGATTAACGTTGATTGGTAGTAGTCGAAGCGGCTCTAAAGATTTTGAGCAAGTTGTTGATTTATATCGT +AAGTACCCAGACATAGTTGAAAAGTTAGCATTATTAAAAGGACATGAAATTAATGTATGTACGATGCAAGATATCGTCCA +AGCGTTTGAAATGGATTTATCGACATCTTGGGGAAAAACAGTATTGAAATGGACGATTTAATAAACCGATGGGGGAATAA +CAATGACAAAAACGAAACAAGCAATACATATTGATAACATATACTGGGAACGTGTTCAGTTATATATTGAAGGACATAGT +GAAGGTGTCGATTTAACATCAGGACAATTTGTTCTGAGGAATTTAACCGAAACAAAAACATTAGAAGCAAATGAAATGAA +AATAGACGGTAATACATTTATATGTAGATTCAACGTCGCAATATTAGACGATGGGTATTATTTACCAATGGATAAATATT +TATTTGTTTATCATGACCAGTTAGAGTATATTGGACAACTTAATCCAAATATTATTGATCAAGCTTATGCGGCATTAAAT +GAAGAGCAAATTGAAGAATACAATGAGCTGACTACACAAAATGGAAAAGTGAACTATTTATTAGCGTATGATGCTAAAGT +TTTCCGTAAAGGTGGCGTATCACAACATACGGTCTATACCATTACTCCGGAAATAGCAAGTGACGTTAACGAATTTGTAT +TTGATATTGAAATCACCTTACCTCAAGAGAAATCAGGGGTCATTGCGACAAGTGCACACTGGCTTCATAAACAAGGTCAT +AAAGCTTCATTTGAAAGTAGAAGTTTCTTATTTAAAGCTATTTTTAATATTACAAAGTTACTACATATTAAAAGAAGCAA +AACAATATTATTCACATCAGATTCGCGTCCGAATTTATCAGGGAATTTCAAGTATGTATATGATGAGTTACTACGCCAAA +AAGTAGATTTTGATTATGATATTAAAACGGTATTTAAGGAGAATATTACGGATAGACGCAAATGGAGAGACAAGTTTAGA +TTGCCATATTTACTTGGTAAGGCCGATTATATTTTTGTTGATGATTTCCATCCATTAATTTATACGGTTCGCTTTAGACC +ATCACAAGAAATTATTCAAGTGTGGCATGCTGTTGGTGCCTTTAAAACAGTTGGCTTTAGTCGTACAGGTAAAAAAGGTG +GTCCGTTTATCGATTCATTAAACCATCGTAGTTACACGAAAGCATATGTTTCATCAGAAACCGATATTCCATTTTATGCT +GAAGCATTTGGAATTAGAGAAGAAAATGTTGTACCAACAGGTGTACCACGTACTGATGTACTATTTGATGAAGCTTATGC +AACACAAATTAAACAAGAGATGGAAGATGAATTGCCAATTATAAAAGGTAAGAAAGTTATTCTATTCGCACCGACATTTA +GAGGTAATGGTCACGGTACGGCACATTATCCATTTTTTAAAATTGATTTTGAACGTTTAGCAAGATACTGCGAGAAGCAT +AATGCAGTTGTGTTATTCAAAATGCATCCGTTCGTAAAAAATAGACTTAATATTTCACGTGAACATAGACAATACTTTAT +CGATGTGTCAGATCATCGTGAAGTTAACGATATTCTCTTTGTTACAGACTTGTTGATTAGTGATTATTCATCTTTAATAT +ATGAATATGCAGTATTTAAAAAGCCGATGATTTTCTATGCATTTGACTTAGAAGATTACATTACGACGCGTGATTTCTAT +GAACCATTTGAATCATTTGTTCCAGGTAAAATTGTACAGTCCTTTGATGCATTAATGGATGCTTTGGACAATGAAGATTA +TGAGGTTGAAAAAGTTGTGCCATTCTTAGATAAACATTTTAAATATCAAGATGGTCGCTCAAGTGAACGTTTAGTCAAAG +ATTTGTTTAGACGCTAATGTTGGCATATATTACTTGCCATGCAAATGAGTCATAAGGCATAGTTTTCAAGAAGGGTTCTG +ACAATGAAGTGAGCAACATGTCATGATGAGAAGCAGGACTATACAATGAGAATAACCTTTTGATTTTTCATGCATAAGGC +GATTCAATCAAAAGCAATCACCTTCCAACTGAATTGTCATTTTGTAAAATAAAATAATCGATCCAATCGTTATCTAATTC +ATAAATGTGTAAACACATACGTTATGAATGGATAACGATTTTTTGTTATGTTAAAGTGGTACATTAATCATGTATTTCGT +ATGATAATTAACGACAAGTGTAATGGTTAAATGTATTTTATGATGAAATGCTATAATAGGCATGGTTACAATGAGCTTGC +TCATACATATTAATATAATTACAAAAACACGTCGGAGGTACGACATGATTAAAAATACAATTAAAAAATTGATAGAACAT +AGTATATATACGACTTTTAAATTACTATCAAAATTGCCAAACAAGAATCTAATTTATTTTGAAAGCTTTCATGGTAAACA +ATACAGCGACAACCCCAAAGCATTATATGAATACTTAACTGAACATAGCGATGCCCAATTAATATGGGGTGTGAAAAAAG +GATATGAACACATATTCCAACAGCACAATGTACCATATGTTACAAAGTTTTCAATGAAATGGTTTTTAGCGATGCCAAGA +GCGAAAGCGTGGATGATTAACACACGTACACCAGATTGGTTATATAAATCACCGCGAACGACGTACTTACAAACATGGCA +TGGCACGCCATTAAAAAAGATTGGTTTGGATATTAGTAACGTTAAAATGCTAGGAACAAATACTCAAAATTACCAAGATG +GCTTTAAAAAAGAAAGCCAACGGTGGGATTATCTAGTGTCACCTAATCCATATTCGACATCGATATTTCAAAATGCATTT +CATGTTAGTCGAGATAAGATTTTGGAAACAGGTTATCCAAGAAATGATAAATTATCACATAAACGCAATGATACTGAATA +TATTAATGGTATTAAGACAAGATTAAATATTCCATTAGATAAAAAAGTGATTATGTACGCGCCAACTTGGCGTGACGATG +AAGCGATTCGAGAAGGTTCATATCAATTTAATGTTAACTTTGATATAGAAGCTTTGCGTCAAGCGCTGGATGATGATTAT +GTTATTTTATTACGCATGCATTATTTAGTTGTGACACGTATTGATGAACATGATGATTTTGTGAAAGACGTTTCAGATTA +TGAAGACATTTCGGATTTATACTTAATCAGCGATGCGTTAGTTACCGACTACTCATCTGTCATGTTCGACTTCGGTGTAT +TAAAGCGTCCGCAAATTTTCTATGCATATGACTTAGATAAATATGGCGATGAGCTTAGAGGTTTTTACATGGATTATAAA +AAAGAGTTGCCAGGTCCAATTGTTGAAAATCAAACAGCACTCATTGATGCATTAAAACAAATCGATGAGACTGCAAATGA +GTATATTGAAGCACGAACGGTATTTTATCAAAAATTCTGTTCATTAGAAGATGGACAAGCGTCACAACGAATTTGCCAAA +CGATTTTTAAGTGATAACTTAAAAACAATAAAAAATTATAAATTAATTAGTTAAGTGATATAAATAATAAACGAAATGTT +TGCTTGTATGTTATTATTTGTGTATGAAAACGTCTGATATAATGTAATATAGGTTTAAACAAAATATGCTAAAATATAAG +CAAAATATGTATAATTTTAAGTAATACACAGACATTTAGAGTTTTGATTTTAAAATGAGTTTTCAATTGGAAAATGCAAC +GAAATTTAAATAATTAAATATAGATATAGTTGAATGGAGGAAGTATTTTATGAAATACGCTGGTATTCTAGCTGGAGGTA +TAGGCTCAAGAATGGGTAACGTACCTTTACCTAAACAATTTTTAGATTTAGACAACAAACCGATTTTAATCCATACATTA +GAAAAATTTATTTTAATTAATGATTTTGAAAAAATTATTATCGCGACGCCACAACAATGGATGACGCATACGAAAGATAC +ACTTAGAAAATTCAAAATTTCTGATGAAAGAATTGAAGTCATTCAAGGTGGTAGCGATCGTAACGATACAATTATGAATA +TCGTTAAACATATTGAATCAACAAATGGTATTAACGATGACGATGTCATTGTGACACATGATGCAGTTAGACCATTTTTA +ACGCATCGTATTATTAAAGAAAATATTCAAGCTGCTTTAGAGTACGGTGCAGTAGATACAGTGATTGATGCTATAGATAC +GATTGTTACATCTAAAGATAATCAAACGATTGATGCAATTCCAGTGCGTAATGAAATGTACCAAGGTCAAACACCTCAAT +CGTTTAATATTAATTTATTAAAAGAAAGCTATGCACAGTTGAGTGATGAGCAAAAGAGTATTTTATCTGATGCTTGTAAG +ATTATTGTAGAAACAAACAAACCGGTTCGACTTGTAAAAGGTGAGTTATATAACATTAAAGTAACAACACCTTACGATTT +AAAAGTAGCGAATGCTATTATTCGAGGTGGTATTGCCGATGATTAATCAAGTATATCAATTAGTTGCACCTAGACAATTT +GAAGTTACGTATAACAACGTAGATATTTACAGTGACTATGTCATTGTACGTCCTTTATATATGTCAATTTGTGCTGCCGA +TCAAAGATATTATACTGGTAGCCGTGATGAGAATGTCTTATCTCAGAAATTGCCAATGTCTTTAATTCATGAAGGTGTTG +GTGAGGTCGTATTTGACAGTAAAGGTGTGTTTAATAAAGGTACAAAAGTAGTTATGGTACCGAATACGCCGACAGAAAAA +GACGATGTCATTGCTGAAAACTATTTAAAATCGAGCTACTTCAGATCAAGTGGACATGATGGGTTTATGCAAGATTTTGT +GTTGCTAAATCATGATAGAGCTGTACCACTACCTGATGATATTGATTTAAGTATTATTTCATATACAGAGCTTGTAACAG +TAAGTTTGCATGCTATTCGTCGTTTTGAAAAGAAATCTATTTCAAATAAAAATACATTTGGTATTTGGGGTGATGGTAAC +TTAGGTTACATTACAGCCATTTTATTACGTAAATTATATCCAGAGTCTAAAATATATGTCTTTGGTAAAACAGATTATAA +ATTGAGTCACTTCTCATTTGTTGATGATGTCTTCTTTATTAATAAAATACCTGAAGGCTTAACATTTGATCATGCATTTG +AGTGTGTGGGTGGTCGCGGTAGTCAATCAGCCATAAATCAAATGATCGATTACATTTCACCAGAAGGAAGCATTGCACTG +TTAGGTGTAAGTGAGTTCCCAGTAGAAGTTAATACACGTCTAGTATTGGAAAAAGGACTAACGTTGATTGGTAGTAGTCG +AAGTGGTTCAAAAGATTTCCAAGATGTTGTAGACTTATACATTCAATACCCAGATATTGTAGATAAATTAGCGTTGTTAA +AAGGTCAAGAATTTGAAATTGCAACAATTAATGATCTTACAGAAGCTTTTGAAGCAGACCTGTCTACATCTTGGGGTAAA +ACAGTATTAAAATGGATTATGTAAATAGAAAAGGATGAATTAACGTTGGTTAAAAGTAAGATATATATAGATAAAATCTA +TTGGGAACGTGTTCAGTTATTCGTTGAAGGACATAGTGAAAACCTAGATTTAGAAGATAGTAATTTTGTATTAAGAAATT +TAACTGAGACACGTACAATGAAGGCGAATGATGTCAAAATAGATGGGAATCAATTCGTTTGTCGTTTCAATGTAGCTATC +TTAGATAATGGTTATTACTTACCTGAAGATAAGTACTTATTAGTGAATGAGCAAGAACTTGATTATATTGCACAGTTAAA +CCCAGATGTGATTAATGATGCATATCAAAATCTAAAGCCAGAACAAGAAGAAGAATACAACGAATTAGAAACACAAAATG +GTAAAATCAATTTCTTATTGCAGACTTACCTAAAAGAATTTAGAAAAGGCGGCATTTCGAAGAAAACGGTTTATACTGTT +ACACCTGAAATTTCTAGCGATGTTAATGAATTTGTCCTTGATGTTGTTGTAACGACTCCGGAAGTTAAAAGTATTTATAT +CGTTCGTAAATATAAAGAATTACGTAAGTATTTCCGCAAACAATCATTTAATACAAGACAATTTATTTTTAAAGCGATAT +TTAATACGACGAAATTTTTCCACTTGAAAAAAGGGAATACGGTGTTGTTCACATCAGACTCTAGACCAACGATGTCTGGA +AACTTTGAATACATCTATAACGAAATGTTACGTCAAAATTTAGATAAAAAGTATGATATTCACACTGTTTTTAAAGCGAA +TATTACAGATAGACGTGGCATCATCGACAAGTTTAGATTGCCATATTTACTTGGGAAGGCAGACTACATCTTTGTTGATG +ACTTTCACCCATTGATTTATACAGTGCGTTTTAGACGTTCTCAAGAAGTTATTCAAGTATGGCATGCCGTTGGTGCCTTT +AAAACAGTTGGCTTTAGTCGTACTGGTAAAAAGGGTGGACCATTTATTGATTCATTAAATCATCGTAGCTATACAAAAGC +TTATGTATCATCTGAAACCGATATTCCATTCTACGCTGAAGCATTTGGTATTAAAGAGAAAAATGTAGTGCCTACAGGTG +TTCCACGTACTGATGTACTATTTGATGAAGCTTATGCGACACAGATCAAACAAGAGATGGAAGATGAATTACCAATTATT +AAAGGTAAGAAAGTCATTCTTTTCGCACCAACATTTAGAGGTAGTGGTCATGGTACAGCACATTACCCATTTTTCAAAAT +TGATTTTGAACGTTTAGCAAGATATTGCGAAAAAAATAACGCGGTTGTATTATTTAAAATGCATCCATTTGTGAAAAATA +GACTTAATATTGCAGACAAACATAAACAATATTTTGTTGACGTTTCTGACTTTAGAGAAGTTAATGATATACTGTTCATA +ACAGATTTATTAATTAGTGACTATTCATCTTTAATATATGAATATGCAGTATTTAAAAAGCCAATGATTTTCTATGCATT +TGATTTAGAAGATTATATTACGACGCGTGATTTTTATGAACCATATGAATCATTTGTTCCAGGTAAAATTGTGCAATCAT +TTGACGCATTAATGGACGCCTTGGACAATGAAGATTATGAAGGAGAAAAAGTCATTCCATTCTTAGATAAACATTTTAAA +TATCAAGATGGCCGATCAAGTGAGCGTTTAGTCAGAAATTTATTTGGTAGCTAAGTTTATATAGTAGTCAAAGTGGGAGA +GGTATAATGATGAAATTTTCAGTAATAGTTCCAACATACAATTCAGAAAAGTATATAACAGAATTACTTAATAGCCTTGC +GAAACAAGATTTTCCGAAAACTGAATTTGAAGTGGTTGTAGTTGATGACTGTTCAACAGATCAAACGTTACAAATAGTTG +AAAAGTATCGCAATAAATTGAACTTGAAAGTAAGTCAACTCGAAACAAATTCTGGTGGTCCAGGTAAACCTAGAAATGTG +GCGTTAAAACAAGCAGAAGGTGAATTTGTATTATTTGTGGACTCCGATGACTATATAAACAAAGAGACTTTAAAGGATGC +AGCAGCATTTATTGATGAACATCACTCAGATGTCTTATTGATTAAAATGAAAGGTGTTAATGGTCGTGGTGTACCACAAT +CTATGTTTAAAGAAACAGCACCTGAAGTTACTTTGTTAAATTCAAGAATTATCTATACTTTAAGCCCGACTAAAATCTAT +AGAACAGCATTACTAAAAGATAATGACATTTATTTTCCAGAAGAATTAAAGAGTGCAGAAGATCAATTATTTACAATGAA +AGCATATTTAAATGCAAATCGAATCAGTGTGTTAAGTGATAAAGCGTATTATTATGCTACAAAGCGTGAAGGTGAACATA +TGAGTAGTGCGTATGTTTCACCTGAAGACTTTTATGAAGTCATGAGATTGATTGCTGTAGAAATATTAAATGCAGATTTA +GAAGAAGCCCATAAAAATCAAATCTTAGCAGAATTTTTAAATCGTCATTTTAGTTTTTCTCGTACGAATGGCTTCTCACT +TAAAGTTAAACTAGAAGATCAACCACAATGGATTAATGCTCTAGGAGACTTTATACAAGCAGTTCCAGAACGTGTAGATG +CATTGGTGATGAGTAAATTACGACCATTGTTGCACTACGCGAGAGCGAAAGATATAGACAACTATAGAACTGTGGAAGAA +AGTTACCGTCAAGGTCAATACTACCGTTTTGATATTGTAGATGGTAAATTAAACATTCAATTCAATGAAGGCGAACCATA +CTTTAAAGGCATTGATATCGCTAAGCCAAAAGTGAAAATGACAGCATTTAAATTTGATAATCATAAAATTGTTACAGAGC +TAACGTTAAATGAATTTATGATTGGCGAAGGACATTATGATGTCAGACTTAAATTACATTCACGAAACAAGAAGCACACA +ATGTATGTACCTTTAAGTGTCAATGCGAATAAACAATATCGTTTTAACATTATGTTAGAAGATATTAAAGCGTATTTACC +TAAAGAAAAAATTTGGGATGTTTTCTTAGAAGTCCAAATAGGTACGGAAGTATTTGAAGTGCGTGTTGGTAATCAACGTA +ATAAATATGCATATACTGCAGAAACAAGTGCATTAATTCATTTGAATAATGATTTTTATAGATTAACACCGTATTTCACA +AAAGACTTTAATAACATTTCGTTATACTTTACAGCTATTACATTAACGGATTCAATCTCATTGAAGTTAAAAGGTAAAAA +CAAAATCATTTTAACTGGTCTGGATCGTGGTTATGTATTTGAAGAAGGTATGGCTAGTGTCGTACTAAAAGACGACATGG +TGATGGGAATGTTAAGCCAAACATCAGAAAACGAAGTGCAAATCTTACTTAGCAAAGATATTAAAAAGCGAGACTTCAAA +AATATTGTTAAGTTAAACACTGCACATATCACTTATCCACTAAATAAATAATAAATGCCCTCAAATCATTGTGAGCCAAC +ATGATTTGAGGGCTTTATTTTGCTGTTTATGACATGATTATGACATTTCCCTGATTTTCATTTTCATATACATTAAATTG +TATACACTGGAAATGAGGAGGTTATCTATAATGATAAATAAAAATGACATAGTAGCAGATGTAGTAACTGATTATCCGAA +AGCAGCGGATATTTTTAGAAGTGTGGGAATAGATTTTTGTTGTGGCGGACAAGTAAGTATAGAAGCAGCAGCCTTAGAAA +AGAAAAATGTAGATTTGAACGAATTATTACAGCGTCTCAACGACGTTGAACAAACGAATACACCAGGTTCGTTAAATCCT +AAATTTTTAAATGTTTCATCACTTATTCAATATATTCAATCAGCATATCATGAACCTCTAAGAGAAGAATTTAAAAATTT +AACACCTTATGTGACGAAGTTATCGAAAGTACATGGACCTAATCATCCATATTTAGTTGAGTTAAAAGAAACATACGATA +CATTTAAAAATGGCATGTTAGAGCATATGCAAAAAGAAGACGATGTCGATTTTCCAAAACTCATTAAATATGAGCAAGGT +GAGGTAGTAGACGATATTAATACTGTGATAGATGATTTAGTTTCAGACCACATTGCAACGGGAGAATTGTTAGTAAAAAT +GAGCGAATTAACATCTAGTTATGAACCTCCGATAGAAGCGTGTGGTACTTGGCGACTTGTTTATCAGAGATTAAAAGCAC +TTGAAGTGTTAACACATGAACACGTACATTTAGAGAATCACGTATTATTTAAAAAAGTATCATAAATAACGCGATTAGAA +ACTGTTGGCAAAAATAAGTCCAGCAGTTTTTCGCTATGTATAAAAGTCATAATAGTGACATAAACAGCATTATTTGAAAA +GAAAAATGGTCAACTTAGCATAAAAATTGATATGAAAATTTAATGGTATAGATAATTAAATAGTAGCGTGTTTTTTTAAT +AATTTATTCATGAATTTTACATGCACTATTATGATAAAATAAACATAATTATAATTCACTGAGGTGCTATCGTGCTATCG +CTAACAATGTTATTACTTGAGCGTGTAGGTTTAATTATTATTTTGGCCTATGTGTTGATGAATATTCCATATTTTAAAAA +CTTAATGAATCGTCGACGTACATGGAAAGCACGTTGGCAATTATGTATTATTTTCAGTTTGTTTGCCTTAATGTCTAATT +TAACTGGTATCGTCATCGATCATCAACATAGTTTGTCAGGAAGTGTGTACTTCCGTTTAGATGATGATGTATCTTTAGCT +AACACACGTGTATTAACGATAGGTGTCGCAGGATTAGTTGGTGGCCCTTTTGTAGGTCTATTTGTTGGCGTTATTTCAGG +TATTTTCAGAGTGTATATGGGTGGGGCGGATGCACAAGTTTATCTTATCTCATCTATATTTATTGGTATAATTGCTGGTT +ATTTTGGCTTACAAGCTCAAAGACGCAAGCGTTACCCGAGTATTGCGAAAAGTGCCATGATTGGAATTGTTATGGAAATG +ATTCAAATGTTGAGCATTTTAACATTTTCCCACGACAAAGCATATGCGGTTGACCTCATATCATTAATTGCACTACCAAT +GATTATTGTTAATAGCGTTGGTACGGCGATTTTTATGTCTATTATCATTTCAACATTAAAGCAAGAGGAGCAAATGAAGG +CTGTTCAAACACATGATGTACTGCAATTGATGAACCAGACATTGCCGTATTTTAAAGAAGGATTGAATAGAGAATCGGCA +CAGCAAATTGCGATGATTATTAAAAATTTAATGAAAGTATCTGCCGTAGCAATTACAAGCAAAAATGAAATCTTATCGCA +TGTAGGTGCAGGTAGTGATCATCACATACCAACAAATGAAATATTAACAAGTCTGTCTAAAGATGTATTGAAATCAGGAA +AGTTGAAAGAAGTTCATACTAAAGAAGAGATTGGTTGTAGTCATCCGAATTGCCCGCTTAGAGCAGCTATCGTGATACCA +CTTGAGATGCATGGTTCTATCGTCGGTACATTGAAGATGTATTTTACAAACCCTAATGATTTAACTTTTGTGGAACGTCA +ACTTGCAGAAGGATTGGCAAATATTTTTAGTAGCCAAATTGAACTTGGTGAAGCCGAAACGCAAAGTAAGTTATTGAAAG +ATGCTGAGATTAAGTCATTACAGGCACAAGTGAGTCCACATTTTTTCTTCAATTCAATTAACACGATCTCAGCTTTAGTT +AGAATAAATAGCGAAAAGGCACGAGAGTTACTATTAGAATTGAGTTATTTTTTCAGAGCGAATTTACAAGGCTCTAAGCA +ACATACGATTACTTTAGATAAAGAGTTAAGTCAAGTGCGTGCATACTTATCACTCGAACAAGCACGTTATCCAGGAAGAT +TTAATATCAATATTAATGTTGAAGACAAATATCGCGATGTGCTTGTACCACCATTTTTAATTCAAATTTTAGTTGAAAAT +GCCATCAAACATGCGTTTACGAATCGAAAGCAAGGTAACGATATTGACGTGTCAGTGATTAAAGAAACTGCAACACATGT +ACGTATTATTGTACAAGATAATGGTCAGGGTATTTCTAAAGATAAAATGCATTTGTTGGGAGAAACATCTGTAGAATCAG +AGTCTGGAACTGGTAGTGCTTTAGAAAATTTAAACTTACGCCTAAAAGGATTATTTGGAAAATCCGCAGCATTACAATTT +GAATCGACATCGAGCGGTACCACTTTTTGGTGTGTACTTCCTTATGAAAGACAAGAGGAGGAATAAATATGAAAGCATTA +ATCATAGATGATGAGCCATTAGCACGTAATGAATTAACATATTTATTAAATGAAATTGGTGGTTTTGAAGAAATTAATGA +GGCAGAAAATGTAAAAGAAACATTGGAAGCACTACTGATCAATCAATATGACATTATATTTTTAGATGTCAATTTAATGG +ATGAAAATGGGATCGAATTAGGAGCTAAGATTCAAAAGATGAAAGAGCCACCTGCGATTATTTTTGCAACTGCACATGAC +CAATACGCAGTACAGGCATTTGAATTAAATGCGACAGACTATATTTTGAAACCGTTTGGTCAAAAACGTATTGAACAAGC +AGTCAATAAAGTGCGTGCGACTAAAGCCAAAGATGATAATAACGCAAGTGCAATTGCGAATGATATGTCGGCGAATTTTG +ATCAAAGCTTACCTGTTGAAATTGACGATAAAATTCACATGTTAAAGCAACAAAATATTATTGGGATTGGCACACATAAT +GGTATTACAACCATACATACAACGAATCATAAATACGAAACAACAGAGCCATTGAATCGTTATGAAAAACGATTGAATCC +CACTTATTTTATACGTATTCATCGTTCATATATTATTAACACGAAACACATTAAAGAAGTGCAACAATGGTTTAACTATA +CTTATATGGTAATATTGACAAATGGTGTCAAGATGCAAGTTGGACGTTCATTTATGAAAGATTTTAAAGCGTCGATAGGA +TTACTTTAACAGTAATCCTTTTTTTTATGCATTTTACCTATGATATTTTGTATTTCGGACTAAAAATCACGCAAATCGAA +GTGAGCCATCTATACTTTAGTTAAATCAAACGTAGGAGGCAATGGTCGTGAAACAACAAAAAGACGCATCAAAACCAGCA +CACTTTTTTCACCAAGTCATTGTAATTGCTTTAGTACTCTTTGTATCGAAAATAATTGAATCATTTATGCCAATTCCTAT +GCCTGCATCAGTAATCGGTTTAGTATTATTATTTGTATTATTATGTACTGGTGCTGTTAAGTTAGGCGAAGTCGAAAAAG +TAGGAACGACACTAACAAATAACATTGGCTTACTCTTCGTACCAGCCGGTATCTCAGTTGTTAACTCTTTAGGTGTCATT +AGCCAAGCACCATTTTTAATCATTGGACTAATAATCGTCTCAACAATACTATTACTTATTTGTACTGGCTATGTCACACA +AATTATTATGAAAGTTACTTCGAGATCTAAAGGTGACAAAGTCACAAAAAAGATCAAAATAGAGGAGGCACAAGCTCATG +ATTAACCACTTAGCACTAAACACACCTTACTTCGGAATACTGTTATCCGTTATACCATTTTTCTTAGCGACCATATTATT +TGAAAAAACTAATCGTTTCTTCTTATTCGCACCGCTATTTGTCAGTATGGTATTTGGTGTGGCCTTCCTCTATTTAACAG +GCATTCCGTATAAGACTTACAAAATAGGTGGAGACATTATTTACTTCTTCTTAGAACCGGCAACAATCTGTTTTGCGATT +CCGTTATATAAAAAGCGTGAAGTGCTTGTTAAACATTGGCATCGTATCATCGGAGGTATTGGTATCGGTACAGTTGTAGC +GTTATTAATTATTTTAACTTTTGCGAAGTTAGCACAATTTGCCAATGATGTTATTTTATCAATGTTACCTCAAGCAGCAA +CTACAGCGATTGCGTTACCAGTATCAGCTGGTATCGGTGGTATAAAAGAATTAACATCATTAGCAGTTATTTTAAATGGT +GTCATTATTTATGCCCTAGGTAATAAATTCTTAAAGCTTTTCCGAATTACTAACCCTATTGCCCGAGGATTAGCACTTGG +AACAAGTGGTCACACATTAGGTGTAGCACCAGCCAAAGAATTAGGACCTGTAGAAGAATCAATGGCAAGTATAGCTTTAG +TGTTAGTTGGTGTAGTTGTTGTAGCAGTTGTGCCTGTCTTTGTAGCAATATTCTTCTAAAACGAAAAACCTAAGCAAGAT +AATAGCAATTTGAGCCATTGTTATTATCGTAAAAAAACGTCTATACTCCAGTTTATAACTGGGATATAGACGTTTTTATG +TATTTATTACTTTTTACTAGGAATATAAAACTGTGCATGACGATAATGAAATACGATGTCAGATGAATCAAAGGGTTTGC +CAGTCATTGTATAAAAAGTCTGGTGGTAACGTAAACATGGTTCACCTGTAGACAATTGTAGTAATGAAGCTTCACTTGAA +GTGAGTTGATCTACATTAAAGAAAATATCTGAAAAACCAATACGAAGTTTCATGTTTGATTCTAAATAGTCGAAGATAGA +GCCCTTAGCAATATCATCATTTAAATATTTCACGATTTCTTTATGATAATAAGAATATTCGATACATAAAACATCATCGT +CCACGAATCTTAATCGCTCTAAATAGTAGACGGTATCATCTGCATTTAATTGGAGCTCATCTTGTACAGATTTAGGTGGC +GTTGCAATCTCCTTAAAAACAAGTACCTTACTTGTCATTCGGTGTTCACCTAAACTTTTAGAGAAACCATTAGTCTTAAA +GACGTTGATACGATTGGCATCAGCAATATTTCTCACATAAATACCACTGCCTTGTGCTTGATAGATCAAACCATCTTGTT +CCAATAAGCCTAATGCTTTAATGATAGTACTCTTACTTACTTGATAACGTTCTTTTAATTGCGTCACGCTTGGCAATTTA +TCACCGGGTTTGAAATTAGATTGATGTATAAACGCATTAAGTTGCTTAGCAATATGTTCATACTTTAACAATATTCTGTC +CTCGTTTCACTTTGTCTGTAATTATTATAGCACAAAATATTATAATTGTATCGGCGTTTACAAATTGAACCGGTACAATT +ATAATTGGAGGTAAGTAAATACATTTTCATTCTACTATCAAGTTGAGGGGGTAATATTTATGAGCAATAAATATAAAGAA +CAAGCCCAAGACATTCTTACAGCTGTAGGTGGTGTCGAAAACATTGTTGATGCAACGTATGATACGAAGTGCATTACAAT +TCATATGCAACATACAATTCCTTCTACAGCAAATGAAGTGAAACAAATAGTTGATGTGACATCTGTAGCAGAAAATGATA +CGCAGTTAGTCATAAAATTAAATGGAAATGTCGATGAAGTGTATCAGCAATTACAGCGATTAATTAAGAATGCTAATGTC +GAAGAGAGTGAGAATACTGACAATATTAATAGTCAAGATACAAGTTATACACCTCAAGTAAAAGTAACAACACCAATTTT +AGTGAAAGCACCAATCGCTGGTCGTCGTATTTTACTTAAAGAAGTAAGAGATTCAATTTTTAGAGAGAAAATGGTAGGTG +AAGGCTTAGCAATCAAAGCTCATGAAGAATCCAAAGTAATCGCACCGTTCAATGGTTTAATATCTATGATTGTACCAACT +AAGCATGCAGTTGGTATTCAATCAGAAGACGGTGTGGACATAGTCATTCATATTGGCGTGAATACAGTTGACTTGGAAGG +TAAAGGGTTCAAGTGCTTTGTAAAGCAAAATGATCATGTTGAAGCAGGGCAAACGTTGTTGCAATTCGACCAGCAATATA +TACAACAACAAGGCTACAATGCTGACGTTATTGTCGTTATTAGCAACTCTGCCGATTTAGGAAAAGTAGAACTGACAATG +AATGAAATCATTACGACTGAAGATGTTATTTTTAAAATATTTAAAAACTAGGAGTGTGTTGTAATAATGACAAAATTACC +GCAAAATTTCATGTGGGGTGGCGCTCTTGCCGCAAATCAATTTGAAGGTGGATATGATAAAGGTGGTAAAGGGTTAAGTG +TAATTGATGTTATGACGAGTGGTGCACATGGCAAAGCACGTCAGATTACAGAATCTATAGATCCCAATCACTATTATCCA +AATCATGAAGGTATTGATTTTTATCATCGTTATAAGGAAGATATTGCCTTGTTTAAAGAAATGGGATTGAAATGTTTACG +TACGTCGATTGCGTGGACACGTATCTTTCCGAATGGGGATGAAGATGTGCCAAATGAAGAAGGACTCGCCTTTTATGATC +GTATCTTTGATGAATTAATTGCACAAGGTATTGAACCTGTTGTGACGTTATCACATTTTGAGATGCCACTTCATTTAGCG +AAACATTATGGTGGATTTAGAAATAGAGAAGTTGTCGATTATTTTGTGCATTTTGCGCGTGTTGTATTTGAAAGATATAA +AGATAAAGTTACATATTGGATGACGTTTAATGAAATTAATAATCAGATGGACACATCAAATCCTATCTTTTTATGGACGA +ATTCTGGGGTAGCATTGACAGAAAATGATAATCCTGAAGAAGTCTTGTATCAAGTAGCACATCATGAACTTTTAGCCAGT +GCTTTAGCAGTTCGTCTTGGTAAAGAGATTAATCCGAAGTTTAAGATTGGAACAATGATTTCACATGTACCCATTTATCC +ATATTCGTGTCATCCGAAAGATATGATGGAAGCACAAATTGCGAATCGCTTACGTTTCTTTTTCCCGGATGTCCAAGTGA +GAGGTTATTATCCAAGCTATGCTAAAAAAATGTTGGCACGAAAAGGATATGATGTTGGATGGCAAGAAGGGGACGACAGT +ATTTTACAGCAGGGCACGGTTGATTATATTGGCTTTAGTTATTACATGTCTACGGCTGTAAAACATGATGTTGATACTAC +AGTTGAAAACAACATCGTCAACGGTGGTTTGAATCATTCTGTGGAGAATCCGCATATCGCAACGAGTGATTGGGGTTGGG +CGATTGATCCAGATGGCTTAAGATATACATTGAATGTGTTATATGATCGTTATCAGTTACCACTTTTTATTGTGGAAAAT +GGTTTTGGTGCAGTTGATGAAGTGGTAGATGGACATATTCATGATGATTATCGCATTGAATATTTAAAAGCACATATTAC +AGCAGCGATAGAAGCAGTTGATCAAGATGGTGTAGATTTAATCGGTTATACACCGTGGGGAATCATTGATATTGTTTCAT +TTACAACCGGTGAAATGAAGAAACGCTATGGTTTAATATATGTTGATCGAGATAATGATGGTCATGGCACGATGGAACGC +TTGAAAAAAGATTCGTTCTATTGGTATCAACAAGTGATAGCATCAAATGGAGATAAATTATAAAGGTATATTATAAGTAT +TTTAGGGTTAGAGCCCGAGACATAAATTAATATAGTAGGACCTACAGTGTTATAATGGCGGGCCCCCAACACAAAGAATT +TCGAAAAGAAATTCTACAGGTAATGCAAGTTGGCGGGGCCCAACACAGAGAAATTCGAAAAGAAATTCTACAGGTAATGC +AAGTTGGGGAAGGACAGAAATAAATTTTGCGAAAATATCATTTCTGTTCCAATCCCGTGATGTTAAAATTTTAAGAAAAA +AATAATGCCACTAACTATGTATAGTGTTAATGTTTGAGTGTTTATGAAAGTCTTTAACCAAAATTGATGACTTACTAATC +AACGATAACGATATTGTTAAGTAGATGATTAGTGTCACAAAGAAATTAAAGTGATAGTCAAACATAGATACAAAGTACAG +TTAGTGGCATTATATTTAGTGCTCTTTTTTAGCGACAAAAGTAATATAATTCATATCTTTACGCAATTTAGTCATCGTTT +TAAACATTTTACAAAACATTGGTCGATTTTCTTTTTTCAAAGCATTGTTGATAATCTTTATAGTTCCAACAATACCTTCG +TCATAAATTAAACCTTTTGGTGTCATTAAACTCATTGGACCAGTATGATAATGCACATGATTAAAACCAGCTTGATTATA +TAAATCTAACCAGCCAAGTTTCGTCTGCGGTGAGACATTGACATTAATTGCTGCAGATAATGATTTAACAACATGTGTGG +CATGTGATTCATTAACGATGACAATATCATGTGTTAACAAGATACCCCCAGGCTTTAAGACTCGGTAGTACTCGCGTAAT +GCTTTTTCCTTTATGGCGATGGGTAACATTGTTAACATTGCTTCATTTAAAACGATATCGAATTGATTGTCATCAAAGGG +CAATTTAACAGCATTCGCTTGTTGAACTTGAATATATGATTCAAGACCTGCTGCTGAAATGTTTTCCTGTGCTTTTTCTA +ATGCTTTCTTATTTATATCAACGCCTTGAATGTGACAGCCATATGTATGAGCTAGATAAATAGATGTTGTGCACATATTA +CATGCCACTTCTAACACTTGTTTATCTTGTGAAAATGCCCCTTGTTGTATTAACCAATCTGTTGCTTCTTTACCACCGGG +GCGTAGACGAGTTTTTCCTAATTTAGCTAAAAATGTATGACCAGCTTCTTTAGACATAGCATAACCTCCTTTTAATTGAT +AATTATTATCATTAATATAGCATATTTATATGCGATAATTTGGAAGTTAAAAAGATTACAAAAAATTGTCACTTCATTTA +TAACTAAACATGAGTCAACAAGCATAGGTTTAGTGGAAAATTATTTCTAATAGATGATGTTTTATAAAGATAAAAAAGCA +CAGCCATGATATACGAATGTTGCATGTTATATGCTAAACCTTCATATCATAGCTGTGTTTGATTCATTTAAACTTGATTT +ACTTCTTCTAGTAGAGGAATAGATGCTTGCGCGCCGTGTTTTTGTACAGTGAGTGAGCTCGCTTTATTACCAAAATCAAT +AGCATCTGCTAAGTTATCTTGCGACTTGTTTAAGCGACTGACAAATGCACCAATAAATGTGTCGCCTGCAGCAGTTGTAT +CAATCGCATTTACTTTATAAGCTTCGATGTGTTGGCTTTGATTTTTAGTAGCAAAATATGTACCTTGCTTACCTAGCGTA +ATCAAAACAGTCTTAATGCCTATAGATAAAAAGTAATTGGCATTGTCTTTCATAGATTGTTCATTAGTTACTTTAATCCC +AGATAACAATTCGGCTTCTGTTTCGTTTGGCACAATAATATCGATTAATGATAATAATTCATTAGGTAATGCTTTCGCTG +GTGCAGGATTTAATACTGTCGTCACACCATGTGCCTTGGCAATTTCAAATGCAGATATAATAGCCGGGATGGGTACTTCT +AATTGTGCAACGACAAAGTCTGCATTGATTATAGCGTCTTTTGCGTTAATAACATCTTCAGGTGTCATCGTCATATTCGC +ACCACCATAAACATAGATGGTGTTTTGTCCTTCTGCATTCACAGTGATAAAGGCTTGGCCCGTTTTTGCTTCAGCTGTTT +TGATAATATATGATGTATCAATATGAGCTACTTTAAAATCTTCTAAGATGAAATCAGCAACGCCATCAGTGCCAATTTTA +GTAATAAATGTTGTGTCTGCTTGCATGCGTGCAGTGGCAATAGCCTGGTTGGCACCTTTACCTCCGCCGAATGCTTTTTG +TGCTTCTTCAACATGTAATGTTTCGCCTGGTTGTGCATATCTTTCAACTGTTAAAAATTGATCGACATTCGTTGAACCTA +AAATAACAACTTTGTTGGTCATGTGTACGCTCCTTTCAAGTTATAACTTTTAAAAAGTAACATTCGATTCTAATGCAATA +TTAGAGTAGGGCGTTGTTTCACCAGTACGAATATTACCTTTATTTAATGGGTGAGCTAAGTTACTTTTCATTTCTTCGTG +AGGAATGAAAATGATTTCGATTTCCGATGAAATCAATTGTTTAATTTGTTGCAATTGTGTAGGGTTATGTTCTTTTATTT +CTTCTGCTAAGTATATTTTTTGGATTTCCATTTCTTCTAACACTGTAGCTAAGACATCAATAAAGCGTGGTAAGTTTTTA +GTTACAGCTAGGTCGATACGACGATGATCATTTGGAATTGGCATGCCAGCGTCATTAATCGTTAATAAATCAAAATGACC +AATTGTCGCGATTGCTTTTGAAATATGTTCATTTAAAACAGCTGATTTTTTCATGACATCTACACTCCTTATTTTATAAA +TACTGTAACAGAAGCGGCTACTAAAATGAGTACTAAGCCGATGATTGTAATAACCATTTCTTTTGACGTTTTATGTTGTT +TTAAGAAATAAATACCAGTTAATGTAGCAAGCACAACGGATGTTTGAGAAAGAATAAATCCAGTTGCTAAACCATTCATA +TTAGGTTGTGCTGAAATAAGATATGTTAAAGCACCAAATGCAAAGAAGAAACCTGAAATAATTTGTAACCACGTAATTTT +ATTACGGAATGGATTCTCTGCTTTCATATTCATAAAGCCATAAATGACTGCAACAATTACCATACCCATTGCTTGAGGTA +AAAAGGCAGTTAGGCCATCAATAGAAGTTGCTTGCGGTGCAGCTGAATATAACCAGTATCCAAATTCACCAATTAACAGA +AGTACCACTGCACGACGTAAATTTTTGGCGTTACTTGCTTCTTTGCGTTCACTCCAAACTGTCATACGCGCTCCAATTAG +AATAACGACTAAAGCTGTAAATCCAATGATTTTATGACCAATGCCTGGCCAATTTCCTAATGCAAAGACACCCCATAAAG +ATGCGCCTAATAATTGGAATGCTGTTGTGACTGGCATGGCACGAGATGAGCCGACTAATTCGAACGCTTTAAATGTAATG +ATTTGTCCGAATCCCCATCCTGCACCTGATAATAAGGCGAATAGCAAATTGGTTCCAGTAGGGAAGCCACTTGATGTGAC +TACGGCTAATAAAATAGCGAAGATTAACGTACCTACAGTAGCACCGATAATTTGATGTACAGGTTTACCACCAAACTTTG +AAGCGACTGTTGGGAAGAAGCCCCAGCCAATTAAGGGGCCTAACCCGATAAGTAATGCAACAATGCTCATGTTATACACC +TCATGATTTGTTTTTAGTAAAACGTTTTACCAGTGCCATCGTACCATTTTTATTTGTAAAATGCATGCGTTTTCATAAAA +ACTGAAAACGTTTAAATCATATTTAAAAATTCATACATTATTATTTTAATTTCTTAGTAAAATAATTATGATTAAAAACC +CTCAGCATTAAAGCAACGCTAAGGGCCTAACAATGATGAGTATATTTCGGAAGATACGTAGTTAGTTTGAAAGATGATAG +CCAGTTGTTGCACGAATTTTTAAAGTCGTTGGTAATTCAATCATATCAATGGATTTATCTAAGTGCTGTAATCGTTGAAG +TAATAAGGTTAAAGATGTTTTGCCAATATCAGTTATAGGTTGTGCCACAGTAGTTAAAGGTGGCGAGACGTACGCTGCAT +AATCAATGTCGTCATAACCTATTAATGAGATATCTTTCGGGATACTGATGCCATGTTCAATTAGTCCTCGTAAAATGCCA +ATAGCGAGTTCATCGTTAATAGCGAAGATTGCAGTGGCAGATTGAACCATGATGTCATCAACAATGGTTAGCCCACCGCG +CTTAGATAATTCAGTATGGACGATTTGTGGTTCTGGCAATTGATTCGCGCGCAAAGTATCGACAAATCCAGCGACACGAG +TCGACATATTCGCCATCATGTCATATGGTGCAACAATTATCATATGGTTGTGACCGAGTTCTATTAAATGTTGTGCTGCA +AGTTGTCCACCTTGATATTCATTTGTCCGAACAAAATCTGTATAGCCTTGATGGTCATTTTGATCCAGTACGACATAAGG +TACATGATGTTTCTTTAGATAGTTATTTAGGGCGTCCGGGGATGATATGTATTGTGCGATAATTAATCCGTCAATACCTC +GATCAATTAAATGTTTAATATTGTCATACAAATCAGTTGCTGTAGATGTTAAAAAGCATAAATCAACATCAGATGGTTTA +TGGTCATGAATACTTTGCATCAGTGCTGAGAAAAACGGATTTGTTAAGCTAGGCAAAATGACGCCAATAGTTTGAATTTT +ACTGCCGCGCAATTGTTTTGCATGTTTATTAGGGGCATAGCCTAAACGTTCTGAAACAGCATGTACGTTTTTTATCGTTG +TTGCGGAAAAACGACTATCATTATGATTTAAAATATGTGACACAGTTGTAACTGATACACCAGCTTCTCTAGCAACATCT +TTAATTGACACTTTTTTCATATTATATTCCTTCTCTTTATTGTGTAATTAATCATACAGTTATATACCCGATTATTATAT +TCATCATATCATGATAAACATTTGATGACATATGAAGACGTGAATGACTTTTATTGTCTACAAACAGACGTCTGAAGTAT +ACGATTAAGGTTGAGACGAGAGAATATATTATTAAGGTTAATACCTTTTAATGATTATGATAACAAGAATTTTAAAATGT +TTATTGTGATTAAAAATACAAAAGTCATTATTTTAAAGAATAAAATATTTGTTTTTGAACAAAGTATTAAAAAACATAAC +GAAATGATGTATATTGAACTATGTGATTGAAAAAATTTTTAATTAAGGCTTGTTAAAACTTAAGAGAGGATGTTTTTAAA +TGCAATTCAAATTAAAAGAAGAAGAGATTATTAGTTTTTTAGAATTGAAATATCCAGAAAAAGAGTTCGAATATGGTCGT +TTGTTAGTTGGACAACATAAACGTGATGATTTAGATGTTTATTACTTTGGTGATACGTTTTTAATGTGCACGATTATTTC +ATTCAAGACATTTGAAATTAAAGAAACAGTAGAATTATCATATGATGCTGTTAATCGTATTGTGTTAAAAGATGGATGGT +TATTTAGAAAAATGAGAATAGAAACAATGCAAAAAGTGTTAAAATACGGTACATCTAAATTAATGTTAACTGATTTTCAA +AAAGAGAATTATAATAAATATATTCAAGGTCAGAAACAACGCGTGATATTTGAAAATGGCCATTTTGTCTAATTGATAGT +GAATATAATTAGAGTAAGAGGCTGGGACATAAATCCCTAAAAAACAGCAGTAAGATAATTTTCAATTAGAAAATATCTTA +CTGCTGTTCTCTATTTATACAATACTTCGTATTGAATGGCTTCGCTTTCCTAGGGTGCCGTCTCAGCCTCGGTCTTCGAC +TGGCACTGCTCCCTCAGGAGTCTCGCCATTAATACTACGTATTAACATGTAATTTTACTTTGAAATACTTTAAAAAAATA +AGACACTTTGCCCAACTTACACTACCAATAAAAACTTCTGTTAGAATTCCTCAAAATGATATTTCGCGACATGTTAATGA +AATTGTTGAAACGATACCTGATAGCAAATTCGATGAATTCAGACATCATCGTGGCGCAACATCCTATCATCCAAAGATGA +TGTTAAAAATCATCTTATATGCATATACTCAATCTGTATTTTCTGGTCGTAGAATAGAGAAATTACTTCATGACAGTATT +CGAATGATGTGGTTAGCTCAAGATCAAACACCTTCTTATAAAACTATTAATCGTTTTAGAGTGAATCCTAATACTGATGC +GTTAATTGAATCTTTATTTATTCAGTTCGCTTATTTTTCTTGGATTTCTTGTATATCATTTTAAAGTGATAAAGATATTT +CGAAACATAATTCTTCTATTATATTTAAGATTTAACTGTTTTTGGAATGATCATGTATGCAGACAATGAGCCAAGGATCA +TCAATACAATGCTGACTATAAATGTTACGGTTGCAGCTACACTTGGTGCATAGTTTAGTTGTAACATACTGAAAACTGTA +GTACTTAGTGCTATACCAAAGGAGCCACCTAATGTACCACTCATTTTATATAATCCTGTAGCTAAACCAACTTTTTCATT +AGGCATACTGAAAATTGCAATCGTAAGTCCAGGTGTTGCGACTAAACCATTACCTATCGCACATATGACGAAACCAATGA +TAACTGCAATGACATATTGTGATGCCAATAATTGAGTCATGCTAATAATAGTGATGCCGATGACAGGGAACAACGGACCA +ATGATGAGCATCAATTTGCCACCGAAACGTAATGTTGCTTTTTCACCTAAACGAATCATCGCAACTGCCACAATGGCATA +TGGCAATGTAACAAGTCCAGATTGCGCAGCTGATAAACCAAGGTGTGTTTGAGCATATATGAAAAAGACCACTGTTACGC +CTAGACCGCTATTTAAAACAAAGTTATTTAAAAATGCACCAATGAACGGACGGTTGCGTAATACTGAGAAATCAATAAAA +GGTACTTCATGTCGACGTTCGATGATGATGAATATCAACGTAGTGATGATAAAAATGCTCAGACAAATGATTGAAAATGT +ACTAAACCAACCTTGTTCGAATCCTTGTGTTAACAATAATGTAAAGCTACCAATCATAACAGCGAAAATCGACATACCTT +TGTAATCGAATGGATGACGGTGGCTATGTTGACTTACTTTTTCAGGTGTGCCTTTTAGAAGCAATATGGCAATGAAAGCA +ATGACTATACTAATGATGAAATTCGTTTGCCATCCGAAATTTGAGGCAATTAAACCGCCGATAACACCAGCTAGGCCGAT +GCCACCAACAGTACTAATCATTAGATAACTAATCGCTCGTCTTAAATGTTCTCCTTTAAATTGATTATTTAACACGCCAA +CTGTTGAAGGTAACAAGATAGCTGCTGATAGACCTTGTAAAATTCTACCGATGATGAGCAGTGCAGTGATGTCCGATATA +ATTAATAGAAGAGATGCAAACATACTGATTATGAGACCCATGTATGTCATTCTCAGTTGTCCTATTTTATCAGCAATATC +ACCTGCAGCCACCATGAAGATACCTGTGGCGAAGGAAGTTAAACTAATAGATAAATTTAACACGGCAGGAGAGGTTTGAT +ATGTTTGACCAACGAGAGGTCCTATATTAATAAATGATTGTGCAAACAACCAATATGTTAATGCAGACAACATAATCGCA +ATAATAATATTACTGCGTGGTGAAGATTGTGTGTTATTCACAGATGTCACTCCTTGTAAAAATTAAATATAGAAAGTCAT +GAGATGAAGTGAACGGATGTATAGATGATCGTTCACTCACACATAAGTCATGATTTCCTTACATATCATACCACAGTTAT +TGAGAATGAATATCAATTAATTGCTTGTATAATTGATTTTTTATAGTATGTTTAAGTGAAACAGCTTTATTTGAAAGTGA +TTAAAGTAAATGAAGGTACAGAGGAGTGAGAACAATGTGCACAGGATTCACAATACAAACTTTAAATAATCAAGTACTTC +TTGGACGCACGATGGATTATGATTATCCATTAGATGGTTCGCCAGCAGTGACGCCTAGAAATTATCGTTGGAAATCTTGC +ACTGGCACGACAGGCCAAACGCAATATGGCTTTATTGGCACAGGAACAGATATGGAAGGTTTTATTTATGGTGATGGTGT +TAATGAACATGGCGTTGCCATTTCAACACAATATTTCCGAGGTTATAGTTCATATGGATCAACACACAAAGCGGACGCGA +TGAATATTACGCAAAATGAAATTGTGACATGGATTTTGGGATATACAACAAGCATTGAAGATATGAAACAACAAGCATCC +CAAATACATGTTGTAGCTGTATATTTAAATGACATCGGTGAAGTTCCGCCATTGCATTATCATGTTTCCGATGCAACTGG +ACATACAGTCGAAGTTTCATTTAAAGAGGGTGAAGTGGTTATAAAAGATAATCCTATTGGTGTCTTAACAAATCATCCAG +ACTTAAATTGGCATTATAGTAATTTAAGACAATATATCAATATTTCTCCTTATCCAGCAACAGCAAATTTATTGGAAGGT +GTAACGATTGAACCTTTAGGCAATGAAGCAGGTACATTTGGATTGCCAGGTGGATTTACTTCAACTGAGCGCTTTGTGAG +AATGGCATTTATGAAAGCAAACATTGCTCAAAACAATGATAAAGAAATGGATTTAATGAATGCATTTTATTTATTAGATG +CGGTAAATATACCGATTGGAATTGTACGTCCGCATGATGCTGACAATCACTATACGATGTATCAGACCGTAATAAATTTA +ACTACAAGAACGTTATATATTAAGTATTATGGCAGCAATGAATTAGTAGCATTAAAGCTCACAGATGATTTAATTAATAG +AAAAGATATGACGATTTTTAAGCCTGAGAAGCATATCACTATTAGAAAGTTGAATGACAATCAATAGCGATTGATAATGG +AATTTGGTTGATATGATTTGATGGATGATTACTATCATGCATTAGCGTTGAATCGCGAACATGGACGAACGATTAGTTGT +TAATTTTGAAAATTTATAAAAGGTTAAACGAATGCAGTGAGAGATGTCATTTTAATATCAATGTATGGCTATTTTGTAAT +GACAATGTAATGAGTTTAGTAAAACATTTCGGGAATATTAAATAGTTGGAAAATGAGAATTAAATCCTTTACTCAGTTGT +CTAATTCTTTTAGTATGTGCAGTACAGTTTAATTGAAAACTAACTTTAACTTTAATGGAGGATGTTTTATACATGAAAAA +ATTAACAGCAGCAGCGATTGCAACGATGGGCTTCGCTACATTTACAATGGCGCATCAAGCAGATGCAGCAGAAACGACAA +ACACCCAACAAGCACATACACAAATGTCAACACAATCACAAGACGTATCTTATGGTACTTATTATACAATTGATTCTAAT +GGGGATTATCATCACACACCTGATGGTAACTGGAATCAAGCAATGTTTGATAATAAAGAATATAGCTATACATTCGTAGA +TGCTCAAGGACATACGCATTATTTTTATAACTGTTATCCAAAAAATGCAAATGCCAATGGAAGCGGCCAAACATATGTGA +ATCCAGCAACAGCAGGAGATAACAATGACTACACAGCGAGTCAAAGCCAACAGCATATTAATCAATATGGTTATCAATCA +AATGTAGGTCCAGACGCGAGCTATTATTCACATAGTAACAACAACCAAGCGTATAACAGCCATGATGGTAATGGAAAGGT +CAATTATCCTAATGGCACATCTAATCAAAATGGTGGATCAGCAAGTAAAGCGACAGCTAGTGGTCATGCGAAAGACGCAA +GCTGGTTAACAAGTCGTAAACAACTACAACCATATGGACAATATCACGGTGGTGGTGCGCATTACGGTGTCGACTATGCA +ATGCCTGAAAATTCACCAGTTTACTCATTAACTGATGGTACAGTAGTACAAGCAGGTTGGAGTAACTATGGTGGCGGCAA +TCAAGTAACGATTAAAGAAGCGAACAGTAATAACTACCAATGGTATATGCATAATAATCGTTTAACTGTTTCAGCTGGTG +ATAAAGTCAAAGCTGGTGACCAAATTGCATATTCAGGTAGTACGGGTAATTCAACAGCGCCTCACGTACACTTCCAACGT +ATGTCTGGTGGCATCGGTAATCAATATGCAGTAGACCCAACGTCATACTTGCAAAGTAGATAATACAGAAAATCCCAAGT +TGCGATATCATACGCAGCTTGGGATTTTTTCGTTTTAATAATAGGTATAAGTCCATTGTTTGTCCTCTAAAACATGTTGA +TAAAAAGGATCTTGGCCAATTAACTTGATATCATCTGCAAGTGCTTCAACTTCATCTAAATGATGGGTAGTTAATATAAT +TAAACATTTAGATTTCATGATGTTAAGTAGTTGGTGGATGTCATGTCTAGATTTTAAATCAATACCAACTGTCGGTTCAT +CTAAAATGAGAATTCGAGGTTGACCTAGTAAACCTACTAATATATTAATTTTACGTTTATTCCCACCGGACAATGTAGAT +ACTTTGGCAGACGTATCATCAAAGTTTAATTGCTGTAAATATTCGTTGATAGTTGTATCGTTAATTGGATTTTTACAAAG +TGATTTAAAAAATTTAATGTTTTCAGCCACTGTCATGTGTTCAAATAACGCAATGTCTTGTGGCACATAACCGATGTGAT +TTTGTATTTGTCTTTGATTCCATTTTTCGCCGAAATAGTTGATAGTTCCATCATTAGCTTTTTCAATACCAGCAATCATA +CGAAGTAATGTTGATTTTCCAGCACCATTATCACCAAGTAATACGGTTAAACGATTACTATCAAAGGACATAGTTAAATG +ATTGAAAATCTGTTTGTTACGGTAACGCTTTGAAAGGTTATTAATTTCTATCATCAGTTACGCTCCTTTACATGAAAATA +ATCAAGTATACGATACCCATAGCAAGTGCATATATAAATGTCATGAATAATCGATGACTTATTGTTTGAATATGGAATAA +GATAAAGACGATACCTATCTCATAAATCAATATAAGTAACAGTGATTTTAAGTAAAATATTAAGCTGAGTGGTTGAGACA +AATATAGACTAACTGCCAATAGTACCAACAATAACAAAATCGTATGTGTCATTACATAAGTACTATATAGTTTGAAACGG +CTTAAATGATATTGTGATAATCGTTGCAATGCTGCTTGTTGGTTTAAACGATAATGAAGTACTACTTGAACAGCGCTAAC +AAATAAAATCACCGCAAAGATTAAGCTAATTGAAATAGAGTGTTGTGCTTGTTTAGTAAGCGACACAAATTTGATTTTAG +ATTCAGGTGTATGTTTATGATAGGACTTGTTGATAGCATCGATGGATTGATGCTGTTTCATATCCTCAAGGTGTTCATAA +ATAATGTTAGGAATTTGCTGCTCATATAATGAACTACTAACAATTTCTACAGCAATACCACCTATAAAGTCATCTCTACC +ATATAACTGTATCGTTTCTTTTAAACGGTTCTCTTTTAATTTTTGAGAGAAACCTTTAGGAATTTGCATACTTAAAATAG +CTTCCTTTTTAGTAACATCATCTTCAATATAGCTTTCATCTTCATCGACTTTTTTAATAGTTACATAGTCAGATTGTTTA +ATTTTATTGACGAATGATTTTGATGCAGTGGTTTGGTCTAAATCTTGAATGGTAATCGGTATTTTGAAGTTGTCATGTGC +TACACGGTAACCGATACCAATAAGTACGAGTGCGATGACAATGGTTGTTACGAGCAAGATGTATTGTAACCATTGCTTGA +ACACAACAAGTTGTATATAAGGCTTCATTGACGATACCTCCAAACCAATACAGCTAAATTAATTATCAAAAGTGCGATGA +AGCTAAGATAGAAACTAGGGTGCAGTTCTAAAATGTAGTTGTTTAAAATAATTTCTAACAATTGATTTGTTACAACTGCG +AACGGTTGAATATTGAAAACACCATTTGCTATATGTTGTAAAAAAATCGTAGGTATTGTTAAACCAGATAACACCAGGAT +GACAATAGCTAATATGACTTTACTAATACTATTCAACAAGCCTGTTGTTAAAAGTTCGATGAGTAATAACCACAGTATTA +AAAAGGTAACATAATAGCTTAAATGAATGGCTAACGTTGGCCAATTATATAATTCAAAGATATTCGGAATACTGAACACA +ATCCAAACTACACCAACGATACTCCATAACATAGTATAAAACCATGTAATCAACGTACGAATGATTAATAAACGCTCTTT +AGAAAAATGAAACATTTTCAATCGCGCTTTCAATACAGTATCTTGATTCATTTTCAAAACTGTAAATAAAGATAGTGCAA +AGATGAATACCGTTGTTAAAAATCCTGTAATTGCATAATAACTGCCCGTATCGTATAAATGAATCGGTTCTAAGTTAAAT +GCACCTGAACGGTTTAATCCTGTAATCAGCAAATCAGTCATAACATTGATACTGTCAGAATGTGATGCTTTCGGTGCTAA +GTCTTGAAAAGCTAAGATGCCACCCATTGATCGCATAAGACGTTGGTAAACAGAATCTGTTAGCTGAGATAGCACGACAC +TTTTCATGGATTGTTGATCATATGTATATACTGAAATTGGTAGTTCGCCTTGTTTATAAAATGCCTTGGTCATACCTTTA +TCAAAAACAAAATAGCCTTGAAGTTTATGTTTTTTTAACAAAGTATGTGCTTGCTTATCATCATATGCTTTAATGCTCAC +GTTTTTTCCTAGGTTACTCCCTTTACCAATAGAGTTTAAGATTAATTTCGTTTCACTTGATTGATCTTTATCTACGACAC +CTATATTAAAATGATTGTCATCTTCTGTTACATGTTGGATTGTCGTTAATGTAATAAGGAGTGCCGCTAATATAAATAGT +AAATAGATAATCAAATACCACTTTTTCAATAAAAAAGAGTGGTAGATGCGAAACAAATGTATTGTTTTCATTTAACCACT +CCTCCAATGATAAGATTGAAAGGCAAGATGACCTTCCAATCTTATTTTTATATTTTAGTTATTTAGATGCCTTTTTTAAA +ATTGATTCAAACATTTTGCCGCCATTTTTTTCGATTTCTTTTTCAAGTTTTTCACGGTCTTTTGAAGATAGACTATTGAA +ATCTTTTGCACCACTATCATCAAAATCAATATCTGCTTTCAATTTTGTGCTAGATTTTAAAATGAAATTAATTGGTTCTT +CAGCATATTTGATGCCGATATTTAACGTAGATTTCTGAGTGTTATTTTTTACGTCAGAATCTATATTATTTTCAAAAGTG +AATTCATTTTCGTCGCTATATTTATCTAATGCGACAGTGATTTTACCTTTATCTTGACGTTTTGTGCCATCTACTTTTTC +TTGGTTATCTAATTTGATTTTTGATTCATCATATTCTGTCTTTTTACCAAATTCGTATTTATCATTATATTTATTATCTT +TTTCTTTAGAAGATACGCCTTTAATTGTATATTTCGCTTCAGCATACGTGTATTTATCTTGATCGAAATCAAGTGCGTAA +TCTAGTTTTAACTTATCGTCTTCTAAAGTATTAGTACCTTTGATTTTAGTTTTATTATTTTCTTTGTCTGTAATAGTAAT +TTCTCGTTTTACAATCGTATGTTTTTCGGTATAAATTTTAGATTGAATTTTAGCAAATTCATCCTTTTTAGTTTCTTTGA +CATCGTCAATTGCTTTTTTGATGTCTTTTTCAAAGTCTTTTGTAGCACCTTGTTCTTCCATTAATTTTTTAAGGTCTTTA +TCCTTTTTAGCTTCTTCTAATACAGCTAATGTAATTTTTTTAGTGTCAGCTCTGCTAAGTGTTAACGTGACAGGTCTAAC +TTTGTACTTTTCACCATTAACCTTAATTTCTTCTTTTTTACCTTTATCAAAATTATCGTCATCTAATTTGTCGACAATAA +GTTCGGAATATTTTTCGGCAATTTTGCTGTAGTCACTTTGTTGTGCTTGAGCATTATTAAAAAGAGTATTTAAATTTAGT +TGTTGGTTTGTAATACCATTTTCTTTTGCTGTTTCTTCATCTTCACCTGTAAGTTTTGAATAAGTTGATAATAAATCAGA +ATTATTAACACTATATTTCCCTTTAAATAATGGTGATTCGAAATAATGCTTATCTTTATCTGCAGCTAACTGGAATTTCC +CTAATGCAGAGTCTGCGATTGTTGGTTCAAGATTAATCATTGATTTCTCTTTTTTAGGATCATGTCCATATGACATTTTA +ATTTTCGATGCATTAACAACAGATTTAGGAATGCCAAGCCCTTTAACAATTTCATCTGATGCATCTGCGCTTAATTCTAA +TGAAGATAAAAACGAATTATCTTTCATCTTTTCTTGGAACTTCACTTCATTTTCAAAACGGTCATTAAAATAATCTTTAT +ACATTTTTGCTGTTTGTTGTTCACTTTTTAGGTATGTATTTTTCGGTGTATTAGCAAAAAATGCATAAACTCCCCATGCG +ATTCCACCTATTAATAGTAAGACAATAATAATAGGAATTATAATTTTTAACTTTTTAGACATTTTGCTTCCTCCCAATTA +TTTATGAGTTAATCATATCAGAACAATACATTTTATTAAATGGTTTATTTTAAATTTTGTTTAAATTAATAGAATTTTAG +TTATAATAATGTTTAATAAGTTATTGGAAATCTAATAAACTACAAAAAATAGTTTGATTACATAATGATTCTTGAAAAAT +GTTGGTTAACTTAATAATATGCATTTTTTTGGCGAAGAAGATTTATTTAACTTATAAAAATATTGAAGTAAGATTGGGGA +GATTATGAATTTATGGAATTGAAAGTCGATGATTTTGTAAAGAATATAAAAAGACCATACTTGACTGTATTGGGAGTATT +TGTAGTTGCAGTTTCATTATTTTTTGACGCGATTATGTTTTTCTACGCTAAACTATATGATAAATTGCCTATGTATTTAC +TGGTGTTTATGGCCTTTACAGCTGTAATTTTGATTATGATGTACATACAAGAGAAAAATGAAAATTACAAAGTTGAAAAA +AGATATGTGGTTAGATATCTCACACTTAACGTTATTGTGGGATATACTTTGCCATTGCTTTTTGTATCTATTTACGTTTT +TGGTGTAGTCGGTTTTGGATTTGATGTTTTCAATTATTGTCTAGGTATTATCTTGATGTTATTTATTTCTTGGTTAGGTT +TATTTTTATTTTATAAAAACGAATTTGATAGTGAAAATCCTAATCCTGCAGTTAATGCCATAGCAATTATTATAAAATTA +TTTGCTTTTGGAGGAATATTTTATATAAGTCTAATAGTTCCTGTAACACAGCAAGAAGAGATTTTTATAGGGTTAAGTAT +ATTCATCAATATAATTGTTGATGCTCTTCTTGTTAGGTCATATTTTAATTACGCGTTATATAAGAGTATTAAGAAAGATA +TCGAAAATGAAGGTAAGACAATGTAAAGGAAAGACATAAAAAATAATAGGAGTCTAACCATGATAATTTTTATTTTAATA +ACAATATTTGCTATTTATTATATAGCTATGATTGCTAGTTTGTTTAAAAGTGAAGGTTTTTCAATAATAGGTTTAATATT +AGATGTTGTTATAATGGGGACATTGATTTTTTACTATTTTATAGGCGCTCGCTTTGTTGATCATGATTTAAGCAACTTTT +TAATGTTTATGGATACTGGATCATATATTTTTATGTACTTTGCAATTAAGTGTTTATGGGTGAAACCTAAGGTAGTAAAT +TATTTGATTGCAAAAGAATTGGGTGAATCTAAAGAGGTCATTGAAGAGCAGGAATTAGATTTACAGACATCAAAGATAAG +AGGTATCTACTTTTTCATTATTTCAGTAGTGATGTTAATTATCACTAAATTAAGAATGCAACCTGAGTTACAGGCAGATG +CTATATCGATGAATCCTGTATTTATTTTTGTTGGTGTTATAATCATTTTAATTTGGCTAGTACTGGATATATATCGCAAG +AAAAAATACGGTATATTCTTATTCAAGACGATAGTGCCACTAGTTGTTACGACTTGGATTATTATAGCTACAATTATACT +TTCGTAAACGATTTTGATTAATGGGGAATGTAGAGAACATTTCTTTGTAATTTTATAATCGAATAACATACAAAATAAAG +CGACCAATTTTGCAGTGTTACGAATTGTAAACTGCGAACTGGTCGCTTTTTATTGATGTCATGATTAATTTAATGGATGT +AATTATATGATGAAACTTCTGAAGCAGAGATGGTTCTTGATGAAACGATATATTCACCAATCCAGTTCATTTCTGAAATT +AGAATACTTCCATCAATATTAACTTTTTCAACGTAGGCTACATGACCAAATGGACCATTTACTGTTTGTAAAATTGATCC +TCGTGTTGGGTGTCTATCTACTTTGAAGCCATTGCTTGAAGCTTGGCCTGCCCAGTTTTTAGCATCTCCCCAAAATGTAC +TAATCGTGTGTCCATCTTTGGCACGTTTATCAAAGACATACCATGTACATTGTCCAGCAGTATATAAGTTGTTCTTACTT +GTGATAAGAGGCTGATCAATGATTTTACCGTTACCTAATGCTAAAGGTTTACCGTCAGCGGTCTTTGCTTTGTCATTAAA +TTCGGCGATTTGTAATTCGTCATACAATTCGTCTAAATCGATGCCTGTAATAAGCCCTTTGTTATCTTTTGAAAAAGCGT +TATTTAATTCATCGTCATTGTCTTCGACATTCGGTATTGCTGGTGTCAAAGGATTGCTTGGTGACGTTTGAGGCGGTGTG +TGTGAATCAATTGCGTCATTAATGTGCGTATACTGACCACTTAATGAAGAATGGTACTGATTGTTGTTAAGATCACGTTG +ATTTGCGTGTTGATGATTGTCGTTTGTACGTGACTGGTTTTGATGATTGTTGTTTGGCGTGTTGTTTTTGTCATATGTAT +AAGTATACGCGCCGGTGTCTTTATTCACTTTGAACTGTGCGTTTGGGTGTGCTTTCTTTGCTTCTTCTAATGTTTTGCTA +TCATTCGTATATGCTTGAGCCGAGTTAGGCGACATACTAAATAAAGTAAGAGTTGTCATCGTCAGTAAAATTGTTTTCTT +CATAATAACCATTTAATCCTTTATGTATTTAATTTAATTTTAGTATACACATTTATATTACAAAAATGAATGGTTAATTA +AAAATATATGGTTATATTCAATATATTTATTTTAAAAAAGCTAAAAATACTTAAAAAACTATATACATATACTAATAATT +TATATATTATTTGAGTAAGGAGCACTTTTTCAAAAAATAGTGTCCCTAAAAAGTTTTGATAAACTTAAAATATTCAGGAG +GTTTCTAGTTATGGCAATGATTAAGATGAGTCCAGAGGAAATCAGAGCAAAATCGCAATCTTACGGGCAAGGTTCAGACC +AAATCCGTCAAATTTTATCTGATTTAACACGTGCACAAGGTGAAATTGCAGCGAACTGGGAAGGTCAAGCTTTCAGCCGT +TTCGAAGAGCAATTCCAACAACTTAGTCCTAAAGTAGAAAAATTTGCACAATTATTAGAAGAAATTAAACAACAATTGAA +TAGCACTGCTGATGCCGTTCAAGAACAAGACCAACAACTTTCTAATAATTTCGGTTTGCAATAAGCATTCTGAAATTGGC +AAAGTCACATTTTCTAATGTGGCTTTGCTTATCATTTTTTTAAGAAAACAACTGAAAGGAAATAAGCATGAAAAAGAAAA +ATTGGATTTATGCATTAATTGTCACTTTAATTATTATAATTGCCATAGTTAGTATGATATTTTTTGTTCAAACAAAATAT +GGAGATCAATCAGAAAAAGGATCCCAAAGTGTAAGTAATAAAAATAATAAAATACATATCGCAATTGTTAACGAGGATCA +ACCAACGACATATAACGGTAAAAAAGTTGAGCTGGGTCAAGCATTTATTAAAAGGTTAGCAAATGAGAAAAACTATAAAT +TTGAAACAGTAACAAGAAACGTTGCTGAGTCTGGTTTGAAAAATGGTGGATACCAAGTCATGATTGTTATCCCAGAAAAC +TTTTCAAAATTGGCAATGCAATTAGACGCTAAAACACCATCGAAAATATCGCTACAGTATAAAACAGCTGTAGGACAAAA +AGAAGAAGTAGCTAAAAACACAGAAAAAGTTGTAAGTAATGTACTTAACGACTTTAACAAAAACTTAGTCGAAATTTATT +TAACAAGCATCATTGATAATTTACATAATGCACAAAAAAATGTTGGCGCTATTATGACGCGTGAACATGGTGTGAATAGT +AAATTCTCGAATTACTTATTAAATCCAATTAACGACTTCCCGGAATTATTTACAGATACGCTTGTAAATTCAATTTCTGC +AAACAAAGACATTACAAAATGGTTCCAAACATACAATAAATCATTATTGAGTGCGAATTCAGATACGTTCAGAGTGAACA +CAGATTATAATGTTTCGACTTTAATTGAAAAACAAAATTCATTATTTGACGAGCACAATACAGCGATGGATAAAATGTTA +CAAGATTATAAATCGCAAAAAGATAGCGTGGAACTTGATAACTATATCAATGCATTAAAACAGATGGACAGCCAAATTGA +TCAACAATCAAGTATGCAAGATACAGGTAAAGAAGAATATAAACAAACTGTTAAAGAAAACTTAGATAAATTAAGAGAAA +TCATTCAATCACAAGAGTCACCATTTTCAAAAGGTATGATTGAAGACTATCGTAAGCAATTAACAGAATCACTGCAAGAT +GAGCTTGCAAATAACAAAGACTTACAAGATGCGCTAAATAGCATTAAAATGAACAATGCTCAATTCGCTGAAAACTTAGA +GAAACAACTTCATGATGATATTGTCAAAGAACCTGATACAGATACAACATTTATCTATAACATGTCTAAACAAGACTTTA +TAGCTGCAGGTTTAAATGAGGATGAAGCTAATAAATACGAAGCAATTGTCAAAGAAGCAAAACGTTATAAAAATGAATAT +AATTTGAAAAAACCGTTAGCAGAACACATTAATTTAACAGATTACGATAACCAAGTTGCGCAAGACACAAGTAGTTTGAT +TAATGATGGTGTCAAAGTGCAACGTACTGAAACGATTAAAAGTAATGATATTAATCAATTAACTGTTGCAACAGATCCTC +ATTTTAATTTTGAAGGCGACATTAAAATTAATGGTAAAAAATATGACATTAAGGATCAAAGTGTTCAACTCGATACATCT +AACAAGGAATATAAAGTTGAAGTCAATGGCGTTGCTAAATTGAAAAAGGATGCTGAGAAAGATTTCTTAAAAGATAAAAC +AATGCATTTACAATTGTTATTTGGACAAGCAAATCGTCAAGATGAACCAAATGATAAGAAAGCAACGAGTGTTGTGGATG +TAACATTGAATCATAACCTTGATGGTCGCTTATCGAAAGATGCATTAAGCCAGCAATTGAGTGCATTATCTAGGTTTGAT +GCGCATTATAAAATGTACACAGATACAAAAGGCAGAGAAGATAAACCATTCGACAACAAACGTTTAATTGATATGATGGT +TGACCAAGTTATCAATGACATGGAAAGTTTCAAAGACGATAAAGTAGCTGTGTTACATCAAATTGATTCAATGGAAGAAA +ACTCAGACAAACTGATTGATGACATTTTAAATAACAAAAAGAATACAACAAAAAATAAAGAAGATATTTCCAAGCTGATT +GATCAGTTAGAAAACGTTAAAAAGACTTTTGCTGAAGAGCCACAAGAACCAAAAATTGATAAAGGCAAAAATGATGAATT +TAATACGATGTCTTCAAATTTAGATAAAGAAATTAGTAGAATTTCTGAAAAGAGTACGCAATTGCTATCAGATACACAAG +AATCAAAATCAATTGCAGATTCTGTTAGTGGCCAATTAAATCAAGTCGACAATAATGTGAATAAGCTACATGCGACAGGT +CGAGCATTAGGCGTAAGAGCTAACGATTTGAATCGTCAAATGGCTAAAAACGATAAAGATAATGAGTTGTTCGCTAAAGA +ATTTAAAAAAGTATTACAAAATTCTAAAGATGGCGACAGACAAAACCAAGCATTAAAAGCATTTATGAGTAATCCGGTTC +AAAAGAAAAACTTAGAAAATGTTTTAGCTAATAATGGTAATACAGACGTGATTTCACCGACATTATTCGTATTATTGATG +TATTTACTATCAATGATTACAGCATATATTTTCTATAGTTATGAACGTGCCAAAGGACAAATGAATTTCATTAAAGATGA +TTATAGTAGTAAAAACCATCTTTGGAATAATGTCATTACGTCAGGTGTTATTGGTACAACTGGTTTGGTAGAAGGGTTAA +TTGTCGGTTTAATTGCAATGAATAAGTTCCATGTATTAGCTGGCTATAGAGCGAAATTCATCTTAATGGTGATTTTAACT +ATGATGGTCTTCGTACTTATTAATACGTATTTACTAAGACAGGTAAAATCTATCGGTATGTTCTTAATGATTGCTGCATT +GGGTCTATACTTTGTAGCTATGAATAATTTGAAAGCAGCTGGACAAGGTGTGACTAATAAAATTTCACCATTGTCTTATA +TCGATAACATGTTCTTCAATTATTTAAATGCAGAGCATCCTATAGGCTTGGTGCTAGTAATATTAACAGTACTTGTGATT +ATTGGCTTTGTACTGAACATGTTTATAAAACACTTTAAGAAAGAGAGATTAATCTAATGTTGATGAATAGCGTGATTGCT +TTAACTTTTTTAACAGCATCTAGCAATAATGGCGGACTTAATATTGATGTGCAACAAGAAGAGGAAAAGCGAATCAATAA +TGATTTAAATCAATATGATACAACGCTATTTAATAAAGACAGCAAAGCGGTTAATGATGCGATTGCTAAGCAGAAAAAAG +AACGACAACAACAAATAAAAAATGATATGTTTCAAAATCAAGCGAGTCACTCGACTCGCTTGAATGAAACTAAAAAAGTG +TTATTTTCCAAATCTAACTTAGAAAAGACTTCGGAGAGTGATAAAAGCCCCTATATTCAAAACAAGCAGGAGAAAAAAAT +ATTCCCGTACATTTTGATGTCTGTAGGGGCTTTTTTGACTTTAGGATTTGTCATTTTTTCAATTCATAAAGGGAGACGAA +CGAAAAATGAATCAGCACGTAAAAGTAACATTTGATTTTACTAATTATAATTACGGCACATATGACTTAGCAGTACCAGC +ATATTTACCGATAAAAAACTTAATAGCTTTAGTATTGGATAGTTTGGACATTTCAATATTTGATGTCAATACACAAATTA +AAGTGATGACGAAAGGTCAATTACTTGTTGAAAATGATCGACTCATTGATTATCAAATCGCTGATGGAGATATTTTGAAG +TTACTATAGGAGGAAAAATAGATGGTTAAAAATCATAACCCTAAAAATGAAATGCAAGATATGTTAACGCCTTTAGATGC +TGAAGAAGCAGCTAAAACAAAATTACGCTTAGATATGAGAGAGATTCCTAAGTCTTCAATTAAACCAGAACATTTTCATT +TAATGTACTTATTAGAACAACATTCTCCATATTTTATAGATGCTGAATTAACTGAACTACGTGACAGTTTCCAAATACAT +TATGACATTAATGACAATCATACACCTTTTGATAATATTAAATCATTTACTAAAAATGAAAAATTACGTTACTTACTCAA +TATCAAAAATTTAGAAGAAGTAAATCGTACACGCTACACATTTGTGTTGGCACCAGATGAATTATTTTTCACAAGAGATG +GATTACCCATTGCTAAAACAAGAGGGTTACAAAATGTTGTTGATCCATTACCTGTGTCAGAAGCTGAATTTTTAACAAGA +TATAAAGCGCTGGTTATCTGTGCATTCAATGAGAAACAATCATTTGATGCTTTAGTTGAAGGAAACTTAGAACTACATAA +AGGAACGCCATTTGAAACTAAAGTTATTGAAGCGGCAACGTTAGATTTACTAACGGCATTTTTAGATGAACAGTATCAGA +AACAAGAACAAGATTATAGTCAAAATTATGCATATGTACGCAAAGTAGGACATACCGTTTTCAAATGGGTTGCTATCGGT +ATGACAACGTTAAGTGTTTTATTAATTGCATTCTTAGCCTTTTTATATTTTTCAGTAATGAAGCATAATGAGCGCATTGA +AAAAGGATACCAAGCATTTGTAAAGGATGATTATACGCAAGTACTAAATACGTATGATGATTTAGATGGTAAAAAATTAG +ATAAAGAGGCACTTTACATTTATGCCAAAAGTTATATCCAAACAAATAAACAAGGTTTAGAAAAAGATAAGAAAGAAAAT +TTACTTAATAATGTGACACCAAATTCAAACAAAGACTACTTATTATATTGGATGGAATTAGGACAAGGACATCTTGATGA +AGCGATTAATATTGCCACTTATTTAGATGATAACGATATTACAAAGTTAGCGTTGATTAATAAATTAAATGAGATTAAAA +ATAACGGAGATTTATCGAATGATAAACGTTCTGAAGAAACGAAAAAGTATAACGATAAATTGCAAGATATTTTAGACAAA +GAAAAACAAGTTAAAGATGAAAAAGCGAAATCTGAAGAAGAGAAAGCAAAAGCGAAAGATGAGAAATTAAAGCAACAAGA +AGAGAACGAAAAGAAACAAAAAGAACAAGCACAAAAAGATAAAGAAAAACGCCAAGAAGCTGAAAGAAAAAAATAGTATA +GGACTGAGGCAAAGACAATGCATAAATTGATTATAAAATATAACAAACAATTGAAGATGCTCAATTTGCGAGATGGTAAG +ACATATACTATTAGCGAAGACGAGCGTGCAGATATTACGTTGAAATCGTTAGGCGAAGTCATTCATTTAGAACAAAATAA +TCAAGGTACTTGGCAAGCGAATCATACTTCTATTAATAAGGTGCTTGTTAGAAAAGGTGACCTTGATGACATTACATTAC +AGCTTTATACAGAAGCTGATTATGCATCATTTGCGTATCCTTCAATTCAAGATACGATGACAATTGGACCAAATGCGTAT +GATGATATGGTTATTCAAAGCTTGATGAATGCCATCATTATTAAAGATTTTCAATCAATACAAGAATCACAATACGTACG +CATTGTGCACGATAAAAATACAGATGTGTATATTAACTATGAACTACAAGAGCAACTAACGAACAAAGCTTACATTGGTG +ATCATATTTATGTTGAAGGGATATGGCTCGAAGTACAAGCTGATGGTTTAAATGTATTGAGTCAGAATACAGTGGCATCG +TCATTAATTCGCTTAACACAAGAGATGCCACATGCACAGGCAGATGATTACAATACGTACCATCGTTCGCCAAGGATTAT +TCACCGTGAACCGACGGATGATATTAAGATTGAAAGACCGCCACAGCCAATACAGAAGAACAATACAGTGATATGGCGTT +CCATTATACCGCCATTAGTAATGATTGCTTTAACTGTTGTCATCTTTTTAGTGAGACCAATTGGTATTTATATTTTAATG +ATGATTGGTATGAGTACAGTAACGATAGTATTTGGTATTACAACGTATTTCTCTGAAAAGAAAAAGTATAACAAAGATGT +TGAAAAACGAGAGAAAGATTACAAAGCTTATTTGGATAATAAATCTAAAGAAATTAATAAAGCGATTAAAGCACAACGTT +TTAGTTTGAATTACCATTATCCAACGGTTGCTGAAATTAAAGATATCGTTGAAACGAAAGCACCAAGAATATATGAAAAA +ACATCGCATCATCACGATTTCTTACATTATAAGTTAGGTATTGCGAATGTAGAAAAGTCATTCAAATTAGATTACCAAGA +AGAAGAATTTAACCAACGTCGTGATGAACTATTCGACGATGCTAAAGAATTGTATGAATTTTACACAGATGTAGAACAAG +CACCATTAATCAATGATTTAAATCATGGGCCAATTGCATATATTGGTGCACGACATCTCATTTTAGAAGAATTGGAGAAA +ATGCTAATTCAATTGTCAACATTCCATAGTTATCATGATTTAGAGTTTCTATTTGTGACACGTGAAGATGAAGTTGAAAC +ATTGAAATGGGCACGTTGGTTGCCACATATGACATTGAGAGGGCAAAACATTAGAGGATTTGTTTACAATCAACGAACGC +GTGACCAAATTTTAACGTCAATTTATAGCATGATTAAAGAACGTATCCAAGCTGTGCGTGAACGCAGCAGAAGTAATGAG +CAAATTATTTTCACACCGCAATTAGTGTTTGTCATTACAGATATGTCATTAATTATTGATCATGTCATTTTAGAATATGT +AAACCAAGATTTATCAGAATATGGTATTTCATTAATCTTTGTTGAAGATGTGATTGAAAGTTTGCCAGAGCATGTAGATA +CCATTATTGATATCAAGTCTCGTACTGAAGGCGAACTGATTACGAAAGAAAAAGAATTAGTTCAATTGAAATTTACACCT +GAAAATATTGATAACGTCGATAAAGAATATATCGCGCGACGTTTGGCGAATTTGATACACGTCGAACATTTGAAAAATGC +AATTCCTGATAGTATTACATTTTTAGAGATGTATAACGTGAAAGAAGTAGATCAGCTTGATGTGGTTAATCGATGGAGAC +AAAACGAAACATACAAAACGATGGCAGTACCTTTAGGTGTAAGAGGTAAAGATGATATTTTATCATTGAACTTACATGAA +AAAGCACACGGGCCACATGGTTTAGTTGCTGGTACCACTGGTTCAGGGAAATCTGAGATTATCCAATCATACATTTTATC +TTTAGCTATTAATTTTCACCCTCATGAAGTTGCATTCCTATTGATTGACTATAAAGGTGGGGGTATGGCGAACTTATTTA +AAGATTTAGTCCATTTAGTTGGTACGATTACAAACTTAGATGGCGATGAAGCGATGCGTGCCTTAACATCAATCAAAGCC +GAATTGAGAAAACGTCAACGTTTATTCGGAGAGCATGATGTTAACCATATTAATCAATACCATAAGTTATTTAAAGAAGG +TATTGCGACAGAACCAATGCCACATTTATTCATTATTTCCGATGAGTTTGCCGAATTAAAATCAGAACAACCTGATTTTA +TGAAAGAACTTGTATCAACGGCACGTATTGGACGTTCGTTAGGTATTCATTTAATACTTGCGACACAAAAACCATCGGGT +GTTGTTGATGACCAAATTTGGTCTAACTCTAAATTTAAGTTGGCATTAAAAGTACAAGATAGACAAGACAGTAATGAAAT +TTTAAAAACACCAGATGCAGCAGACATTACATTACCAGGTCGTGCGTATTTACAAGTTGGTAATAATGAAATTTATGAAT +TATTCCAATCTGCATGGAGTGGTGCAACATATGACATCGAAGGCGATAAATTAGAAGTTGAAGATAAGACGATTTACATG +ATTAATGACTATGGTCAACTTCAAGCAATCAACAAAGACTTGAGTGGACTTGAAGATGAAGAAACGAAAGAAAATCAAAC +TGAGTTAGAAGCGGTCATAGATCATATCGAATCTATTACAACACGATTAGAAATCGAAGAAGTTAAGCGTCCATGGCTAC +CACCATTGCCAGAAAATGTATATCAAGAAGATTTAGTAGAAACAGATTTCAGAAAATTATGGTCAGATGATGCAAAAGAA +GTGGAATTAACATTAGGACTTAAAGACGTACCAGAAGAACAATATCAAGGACCGATGGTATTGCAATTGAAAAAAGCTGG +GCACATCGCGTTAATCGGAAGTCCAGGATATGGTAGAACAACGTTCTTACACAACATTATTTTCGATGTTGCAAGACACC +ATCGTCCTGATCAAGCACACATGTACTTGTTCGATTTCGGTACCAATGGTTTGATGCCAGTTACAGACATACCACATGTC +GCTGATTACTTTACAGTAGATCAAGAAGACAAGATTGCTAAGGCGATACGTATATTTAATGATGAAATTGATCGTCGTAA +GAAGATTTTAAGTCAGTATCGTGTCACTAGTATTTCTGAATATCGAAAATTAACTGGTGAAACAATTCCGCATGTCTTTA +TTCTTATTGATAACTTTGACGCAGTAAAAGATTCACCTTTCCAAGAAGTTTTTGAAAATATGATGATTAAAATGACGCGT +GAAGGGCTAGCATTAGACATGCAAGTAACCTTAACTGCTTCAAGAGCTAACGCTATGAAAACACCAATGTACATTAATAT +GAAAACGCGTATCGCAATGTTTTTATATGATAAATCAGAGGTGTCGAACGTAGTAGGACAGCAAAAATTTGCGGTTAAAG +ATGTTGTGGGTCGAGCATTGTTAAGTAGTGATGACAACGTATCATTCCATATTGGCCAACCATTTAAACATGATGAGACC +AAATCATATAATGATCAAATTAATGATGAAGTATCGGCGATGACAGAATTTTATAAAGGTGAAACACCAAATGATATTCC +TATGATGCCAGATGAAATTAAATATGAAGATTACAGAGAATCATTAAACTTACCAGATATAGTTGCAAATGGTGCTTTAC +CAATTGGATTAGATTATGAAGGTGTTACACTACAAAAAATTAAATTAACTGAACCAGCAATGATTTCATCAGAAAATCCG +AGAGAAATTGCGCATATTGCTGAAATTATGATGAAAGAAATTGACATATTAAATGAAAAATATGCGATTTGTATCGCAGA +CTCAAGTGGAGAGTTTAAAGCTTATAGGCATCAAGTGGCTAACTTTGCCGAAGAAAGAGAAGACATTAAAGCGATTCATC +AACTAATGATTGAAGACTTAAAGCAAAGAGAAATGGACGGCCCATTTGAAAAAGATTCACTTTATATTATCAATGATTTT +AAAACATTTATTGATTGCACGTATATTCCGGAAGATGATGTTAAAAAGCTTATTACAAAAGGACCAGAACTTGGCTTGAA +CATTTTATTTGTCGGCATTCATAAAGAATTAATAGATGCTTATGATAAACAGATTGATGTTGCACGTAAAATGATTAACC +AATTTAGTATAGGTATTCGTATTTCAGACCAACAATTCTTTAAATTTAGATTTATTCAACGAGAACCTGTTATTAAAGAA +AATGAAGCATATATGGTCGCAAACCAAGCTTATCAAAAGATTAGATGGTTTAAATAGCAATGAATTAAATAGGAGGGAGG +TATGTTATGAATTTTAATGATATTGAAACAATGGTTAAGTCGAAATTTAAAGATATTAAAAAGCATGCTGAAGAGATTGC +GCATGAAATTGAAGTTCGTTCTGGATATTTAAGAAAAGCTGAACAATATAAGCGATTAGAATTTAATTTGAGTTTTGCAC +TAGATGATATTGAAAGCACAGCAAAGGACGTACAAACTGCAAAATCTAGTGCTAATAAGGACAGTGTAACTGTTAAGGGA +AAGGCGCCCAATACGTTATATATTGAAAAAAGAAATTTGATGAAACAAAAGCTTGAAATGTTGGGTGAAGATATCGATAA +AAATAAAGAATCCCTCCAAAAAGCTAAGGAAATTGCTGGCGAAAAGGCAAGTGAATATTTTAATAAAGCAATGAATTAAT +ATTGAGGTGAAGATATGGGTGGATATAAAGGTATTAAAGCAGATGGTGGCAAGGTTGATCAAGCGAAACAATTAGCGGCA +AAAACAGCTAAAGATATTGAAGCATGTCAAAAGCAAACGCAACAGCTCGCTGAGTATATCGAAGGTAGTGATTGGGAAGG +ACAGTTCGCCAATAAGGTGAAAGATGTGTTACTCATTATGGCAAAGTTTCAAGAAGAATTAGTACAACCGATGGCTGACC +ATCAAAAAGCAATTGATAACTTAAGTCAAAATCTAGCGAAATACGATACATTATCAATTAAGCAAGGGCTTGATAGGGTG +AACCCATGATGAAAGATGTTAAGCGAATAGATTATTTTTCTTACGAAGAATTAACAATTTTAGGTGGTAGTAAATTGCCT +CTCGTAAATTTTGAATTGTTTGATCCATCAAATTTTGAAGAAGCTAAAGCTGCTTTAATTGAAAAGGAATTAGTAACAGA +GAATGACAAGTTAACTGATGCAGGTTTTAAAGTGGCTACATTAGTCAGAGAGTATATTAGCGCCATTGTAAATATTCGAA +TTAATGATATGTATTTTGCACCATTTAGCTATGAAAAAGATGAATATATTTTGTTAAGCCGGTTTAAAAATAATGGGTTT +CAAATACGAATTATCAATAAAGACATTGCATGGTGGTCGATTGTACAATCATATCCTTTATTGATGAGACAAGAAAAGTC +CAATGATTGGGACTTTAAACAAATTGACGATGAAACATTGGAGAACTTAAATAATGAAAGTATCGATACGATTGGGCGTG +TTTTAGAAATTGAAATATACAATCATCAAGGTGACCCTCAACAAAGTTTATATAACATTTATGAACAAAATGATTTGTTA +TTCATTCGATACCCATTAAAAGATAAAGTGCTGAATGTTCATATTGGTGTCATTAATACATTTATACGAGAATTATTTGG +ATTCGATACTGATGAAAATCATATTAATAAAGCAGAGGAGTAATGACGTTGAGTGGAAAAATTAGTGTTAAAGCTGAAAC +GATTGCACATGTTGTAAAAGAATTGGAAAGCATAAGTCAAAAGTATGATGAAATAGCTCAAAACTTTGGAAAAATAGCGC +AATTAAATTACTACAGTAGTGAAAAAGCTGCACATTCTATGGAAAATGGCTATAGTAGTGCTGCAACAGTCATTAGTGGT +CTCAAAGGTCCATTGAGTACACTCGGTGGTGGCGTCATGAATTCAGCACAAAAGTTCTTTGAAGCAGATGAACATTGGGG +TACGGAATTTGCCAAGCTTTACTATAATATTGAGGGATAGGTGCATGACATGACAAAAGATATTGAATATCTAACAGCTG +ATTATGACAATGAAAAGTCATCTATCCAAAGTGTAATAGATGCAATAGAGGGTCAAGACTTCTTAGATGTAGATACAACA +ATGGATGATGCGGTAAGCGATGTCAGTTCTTTAGACGAAGATGGCGCAATATCATTAACAAGTAGTGTAGTAGGTCCACA +AGGATCTAAATTAATGGGGTATTATCAAAATGAGTTATATGATTATGCATCTCAATTAGATTCGAAAATGAAAGAAATTA +TTGACACGCCATTTATAGAAGATATAGATAAAGCATTCAAAGGTATAACGAATGTTAAATTGGAAAATATACTAATTAAA +AATGGTGGTGGACATGGTAGAGATACCTATGGGGCTTCTGGGAAAATTGCAAAGGGAGATGCCAAGAAAAGTGACAGCGA +TGTTTATAGCATCGATGAAATATTAAAATCGGATCAAGAATTTGTAAAAGTAATTGATCAGCATTACAAAGAAATGAAAA +AAGAAGATAAGAAATTATCTAAAAGTGATTTTGAAAAAATGATGACTCAGGGCGCTTCTTGTGATTACATGACAGTAGCT +GAAGCGGAAGAGCTAGAGGAGCAAAAGAAAAAAGAAGAAGCTATAGAGATTGCAGCACTAGCTGGTATGGTAGTTTTATC +TTGTATTAATCCTGTTGCTGGAGCAGTAGCTATTGGTGCTTATTCCGCTTATTCAGCAGCAAATGCAGCCACAGGAAAAA +ATATTGTAACTGGAAGAAAGCTATCTAAAGAAGAACGAATCATGGAAGGACTTTCGCTTATTCCATTGCCAGGTATGGGC +TTCCTCAAAGGTGCTGGGAAAAGTTTAATGAAATTAGGCTTCAAAGGCGGAGAAAAATTTGCAGTTAAAACAGGATTGCA +AAAGACAATGCAACAAGCAGTTAGTCGTATTTCACCTAAAATGGGAATGATGAAAAACAGTGTGTTGAATCAATCTCGTA +ACTTTGCTCAAAATACTCATGTTGGACAAATGCTGAGTAACATGCGTGGTCAAGCAACTCATACTGTTCAACAAAGTAGA +AATTGGATTGGACAACAAGCACAAAATGTCAAACGAATAGTGAATAATGGACTTGATAAAGAAATAGCACATCCATTTAA +ACAACAACTTGCACCAGCGGGAATGGGTGGTATAAAATTTGCTGAAACAACTACTTTGAGAAACATGGGTCAAAACATAA +AGCGTGCTGTTACACCACAAAATCACGTGACACATGGTCCAAAAGATAGTATGGTGAGAAGTGAAGGCAAACATAGTATA +AGTAGCCATGAAATGAATTCATCAAAGTATGTTGAATCACCAAACTACACCAAGGTTGAATTCGGAGAACACTATGCAAG +ACTTAGACCTAAGAAACTAAAGGCGAATATTGAATACACAACACCTACTGGTCACATATATCGAACCGATCATAAAGGTC +GCATAAAAGAAGTTTATGTAGACAATCTCTCTCTAAAAGATGGGGATCGTAATAGCCATGCACAAAGAACTGTGGGGGGA +GAGGATAGATTACCAGACGATGATGGAGGTCACTTAATCGCTAGAATGTTTGGTGGTTCAAAAGACATTGATAACCTTGT +GGCACAAAGTAAATTTATCAACCGTCCATTTAAGGAGAAAGGTCATTGGTATAATCTTGAGAAAGAGTGGCAAGAGTTCT +TAAACTCTGGGAAAGAGGTGAAAAATATTAAAATGGAAGTAAAATATAGCGGTAATAGTCAAAGACCGACTATATTTAAA +GTTGAATATGAAATTAATGGTGAAAGAAATATTAGAAGAATATTAAATAAGTAGAGGTGCCAACATGACATTTGAAGAGA +AGCTTAGCAAAATATACAATGAAATTGCGAATGAGATTAGCAGTATGATACCGGTAGAGTGGGAAAAAGTATATACAATG +GCTTATATAGATGATGGAGGAGGTGAAGTATTCTTTAATTATACTAAACCAGGTAGTGATGACTTGAATTATTACACCAA +TATACCTAAGGAGTATAACATTTCTGTGCAAGTATTTGATGATTTATGGATGGATTTATATGATTTATTTGAGGAGTTAA +GAGATTTATTTAAAGAAGAAGATTTAGAACCATGGACATCATGCGAATTTGATTTTACAAGAGAAGGTGAATTAAAAGTT +TCATTTGATTATATTGATTGGATAAATTCAGAATTTGGTCAAATAGGTCGACAAAATTACTATAAGTATAGAAAATTTGG +AATTTTACCAGAAACGGAATATGAAATTAATAAAGTTAAAGAAATCGAGCAATATATTAAAGAGCTAGAAGAATAAACTA +TCTTAATGTAAGACTAAACAATAAAGCTTTGTTTAGTCTTTTTAGCGTTTAAGTAAAAAGCAATAGATACCGTAAAGTTG +ATGCTCATCAAATAATAATATAAAGATAATTTTAGGTTTTTAAACTTTTAATCGAAAAGAATCTAAGTTTATAGCTTTAA +ATACGTAATATGCTATTTAAAAAATAATGTATAGGAGATAGATATGACTACTAAAGAAAATATTGATACTCTTCGAAAGC +CAGGTGCACAAGCCTTAAGTTTAGCATCATTATTTATGATACTTTTTTCATGTCTAACTTTCTTTTTTGGTTTAGATTAT +GAAAGGTTTCCAAATTATTTAAAGATAACGACAATTATAGAATTAATAATTATCATAATTAGTTTACTTCAATGGATTAG +ATTTATAGATTTCGAAAAGGAAAGCGCACAGAAATATAAGAAAATATATGCCCGATTTTTAGTTATTATAAATGTGCTAA +CTACTATCACCGCAGTATTTGCAACATGTAACCTTTATTATTTTGTTGCTGTACAAAATCATTATGACCTATTCAATTAT +TGGTTGATGGGTACGATTTCAATCATAATTAGTTATTTGTTATTAGTAATTGGCGGAATGTTCACGTTATTAAAATTACC +TAAAGTAACAAAACGTTGGGGTGGTAAAACTAAAACACATTTCGGTTTATTATTAACTGCGTTGAGCTCATTTATATATA +TTGAAAAAATTATCGAATATATATTGGTTCCTAATGTCGTAGAATCTAAGTTTATAATTATTGTGAGCATGTTGGTTATC +GCTGGAGCACAATTTGTGGCATTTCAATTTATTATGCAATACAGTAGATTCTATATTTTTGAATTAAACACTGAAGATGA +TGACTAAACAATTTTATAAATTCTGTGAAGTATACTTATATATAAGATAATCAACTTCAACGTATATCACCATATTATTT +TATACTCAATGATGTGAATGGATTTAAGTGGATTGATATTAAAGTAAAAGTTATGGAATATTGAGGTGTGAATATGGAGT +TCTTATTATTAATTGTCGTAGCCGGACTGTATTATATTATATATTTAACTGCTGTGATGTATTCTGAAAAAATAGTAGTA +TTGCCTATAATCATCTATGCCATTGTGTTTGTAATAATTGGTATCACTTATATCTTTATAGGCGACAGCTATGATCAATT +AACAAATTTCAATGTGATTTTGTATATGGGGAGTTTGTTTTATGCATGGATGGCTATTAGAAATCTTTGGAACAGACCAC +TATTACTAAAATATAAGAACATTACAGATAGTTCAAGTGGAATAGTTAATAAATCTGAATACAATTCAGTTGAAAGCTTA +CGTATAAATATTGAAATAGCTAAGTATAAAGGGATTATTTCTTTGATAGTAGCTATAGTACTAACGGTATTAATGACATT +AAAGTCAACACCTCAAATCACTGCAGAAACACGTGACTTAAGTATCTCATTTTTCATACTCAGCTTATTTATCATTATTA +TATTTGCAGTTTGGGATTTGTTTATTAGAGTTAGAAAAGGAGCGTTTGCTTTTGTTGTAATAAGGCCAATATTATTTAGT +TGTTGGTTATTTATTTTGAATATGATTTTATCAAGATTATTATAATTGTATTACATATAGGTGTGGGTAAATTTTTATTT +ATGAAATGATAAAAAAGTATATACTATTAATATCTTTTTTACTATAATTATAAATAGTTAAATAAATTGAAAGACAAAAT +AATGAGTAGGAGTTAATGAGATGGAAAACCAAAAACAAGGCAATGGCTTAAAAATTGCAACATGGGTATTTATTGTATTA +ACAGTAGTTACACCGCTATTTGGTATTGGAAGTATTGTTTGTAGTATTAATTATAAAAAATACGATGCTGAAAAAGGTTC +GAAGTTATTGCAAATTGCAATTATCGTAACAATAATTGCTTTTGTTTTAAATTTATTAGCATATTTAGGTTTAAGATAAG +TAAAACGAATTTGAAGAAGCATTAAGCGACATTTGGGTGTTGTTTAATGCTTTTTATTTAACGGAAATAAGTGTAGAGAA +TAAATTAATAGTTTAATCAAGAGATATTTCGAACACATAGGGGCGATAACATGACTTTCGAAGAAAAATTAAGTGAAATG +TATAGCGAGATTGCGAATAAGATTAGCAGCATGATACCGGTAGAGTGGGAGCAAGTATATGCAATGGCATATGTAACTGA +TCAAGCTGGAGAAGTCATCTTTAATTATACTAAACCAGATAGTGATGAATTAAATTATTATTCAGACATACCTAAAGATT +GCAATGTCTCAAAAGATATTTTTAAGAATTCATGGTTTAAAGTTTATCGAATGTTTGATGAGTTAAGAGAAACTTTTAAA +GAAGAAGGGCTTGAACCATGGACATCATGCGAATTTGACTTTACAAGAGATGGCAAATTGAATGTATCTTTTGATTATAT +AGATTGGATAAATACAGAGTTTGATCAATTGGGCCGTCAAAATTATTATATGTACAAAAAATTTGGGGTTATACCAGAAA +TGGAATATGAAATGGAAGAAGTTAAAGAAATCGAACAATATATTAAAGAGCAAGAAGAAGCTGAACAATAGAGGTGATAA +CATGACTTTCGAAGAGAAAATAAGCAAATTATATAATGAGATTGCGAATGAGATTAGCAGTATGATACCGGTAGAGTGGG +AAAAAGTATATACAATGGCTTATATAGATGATGGAGGAGGTGAAGTATTCTTTAATTATACTAAACCAGGTAGTGATGAC +TTGAATTATTACACCGATATACCTAAGGAGTATAACATCTCTGTGCAAGTATTTGATGATTTATGGATGGATTTATATGA +TTTGTTTGAGGAATTAAGAGATTTATTTAAAGAAGAAGGGCTTGAACCATGGACATCATGTGAATTTGACTTTACAAGCG +AAGGTAAATTAAAAGTTTCATTTGATTATATAGATTGGATAAATACAGAGTTTGATCAATTAGGCCGTGAAAATTATTAT +ATGTATAAAAAATTTGGGGTTTTACCAGAAATGGAATATGAAATGGAAGAAATTAAAGAAATCGATCAATATATTAAAGA +GCAAGATGAAGCTGAAATATAGGGGAGATAACATGACTTTCGAAGAGAAAATAAGCAAATTATATAATGAGATTGCGAAT +GAGATTAGCAGTATGATACCGGTAGAGTGGGAAAAAGTATATACAATGGCTTATATAGATGATGGAGGAGGTGAAGTATT +CTTTAATTATACTAAACCAGGAAGTGAAGATTTGAATTATTATACCGATATACCTAAGGAGTATAATGTTTCTGTGCAAG +TATTTGATGATTTATGGATGGATTTATATGATTTGTTTAAGAATTTAAGAAATTTATTTAAAGAAGAAGGACTTGAACCA +TGGACATCATGTGAATTTGACTTTACAAGAGACGGCAAATTGAATGTTTCATTTGATTATATTGATTGGGCGAATTCAGA +GTTTGGACAAATGGGAAGAGAACATTATTACATGTATAAAAAATTTGGAATTTGGCCTGAAAAAGAATATGCCATAAATT +GGGTAAAAAAAATAAAAGATTATGTTAAAGAGCAAGATGAAGCTGAACTATAGGGGCGATAATATGACTTTCGAAGAAAA +ACTAAGTCAAATGTACAATGAAATTGCAAATGAAATCAGTGGAATGATACCAGTTGAATGGGAAAATATATATACAATTG +CCTATGTAACTGATCAAGGTGGAGAGGTCATTTTTAATTATACTAAACCAGGTAGCGATGAATTGAATTATTACACATAT +ATCCCTAGAGAGTATAATGTCTCTGAAAAAGTATTTTATGATTTGTGGACGGATTTATATAGATTGTTTAAGAAGTTAAG +AGAAACTTTTAAAGAAGAAGGGCTTGAACCATGGACATCAAGTGAATTTGACTTTACAAGCGAAGGTAAATTAAAAGTTT +CATTTGATTATATTGATTGGATAAATACAGAGTTTGATCAATTAGGCCGTGAAAACTATTATATGTATAAAAAGTTTGGT +GTTTTACCAGAAATGGAATACGAAATGGAAGAAGTTAAAGAAATCGAGCAATATATTAAAGAGCAAGATGAAGCTGAACT +ATAGGGGCGATAACATGACTTTCGAAGAAAAGCTAAGTCAAATGTACAATGAAATTGCAAATGAAATCAGTGGAATGATA +CCAGTAGAATGGGAAAAAGTATATACAATTGCCTACGTAGATGATGAAGGTGGAGAGGTTGTTTTTAATTATACTAAACC +AGGAAGTGAAGATTTGAATTATTATTCAGATATTCCTAAAGATTGCAATGTCTCAAAAGATATTTTTAAGAATTCATGGT +TTAAAGTTTATCGAATGTTTGATGAGTTAAGAGAAACTTTTAAAAAAGAAGATTTAGAACCGTGGACATCATGTGAATTT +GACTTTACAAGAAAGGGAAATTTAAAAGTATCATTTGATTATATAGATTGGATTAAATTAGGTTTTGGCCCATCAGGAAA +GGAAAACTACTATATGTACAAAAAATTTGGTATTTTACCAGATATGGAATATGAAATGGAAGAAATTCGAGCAGTAGAGA +AGTATGTTAAAGAGCAAGAGTAGCAGACATGTTATAAAAGACTGTGCAAAATCACCCTCGTTTTACATTTGATTCAAAGA +AGAAGGTAAAAGATAAGATTATTTGCAACTTAAAAAGTCAATTAGCTTATCGGTATCCATACATCATGGATAAATGAGTT +CAACTAATTAACAAATCACGATATAAATTTAAAATTGTCATTAATTGAAAAGGTTATTGTAGTGTATTTTATAAAGTTGA +TGTACATCGACTTATTTTTTAAAACTTATATCAAAAAAATATGAGAAAAGTATATACTATTAAAATGTTTTTTACTATAA +TTATAAATAGTTAAATATTTGGGAATTAAATAAATATATAGGAGGCAATTTTTAGAGATACATAGCAATGTAAAGGTATC +AAATAGTTATTTCATAATTTATTATGATGACATAATTTTCAGTAGATAGTTGTAAATTAACACTGAAATAGATGTTTATA +TTAAGAAGACTTTAATATCATAATTAATCAAAATCTTGCGTCAAACATTTAAACGAATTTTAAGTTGATAACAATTGGCA +AAACATTGTTCAAACATCACAATGATAAAGCATATTATCAGTATTGTAGTGTGTGGAAAATGACAGCCATCTAAGGAGAA +AAATGATGAAAAGAATATTGGTAGTATTTTTAATGTTAGCAATTATATTGGCAGGTTGTTCTAATAAAGGTGAAAAGTAT +CAAAAAGATATTGATAAAGTGTACAAAGAACAGAATCAAATGAATAAAATTGCCTCGAAAGTACAAAACACTATTAAAAC +AGACATTAAACAAGAAGACAGTAATACACATGTTTATAAAGATGGTAAAGTCATTGTTATTGGTATTCAATTATATAAAG +ATCGTGAAAAAATGTATTATTTCGCATATGAAATAAAAGATGGTAAGGCAGAGATTAATAGAGAAATAGACCCAATTAAG +TATATGAAAGACCATAAAGCAGATTATGAAGATGAAAATGTAGAAGTGGAAAAAGATTAACATAGGCTTTCTTTAAAACT +TTTAACCAGTTTCAAAAAGTAAATGAGCTTTAGCGACAATAATTTTTAAAATAAACACGATATAAATTTAAACATTTATA +TATAAGAACACAAACGCAAGTTTGTAAATAATAATAATAAAGGAGTAAGACAATGGAAAAATCGATCAAAATAATGACAA +TAATAGGAATTGTTGTTCAGGGTTTAGCAACGGTATTTAGTTTACTATTGATGGTTTTAGCAGCATCAGGTGTAATGACT +ACAGATGTGTCAACAACAGTTAATGGTGAGGTTGACCCAGTTGATGCAGAAACAGCAGCAGCAATTTTCACTGTATTATT +CTTATCCCTATTCATATTTGGAATCATTTCAATTATTTTAGGTGCAATCGGTATGTTTAAAGCATCTAAAAACAAAAAAA +TGAGTGGTATATTGTTGATTATTGGAGCTGTAATAAGTGGTAACATAATTACATTTGCTTTATGGTTAATCAGTGGTATT +AAACTACTTACTAATAACAAGCCTAAAGATGAAATAAGCGACTTATCATAAACATCGTATATTGAAATTTCAAGATTTCT +TAAGTAATTAAATAAAGTGCTCTCTTAGAAGTAGATTTTCAGTCAATAACTGCTTTTAAGAGAGCATTTTGTATTTGATA +ATGCTCATAGCATCGGATAAAAATCATTGACGAACTTTAAAAAAATACGTTAGACAATGCACTTCTAAATATTAAAGGCA +TCGACTAACGCATTGTTCAAATATAGTTAATTATTTTTATAAAATTGATGATGATCATTCAAGTAAGCATAGAATAAACC +TATAATGAGTCCGCCTCCAATATAGTTACCGATAAAAGCCGCAGCGATATTCGAAATAGCTGGTATGAAGTGCAATGTAT +CAACTTGATAAATTAAACCACCCATAAATAAGCAACTGTTGTAAACGACGTGTTCATAACCCATAAAGGCGAATATGGTA +ACACCGAACATCATGACAAACATTTTTGCGAGTACATCGTCAATTTGCATGGCAATAACTAATGAAATATTGATAAAGAA +ATTGGCGAAGATCGCTTTCATTAATATACTTACAAAACCAGTAGACAACGTTTTATGCTCTATAACTGCTGATAACTGAT +TTAACATATCTGGCGTCATTACATTTGAAAAACGCATGAAACTAAATAAAATAGCAGCACCTAAAATATTTCCTGCAAAG +CATAATAAAAATATTTTCAATACTCTAGTTGGTTTAATTACTTTATAATACAGGCCTACAGTAAAGTACATGAAGTTACT +GGTTAGTAGTTCGGAGTTTGTAAATAAAATGAGTACTAACGCAAAGCTGAATGTAATGGCACTGGCCATATTCACAATGC +CTGGCGGTAAATCTGGTTCGTGTGTTGCTTTAACTGATAATACGAAGACCGTAATAATCCCGATAATAAATCCTGCCATC +ATCGCGCGTAATAAATAACGTTTTAAATAAACGCTTTGTAATATATCTTTTGTTCTTATCGTTTCGACTACGTTATTTAC +CCAGTCGTCCCCATAAAAAATCTTATCCCATTTAATATGTTTCTCCTTCACAATGACATCCTCACTTTTCATTTGTTAAT +CAGAGTATACTATGAATTGCCTTATTTGTGAATAATTTCACAAAATTAATTTTAAAAACTAATATTTTTGAATAACAGCT +AGGATTTTTTAACAAATCATTTATTTTAAAATAATCAATAGACAGACGGTATCACTGAAAAGTCATGAATTTCATATAAA +AAAGCCTAGTAACGATGGGTCAATCATTACTAGGCAGTGAGCATTTATTAAGTTGTCGCTTGTTTCGGACGGCGTATAAA +TACATCGATTATGAAACCGATAATAGCAAAGAGCATGAATGGTACAAGCCAAGCTAAATCGATATCTGCTAAAGGTAACA +TCATAAACGATTTCAAAATAACACCGTGTAATAAGTTGAAACTATTTAGTATTTGTAAAATTGAAATAATCAATGTAATA +ACAGTTGCGAGTCGATAGGCCCAACTGAATCTGAATGTGCTAAACATGTTAGCAAATGATATGAGTACAAGTGCAATCGA +CACGGGATATATTAAAGTCAATAATGGGACAGCAATTTTTAAAATCATTTCTAAACCAAGTGTTGTAAATAAGAACCCTA +TGATAGAGAAAATAAGTGCGAATATTTTATAAGAAAACTTAGGTACGTGTTTCTTAGTAAATGTGGCGCAAGCATTGACG +AGTCCTATACATGTTGTTAGGCATGCAAGGATAACCGTCATTCCAAATACGAGGTTACCGAACGAACCAAATAATCGTAA +TGAGTTGTACGTCAATATATCTGTACCATCTTTAAAGTTTCCTGGAGCTGTTGATGCCCCAACGTATGCAAGTGCAAAGT +AAATCATTCCAAGTAATATGGCTGCAATAAGACCTGAAAAGCAGACATATTTTAAAATTTTCATGCGATCTGTGAGGCCT +TTAAACTTATAGCCATTGACAATGACTACGGAAAAAGCTAACGCAGCAACAAGATCCATTGTAAAATAGCCTTCCAAACT +TCCTGAAATGAAAGGATGTGTTATATATTTATCCTTAGGTGCACTTAGTGCAGATTCAGGGTTGAAAATGACAGCAATAC +TTAATAGAGCGACCATTAATAGTAATAACGGTGTTAATAATTTACCTAAATTATCAACGATTTTCGATGGATTTAAACTA +ATCCAGTAAACGATGGCAAAAAAGATTGCTGCGAATATAATTAAAGTCCATTGGTTATGCACAGGTAAAATGTGTCTTGT +ACCAATTTCGTACGCGACATTTGCAGCACGTGGAATACCGTAAAATGCTCCGATAGACATGTAAATCACGACAGCAAAAA +TAAACCCGAACCATGGATGTATACGATTGCCTACACTTTCAACACCTTCATCATAAAATGCAACAACAATAACAGTAATA +AAGGGGAGTAATATGCCTGTAAGGGCAAAGCCTAGCATACCAATCCACATATTTTGACCCGCTGTATGGCCAAGCATGGG +CGGGAATATTAAATTTCCGGCTCCAAAAAATAGTGAAAATAACATGAGGCCCGAAATAATAACTTGTTTTTTCAATGTAA +TTTCCTTCCTAAAACTAGATATTCATAATAATTTAAAAAATCTGAAAAATAAAAACGTTCTTACTTTATCTTTAAATGAC +AATACTATGATTCTATTATTTTATAAAAAAGATTGCAATAATAACGTCTTGCTATTATTGGATAATGACAATTAAAGTGT +CGTTTTTATGAATTAAAATAGGTGAAAGTAAGTTGGAAATACGTTGGAGCGTTGTGATTTTTATGAGAAATTGTATGAGT +GAGGACTGCTATTTATAAAAGGTAAACTATGGATGAGTTTATGGTTGTTCCAATAGGTAAATAAGAGATAGCACACTGTA +CACATAATGATACGTGGATGAGTGAATATGTCTGATAGGAAGGCCTAGTCACTCAAATGAATTTAAGTTTATTACGTGAT +AAATCACAAATCTCTCTCATGTGATAGGTCTCCCATTAAATCATGCTTATATGAAATGTTCACATATTTGTTAGCTTTTC +AAGAAATAATATTAAATATCTTTTCATAGTATAGCAATATTAAATAAATGCATTTATAATTTTAACTGTATTTTAAATAT +TAATCATGAGGTGATAAGATGAATAAAATTTCAAAGTATATTGCAATAGCATCATTATCGGTAGCGGTTACAGTTTCAGC +ACCACAAACGACAAATTCTACAGCGTTTGCCAAAAGTTCTGCTGAAGTTCAACAAACGCAACAAGCTTCTATACCAGCAT +CACAAAAGGCGAATCTTGGTAATCAAAATATTATGGCAGTGGCTTGGTATCAAAATTCAGCTGAAGCAAAAGCATTATAT +TTACAAGGTTATAACAGTGCAAAGACACAGTTAGATAAAGAGATTAAAAAGAATAAAGGTAAACATAAGTTAGCTATTGC +TTTGGATTTAGATGAAACAGTTTTAGATAATTCTCCATATCAAGGCTATGCATCAATACATAATAAACCTTTCCCAGAAG +GTTGGCATGAATGGGTACAAGCTGCTAAAGCTAAACCTGTCTATGGCGCAAAAGAATTCTTGAAATATGCTGACAAAAAA +GGTGTCGATATCTACTATATTTCTGATAGAGATAAAGAAAAAGATTTAAAGGCAACACAAAAGAACTTAAAACAACAAGG +TATCCCTCAAGCTAAGAAGAGTCATATTTTACTAAAAGGTAAAGATGATAAGAGTAAAGAATCACGCAGACAAATGGTTC +AAAAGGATCATAAACTTGTCATGCTATTTGGAGATAATTTATTAGACTTTACAGATCCAAAAGAAGCTACAGCTGAATCT +CGTGAAGCATTAATTGAAAAACATAAAGACGATTTCGGTAAGAAATATATCATTTTCCCTAACCCAATGTATGGTAGTTG +GGAAGCTACGATTTACAACAATAACTATAAAGCAAGTGACAAAGCAAAAGATAAATTACGTAAAAATGCTATTAAGCAAT +TCGATCCTAAAACAGGCGAAGTTAAATAATATATGAATTGGACGTCTACATGTACTTAAAGGTATATGTAGGCGTTTTTA +AATTGATCCTTAATTTTAAATGTACCGTACAAAAATAATAGACAATTAATTGCATTTTCCATAAATTTGCTTTTAATATA +AAAAGTTCGGGTATAATGAAGGATAAGTGTATGTATAACATTACAGAGCGCAACACGACGTACGTGCATCTAAGAATATA +AACAATGTTTAAATCATAGTAGTAGGGAGAGAAATGCATGTTTTTAGCTTGGAATGAAATACGGCGCAACAAATTGAAGT +TTGGACTAATTATTGGTGTGTTAACGATGATTAGTTACTTGCTATTTTTATTATCTGGATTGGCGAATGGTCTTATCAAT +ATGAATAAAGAAGGCATTGATAAGTGGCAAGCAGATGCCATTGTTCTAAATAAAGATGCCAATCAAACTGTGCAACAATC +TGTTTTTAACAAGAAAGATATTGAAAATAAATACAAGAAGCAAGCTACTTTGAAGCAAACAGGGGAAATTGTGTCTAATG +GCCATCAAAAAGACAATGTTTTAGTGTTCGGTGTTGAAAAGTCATCATTTTTAGTTCCGAGTTTAATAGAAGGGCATAAA +GCGACTAAAGATAATGAAGTGTTAGCTGATGAAACGCTTAAAAATAAAGGATTTAAAATTGGTGACACATTATCACTATC +TCAATCAGATGAAAAATTGCATATCGTAGGTTTTACAGAAAGTGCAAAATATAATGCGTCATCAGTCATTTTCACGAATG +ACGCTACCATTGCCAAGATCAATCCTAGATTGACTGGAGATAAAATTAATGCAGTTGTTGTACGTGATACAAATTGGAAA +GACAAAAAATTAAACCAAGAGCTTGAAGCGGTAAGTATTAATGACTTTATTGAAAATTTACCAGGTTATAAACCACAGAA +CTTAACATTAAACTTTATGATTTCATTCTTATTTGTCATTTCAGCTACAGTTATAGGCATTTTCCTATATGTCATGACAT +TACAAAAGACGAGTTTATTTGGCATATTAAAAGCTCAAGGATTTACGAATGGCTATTTGGCGAATGTGGTAATTTCGCAG +ACGGTCATATTAGCACTATTTGGTACGGCATTTGGCTTACTGTTAACAGGCGTTACAGGTGCATTTTTACCTGATGCAGT +ACCTGTCAAATTCGATGTACTAACATTGCTCGTATTTGCAATTGTGTTAATGATTGTCTCTGTATTAGGAAGTTTATTCT +CCATTTTAACAATTAGAAAAATAGATCCGTTAAAGGCGATTGGGTAGGAGGTGTAGCAAATGTTGAAATTTGAAAATGTA +ACAAAGTCATTTAAAGATGGGAATCGTAACATTGAAGCGGTTAAAGATACAAATTTTGAGATAAATAAAGGTGATATTAT +AGCATTGGTTGGACCTTCTGGCTCTGGTAAAAGTACATTTCTAACTATGGCAGGTGCTTTACAAACACCGACATCTGGGC +ACATTTTAATCAATAACCAAGATATTACGACAATGAAGCAAAAAGCATTGGCAAAAGTTAGAATGTCTGAAATAGGTTTT +ATTTTACAAGCTACAAACCTTGTACCATTTTTAACGGTAAAGCAACAATTTACATTATTGAAAAAGAAAAATAAGAATGT +TATGTCTAATGAAGACTATCAGCAACTTATGTCACAATTAGGTCTAACTTCATTGCTTAATAAGTTACCTTCAGAAATTT +CAGGTGGTCAGAAACAACGTGTGGCGATAGCCAAAGCGTTATATACGAATCCGTCGATTATTTTAGCGGATGAACCTACC +GCGGCGTTAGATACTGAAAATGCGATTGAAGTCATTAAAATTCTACGTGATCAAGCCAAACAAAGAAAGAAAGCATGTAT +TATTGTTACACATGATGAACGACTTAAAGCATATTGTGATCGTTCATATCATATGAAAGATGGCGTCCTTAATCTTGAAA +ATGAAACAGTAGAATAGTTTTATTAAGCCGGTACATCATGTGCCGGTATTTTTATGTTTATGTATTATTTGAATAAACTT +TCACATTCAATTAATAATAATTATTATCGAAAATCAGAAATATTCCGTGAAATATAATATTTTTTGTAGTAAAATGGCCT +CTAAGTATTCAATATTTAAATATGGGGATTGAATATAAAATTATCGTAATGGGGGTCAATGGTTATGGATTTATTGATAG +GTACTTTATTTTTATTTTTGGTCTTAGTGATTTTTACATTATTTACATATAAAGCGCCTAATGGTATGCGTGCCATGGGA +GCATTAGCTAATGCAGCAATCGCAACATTTTTAGTGGAAGCATTTAATAAATATGTTGGTGGCGAAGTATTCGGTATTAA +ATTTTTAGAAGAGCTAGGAGACGCTGCGGGAGGTCTAGGTGGTGTCGCTGCCGCTGGATTAACAGCATTAGCTATCGGTG +TGTCACCAGTATATGCATTAGTTATAGCAGCCGCGTGCGGTGGTATGGATTTATTACCAGGTTTCTTTGCGGGTTATATG +ATTGGATATGTGATGAAATATACAGAGAAATATGTGCCGGATGGTGTCGACTTAATTGGATCGATTGTCATCTTAGCGCC +ATTAGCTCGTCTTATTGCAGTATTATTAACGCCAGTAGTGAATAGTACATTGATTCGAATTGGTGATATTATCCAAAGTA +GTACGAATACGAATCCAATTATCATGGGTATCATTTTAGGTGGTATTATTACGGTTGTCGGCACAGCGCCATTGAGTTCA +ATGGCATTGACAGCATTATTAGGTTTAACGGGTGTACCTATGGCTATTGGTGCCATGGCAGCATTTAGTTCGGCATTTAT +GAATGGGACGCTATTCCATCGCTTAAAATTAGGTGATCGTAAGTCTACGATTGCAGTAAGTATTGAACCTTTATCACAAG +CAGATATTGTATCAGCCAATCCAATTCCAATCTATATTACAAATTTCTTTGGTGGTGCGATTGCTGGTTTAATTATTGCT +ATGTCAGGTTTAATTAACGATGCGACAGGTACAGCTACACCGATTGCAGGATTTTTAGTTATGTTTGGATTTAATCATCC +GACGACAATTGTGATTTATGGTGTAGTAATGGCGATTGTAGGTGCGCTTGCAGGTTATCTTGGTTCAATTGTATTTAAAA +AATATCCAATTGTTACTAAGCAAGACATGATTAATCGAGGTGCAGTAGACGCATAGCATCATCATATTGAATAGTAAAAA +CAAATAAAACATAGTAACGTGATTCAGTCGATGTAACAGTCGATAATGAGTCACGTTTTTTTATAGAAAAATACAAGACA +TAAAAATGTCATAATTTATTGTCGACAAATATCATACTGTATAAACATTTATCATTTTCTCAAGTACCTTTTACACGATG +GAATGAACTTACTTTTTACGAAATTATGCGTATTTTATAAACAAATATCATTGATATAACGGTAAATGTAAGCGTTTACA +ACAGAAATAACAGCATGCTACGATATTTTTGTAAATTCACTGATTCAAGTATTTTAAGTCAATATGAGGAGGGATGTTAT +GAGCGATTCTGAGAAAGAAATTTTAAAAAGAATTAAAGATAATCCGTTTATTTCACAACGTGAACTTGCTGAGGCAATTG +GATTATCTAGACCCAGCGTAGCAAACATTATTTCAGGATTAATACAAAAGGAATATGTTATGGGAAAGGCATATGTTTTA +AATGAAGATTATCCTATTGTTTGTATTGGCGCAGCGAATGTAGATCGTAAGTTTTATGTGCATAAAAATTTAGTTGCAGA +AACATCAAATCCTGTAACGTCAACACGCTCTATTGGTGGCGTAGCAAGAAATATTGCTGAGAACTTAGGTAGGCTTGGCG +AAACGGTCGCTTTTTTATCTGCTAGTGGACAAGATAGTGAATGGGAAATGATTAAACGATTGTCCACACCATTTATGAAT +TTGGATCATGTTCAACAATTTGAAAATGCGAGTACAGGTTCATATACAGCTTTAATTAGTAAAGAAGGCGACATGACATA +TGGCTTAGCAGATATGGAAGTGTTTGACTACATTACGCCTGAATTTTTAATTAAGCGTTCACACTTATTGAAAAAGGCTA +AGTGCATTATTGTAGATTTGAATTTAGGCAAAGAGGCATTAAACTTCTTATGTGCCTATACCACGAAACATCAAATCAAA +TTAGTTATCACCACGGTTTCTTCCCCAAAAATGAAAAATATGCCTGATTCATTACATGCTATTGATTGGATTATCACGAA +TAAAGATGAAACAGAAACATACTTAAATTTAAAAATAGAATCTACTGATGATTTAAAAATAGCTGCTAAACGCTGGAATG +ATTTAGGTGTTAAAAATGTTATTGTGACAAATGGCGTGAAAGAACTCATTTATCGAAGTGGTGAGGAAGAAATCATTAAG +TCAGTTATGCCATCAAATAGTGTGAAAGATGTTACAGGTGCAGGCGATTCATTCTGTGCTGCAGTAGTGTATAGCTGGTT +AAATGGGATGTCTACTGAAGATATATTAATTGCTGGTATGGTTAACGCAAAGAAAACGATAGAAACGAAATATACAGTTA +GGCAAAACCTAGATCAACAGCAACTTTATCACGATATGGAGGATTATAAAAATGGCAAATTTACAAAAGTATATTGAGTA +TTCTCGAGAAGTTCAGCAAGCACGGGAGAACAATCAACCGATTGTAGCATTAGAATCAACAATTATTTCGCATGGTATGC +CGTACCCACAAAATGTTGAAATGGCAACAACAGTAGAGCAAATTATCAGGAATAATGGTGCCATTCCAGCAACCATAGCC +ATTATAGATGGCAAAATTAAAATTGGTTTAGAAAGCGAAGATTTAGAAATACTGGCAACTAGTAAAGACGTTGCTAAAGT +ATCTAGAAGGGATTTAGCAGAAGTTATTGCGATGAAGTGTGTTGGTGCTACTACTGTAGCGACGACGATGATATGTGCTG +CAATGGCTGGTATTCAATTTTTTGTTACAGGAGGTATTGGGGGCGTCCATAAAGGTGCAGAACATACGATGGACATTTCA +GCAGACTTAGAAGAACTGTCTAAAACAAATGTCACTGTTATCTGTGCAGGTGCCAAATCAATTTTAGACTTACCTAAGAC +GATGGAGTATTTAGAAACAAAAGGCGTTCCAGTTATTGGATATCAAACGAATGAATTGCCAGCATTCTTCACTCGCGAAA +GCGGTGTTAAGTTAACAAGTTCGGTTGAAACGCCAGAACGACTTGCTGACATTCATTTAACAAAACAGCAGTTAAATCTT +GAAGGTGGCATTGTTGTTGCTAATCCAATTCCATATGAGCATGCCTTATCAAAAGCATATATTGAGGCAATCATAAATGA +AGCTGTTGTTGAAGCGGAAAATCAAGGTATTAAAGGTAAGGACGCCACACCGTTCTTGTTAGGGAAAATTGTAGAAAAAA +CGAATGGTAAAAGTTTAGCAGCAAATATAAAACTTGTTGAAAACAATGCGGCGTTGGGTGCTAAAATTGCTGTCGCTGTT +AATAAATTATTGTAGGTGATGATACATGAATATTTTATTCGCTATCACAGGGATAGCATTTGCACTATTTGTTGCGTTTT +TATTCAGTTTTGATCGTAAAAAAATAGACTTCAAAAAGACGTTAATAATGATATTTATTCAAGTGTTGATCGTGTTATTT +ATGATGAACACAACGATTGGTTTGACAATTTTAACTGCACTAGGTTCATTTTTTGAAGGGCTAATAAATATTAGTAAAGC +AGGCATAAATTTTGTTTTTGGAGATATACAAAATAAAAATGGCTTTACGTTCTTTTTAAACGTATTACTGCCATTAGTTT +TTATTTCTGTATTAATAGGCATCTTTAATTATATTAAGGTATTACCATTTATTATCAAATATGTAGGTATCGCTATTAAT +AAAATAACTAGAATGGGGCGCTTAGAAAGTTATTTTGCTATTTCAACAGCAATGTTTGGGCAACCAGAAGTATATTTAAC +AATAAAAGATATTATTCCAAGATTATCTAGAGCGAAATTATATACAATTGCGACGTCTGGTATGAGTGCTGTTAGTATGG +CAATGCTAGGTTCATATATGCAGATGATTGAACCCAAGTTCGTAGTTACAGCAGTAATGTTAAATATTTTTAGTGCGCTT +ATCATCGCCAGTGTAATCAATCCCTATAAATCTGATGATACTGATGTTGAAATTGATAACTTAACGAAATCCACAGAAAC +TAAAACATTGAATGGAAAAACAGGAAAACCTAAGAAAGTTGCCTTTTTCCAAATGATTGGTGATAGTGCGATGGATGGGT +TTAAAATCGCTGTTGTAGTAGCCGTAATGTTGTTAGCATTTATTTCATTAATGGAAGCAATTAATATCATGTTTGGTAGT +GTTGGTTTGAACTTTAAACAGCTTATTGGCTATGTGTTTGCACCAATCGCATTCTTAATGGGGATTCCATGGAGCGAAGC +TGTTCCAGCTGGCTCTTTAATGGCGACTAAATTAATTACAAATGAGTTTGTAGCAATGCTTGATTTTAAAAATGTCCTGG +GTGATGTATCAGCTCGAACACAAGGTATCATTTCAGTTTACTTAGTAAGCTTCGCTAATTTTGGTACGGTTGGTATCATC +GTAGGTTCAATTAAAGGCATTAGTGATAAACAAGGAGAAAAAGTTGCATCCTTTGCAATGAGGTTGCTACTTGGTTCAAC +TCTAGCTTCAATCATTTCAGGATCAATCATTGGCTTAGTATTGTAAATGAATCGAAGTACCTAAATTAAATTCATGGCAA +AGCTAAACCCCGTCACCAAGTTGGCGCAACAGCGCATCATAACTTAGTGACGGGGTTTTATCATAACAATCTACTTTTTC +GTAGCCGTTTTTGAAATGTATGTTGATGGTTTATCTTTTTCAAAAATTGTTAATCCCGTTATATCTTTTTTATGTTTTGA +AGGGACAATGAAGCTAAGTATATAAGCAAAGACAAAAGCAACTGTAAATGAAATGGTAGATACATAGAAAGGTGAGTTAC +CTTTGCCAACACCATTATAGACATAAGCAAAGATGATACCCAATATTAATCCACAAATAACACCGAATGTATTCGTACGT +TTAGTGAAAATACCAACTGCAAATACACCAGCCAATGGAACGCCGAATAATCCAGTCACAAACAAGAATAAATCCCATAA +GTCATTTGAATTAGAAGCAATTAAGTATAGTGACATTCCAAAACCGAAAATACCTGCAATGATAATAATGAAACGTGCAA +AGTTAACTTCGTGTCGCTCGCTACCTTTTCCGAAGAAGCGTTGCTTAATGTCGATTGAAATACAAGCAGATATAGAATTT +AAACTAGATGAAATGGTAGACTGTGCAGCGGCGAAAATGGCTGCAATAAGTAATCCTGCTACAAATGGTGGCATCTCAGT +CAAAATGAAATATGGCACTACAGATGATGTATTGAAGCCTTTTGGTAAAACAGCTTCATGTGTATAAAATGAATACAGCA +TTGTACCCATACCATAAAATAAGGGTGCTGAAATTAAAGCTAGGATACCATTTGTCCATAACGATTTATTTGTTTCTTTT +AAACTATCAGAAGCTTGATAACGCTGCACGACGTCTTGACTCGCTGTGTATTGATACAAGTTGTTGAAAATATTTCCTAG +GAAAATAATTGGAATGGCAGCTGCCGCAGTATTTAGTTTCCAATTGTCTGCACTAATTAATTTTTTGTGCTCAATCGCAT +CTGCAAAGACAGTGCCGAAACCGCCTTTAATGTTCACAACACCTAGAATAATAATAACTAAAGCGCCGCCTAATAAAATG +ACGCCTTGAATGAAATCACTCCAAACCACACCTTCGAAACCACCTAAAAATGTATATAAAATACATAGTAAACCAACGAG +TGATGCAACGATATAAGGGTTCATGTCTGATACAGATGTGATTGCTAATGTTGGTAAGTAGATAACAATTGCAACACGCC +CTAAATGGTAAACGACAAATAATAATGAGCCAATGACACGTATGCTAGGGCCAAATCTAGCTTCTAAATATTCATATGCA +GATGTTACCTTTAACTTTTTAAAGAAAGGGACATAGAAATAAATAAGTAATGGAATAATTGCGACGATAGCAATGTTACC +AGCGATATATGACCAATCTGTTAAAAATGCTTTCTCTGGTGTCGACATAAATGTAATCGCACTTAACGTAGTAGCATAAA +TTGAAAAGCCAACTACCCAAGATGGCAAGCGACCACTTGCGGTAAAGAAACTATTGGTACTTTGGCTCGCGCGCTTGGTA +AAATAAACGCCAATGAACAACATAGCTAGTAGATAAATGATAACGGCAACCCAGTTTAGTGTGCCAAATCCAACTTCTTT +CATGGGCAACATCCCCTTTACAATGTATTGATTCTTTGATGTCTATAAATCGTATTTTGCAATGAGTTGATCTAATGTTT +GTCGATGTGCTTCGTTAAAAGGTTTGAAAGGTCTTTTCGGTAATCCTGCATCAATGCCACGATGACGTAATATTTCTTTC +AATGTTGGATAAATCCCCATTGATAACACTGTTTCGATAATGTCGTTTGAATCATGTTGCAGTTGGTAAGCTTCTTGAAT +TTGACCTTGTCGTGCTAAGTCGAAGATTTTTCTTGCACGGCGACCATTAACGTTATATGTAGAACCAATTGCACCATCTA +CGCCAGAAATCGTAGCTTGAACTAACATTTCATCAAAGCCAGATAAGATTAATTTGTCTGGGAATGCTTTTCTAATACGT +TCGAGTAGGAAGAAGTTTGGCGCTGTATATTTAACACCAACAATTTTTTCATGATTAAATAGCTCGCTGAATTGTTCAAT +AGAAATATTCACACCTGTTAAATCTGGTATTGCATAAATAATCATATTGTTCTGAGTTGCTTCGATAATATCGAAATAGT +AATCTCTAATTTCTTCAAAAGTAAATGGATAGTAGAATGGTGTTACGGCAGAAAGTGCATCATAACCGAGTTCTGTGGCA +TATTTTCCAAGTTCAATGGCTTCATTTAAATCTAACGAACCTACTTGAGCAATCAATTTCACTTTATCCCCAACTGCCTC +TTTGGCAACCTTGAAAACTTGCTTCTTCTGCTCTGTATTTAATAAAAAGTTTTCGCCTGAGCTACCATTTACATAAAGAC +CGTCTAATTCTTCAGTTTCAATGGCATTTTGAGCAATTTGTTTAAGTCCTTGTTCATTTACTTGACCATTTTCATCAAAA +GGAACGAGTAACGCTGCATATAAACCTTTTAAATCTTTGTTCATTATGAAGTCCCTCCAAAAATCATTTGATAATATAGT +TTACAGCTATAATTGTAAACGCTATCATAAAATGTAACAATATCTTTTTGAAAATTGTAGTCATATTTATGTATAATTAA +TGAAAATGTTTTTCAAAATCAATAGAAATGGAGTGAGTAAGGTGTATTACATCGCAATCGATATTGGAGGCACTCAAATT +AAATCGGCAGTTATTGATAAGCAATTGAATATGTTTGACTATCAACAAATATCAACGCCGGACAACAAAAGTGAGCTTAT +TACTGACAAAGTATATGAGATTGTAACAGGATATATGAAGCAATATCAGTTGATCCAACCTGTCATAGGTATTTCATCAG +CAGGCGTTGTTGATGAACAAAAAGGCGAAATTGTATACGCAGGGCCAACCATTCCGAATTATAAAGGTACTAATTTTAAG +CGATTATTAAAATCACTGTCTCCTTATGTCAAAGTAAAAAATGATGTAAACGCTGCATTACTAGGCGAATTGAAATTACA +TCAATATCAAGCAGAACGGATCTTTTGTATGACGCTTGGTACAGGCATTGGGGGTGCGTACAAGAATAATCAAGGTCATA +TTGATAATGGTGAGCTTCATAAGGCAAATGAAGTTGGGTATTTATTGTATCGTCCAACTGAAAATACAACGTTTGAGCAA +CGTGCTGCAACGAGTGCATTGAAAAAGCGCATGATTGCCGGAGGATTTACGAGAAGCACACATGTGCCAGTATTGTTTGA +AGCAGCTGAAGAAGGTGATGATATTGCAAAACAAATATTGAATGAGTGGGCAGAAGATGTAGCAGAAGGGATTGCCCAAA +TACAGGTCATGTATGATCCAGGGCTTATATTAATTGGGGGCGGTATATCTGAACAAGGAGATAATCTCATTAAATATATC +GAGCCGAAAGTTGCACACTATTTACCAAAAGACTATGTTTATGCACCAATACAAACGACTAAGAGTAAAAATGATGCAGC +ATTATATGGCTGTTTGCAATGATAGTTGAAAGAAGGAGTCATTCTAAAATAGAATTTGAAACCGTTACGAGAGATGAGAG +CTGTTGTTAGTTCCACACATCACACTCTATCTAGGACCAATCTAAACTATATCAACCAACAGTGTGCCACGGGCAAATTA +AATTGAAGAAGCTGAGATATTAAAATTTTAGAAAATGTAAAAAAATATTTGGTATTGAAATTAAAAAAGCACCTAGCAAC +TCGTTGGGACAATCACGATGATTGTCTACAGTTGCAGGTGGATTTGAATATACTACTAGTTATTTGTTGTCTAGGATAAT +AGATTTAGTATGTTGATAAGTTTGACTCAGATTCGTATTTTCTAATAAATGATAACTCACGATATCGATTAAAAAGAGTG +TCGCAATTTGTGTGTTGATAAATTGATGGTCGGTATTACGCGATTGATCCGTTGTTAAAAGTACTAAATCTGCACAATCT +GTAAGTTTACTACCTTCAAAATTTGTGATGGCAACGACATATGCACCATGAGATTTGGCGACTTCCGCTGCAGAAATTAA +TTCCGAAGTATTACCACTATTTGACATAGCAATAAACATATCCGAATGAGATAGTAGGGATGCCGATATTTTCATTAAAT +GTGAATCGGTAGTAACATTACCTTTTAGCCCCATACGAATCATACGATAATAAAATTCAGTCGCTGATAAACCAGAGCTA +CCTAGTCCAGCAAAGAGTATATGTCGACTTGATTGAAGTTTGTCGATAAAGGTTTGGATAATGTCGTTATCAATAAATTC +ACCAGTTTGTTGAATGATTTGTTGATGATATTTATGAATTCTTTGAATAATTGGGCTATTTTCAATAACTGTCTCTGTCA +TTTCTTGTTGAATATTAAATTTTAAATCTTGGAAATTCTCATAATCCAGCTTATGACTAAAGCGTGTCATCGTTGCTGGT +GATGTACCAATCGCATGGGCTAAGGAGTTAATCGTTGAAAAGGCATCGCTATAACCATTTTGTCTTATATAATTGACGAT +GCGTTTATCAGTTTTTGTAAATAAATGTTGATAACGTTGAACACGATTCTCAAATTTCATTGTGTCACCCCTTCATCTTA +ATGATTACTATTATATATGAAAAATATTTTCAAGATAGTAAAAAGCATTGATAAAAATTATCTTAATGATATATTGTAAA +TGACTTTACGTGAAAAAACGACTTATGGAGTGAGGAATAATGTTACCACATGGATTAATAGTATCTTGTCAGGCACTACC +AGATGAACCATTGCATTCATCTTTTATTATGTCGAAAATGGCATTAGCTGCGTATGAAGGTGGTGCTGTTGGTATTCGCG +CAAATACTAAGGAAGACATTTTAGCAATTAAAGAAACGGTAGATTTACCAGTTATTGGCATTGTGAAACGTGACTATGAT +CACTCAGATGTTTTCATTACTGCAACGTCAAAAGAAGTTGATGAACTGATAGAAAGCCAATGTGAAGTCATTGCATTGGA +TGCAACGTTACAGCAACGTCCGAAAGAAACGTTAGACGAATTAGTATCATATATTAGAACACATGCACCGAACGTTGAAA +TCATGGCTGATATCGCGACCGTTGAAGAAGCTAAAAATGCCGCACGACTTGGCTTTGATTATATTGGCACGACGTTACAT +GGCTATACTAGTTATACGCAAGGACAATTACTTTATCAAAATGACTTCCAATTTTTAAAAGATGTACTACAAAGTGTTGA +TGCAAAAGTTATTGCGGAAGGTAATGTCATTACACCGGATATGTATAAACGTGTGATGGACTTAGGCGTTCATTGTTCAG +TCGTTGGTGGTGCGATAACACGACCAAAAGAAATTACGAAACGTTTTGTTCAAATTATGGAAGATTAAATGATAACGATA +AAAAAACGAGATGACCATCATTAATTAAAGGCACCTAATTATCTTAGGTGGCTGAATGAATGTAATGGGTTCATCTCGTT +TTGTTTGTTTATGATAGTGATTTTATTTTCAACTTTATCCAAAAATAAGTAAAGCGACGGGGATGGTGATTAATAGCGAC +AACGCCACGCGTAAAAACCAAATGATGATGAGTTTCCAGACAGGTATTTTAATTTCAGTTGCTAGTATACATGGCACTAA +TGCTGAGAAAAAGATAATGGCTGATACGCTTACTACACCGACGACAAATTTAGTACTCATTGCAGCTTTAGTTACTAACA +AAGATGGTAGAAACATCTCTACAATAGAAATCGCTGACGCTTTTGCTAGTAAAGCCTGATCAGCAATTGGGAAAATATAA +ATAAATGGATAGAAGATATAGCCAAGCCAATCAATGAATGGTGTATAGTTCGCTACAATCAGTCCTAAAAAACCAATCGA +TAATATAGAAGGTAAAATACCAACAGTCATTTCTAAACCGTCTTTCAAATTGTCCCAAACGTTCTTCACGAGAGATGGTG +TTAATGCATTTTGTTTCATCGCCTCTGCATATGCAGTTTTCAGTCTGCTTCCTTCAATAGCAACTTCTTGTTCTCCTTCT +TGTCCGTTATAATATTCTGTTGATTCATTGCTGATTGGCGGTAGCCATGCAGTAATTGCAGTCACGACAAATGTGATGAC +TAAAGTTATCCAAAAGTATAAATTCCAATGCGGCATTAATCCTAAAGTTTTAGCAACGATAATCATAAAAGTTGCTGAAA +CTGTTGAAAAGCCAGTCGCAATAATCGTGGCTTCTCGTTTGTTGTACATCCCTTGCTTATAGACACGATTAGTAATCAAT +AATCCTAAGGAATAACTGCCGACAAACGAAGCCACTGCATCGACAGCGGATTTTCCTGGTGTTTTAAAAATAGGTCTCAT +AATAGGCTCCATATAAACACCGACAAATTCTAATAAGCCATAGCCCACTAATAAAGAAAGCGCAATTGCACCTACTGGAA +TTAAGATACTTAATGGCATCATTAATTTTTCAAACAAAAACGGACCATAGTTAGCTTTAAATAGTATTGATGGACCGATT +TTAAATACATACATTATACCGATCATTGCACCTGCAACTTTAAATAATGTAATGACCAAGTTTGTGATTGAAGTCATAAA +AGTACGTCTCACTATTGGTAACGCTGTACCAATTAAAATCATAATCAGTGCAACATAGGGCATAAGTGGACCTATGATTG +AGCGAATGGCTAGATGAACATGATCGACGAAAATAGTGTTGTTACCATTAATCGTAAAAGGAATAAAGAAACATAGTATG +CCCACTAAACTATAGACAAAAAAACGCCATGCACTTGGTTGTTGTGCATTAGAATGATATTGATTCATTAAAGCAACCCC +TTTGTTTAAATGAATACACAAAACTGTATGATGCATCTTCCCCTTAATGAGATGAATCATTATTTTAATTTAGAAAAATC +TGAAAACTTACTATAATTGTATAGTTTGAATTATTTTCATACCAATACAAATTAACTAATTATATATAGATTGAAACTAT +ATTACTTAATAAAATATTTATCTTAAATGTTGTTGTGTTGATTCAACACCACAACTAAAAGTGTTTATAAATTATTTGGA +AATACACATATTTGTAAATGATTAGTATCGATTTAATATCGTATTATTAAATTTTTATTAATTTTGTAGTCTTAATCAAA +AAATAATATATGTCATGTTATATTGAAGGTGCAGTTGTTTTTCATTCTCAAGAGGGGGTCAAAAAAATACTTTTGAGGTG +ATTATATGTTAAGAGGACAAGAAGAAAGAAAGTATAGTATTAGAAAGTATTCAATAGGCGTGGTGTCAGTGTTAGCGGCT +ACAATGTTTGTTGTGTCATCACATGAAGCACAAGCCTCGGAAAAAACATCAACTAATGCAGCGGCACAAAAAGAAACACT +AAATCAACCGGGAGAACAAGGGAATGCGATAACGTCACATCAAATGCAGTCAGGAAAGCAATTAGACGATATGCATAAAG +AGAATGGTAAAAGTGGAACAGTGACAGAAGGTAAAGATACGCTTCAATCATCGAAGCATCAATCAACACAAAATAGTAAA +ACAATCAGAACGCAAAATGATAATCAAGTAAAGCAAGATTCTGAACGACAAGGTTCTAAACAGTCACACCAAAATAATGC +GACTAATAATACTGAACGTCAAAATGATCAGGTTCAAAATACCCATCATGCTGAACGTAATGGATCACAATCGACAACGT +CACAATCGAATGATGTTGATAAATCACAACCATCCATTCCGGCACAAAAGGTAATACCCAATCATGATAAAGCAGCACCA +ACTTCAACTACACCCCCGTCTAATGATAAAACTGCACCTAAATCAACAAAAGCACAAGATGCAACCACGGACAAACATCC +AAATCAACAAGATACACATCAACCTGCGCATCAAATCATAGATGCAAAGCAAGATGATACTGTTCGCCAAAGTGAACAGA +AACCACAAGTTGGCGATTTAAGTAAACATATCGATGGTCAAAATTCCCCAGAGAAACCGACAGATAAAAATACTGATAAT +AAACAACTAATCAAAGATGCGCTTCAAGCGCCTAAAACACGTTCGACTACAAATGCAGCAGCAGATGCTAAAAAGGTTCG +ACCACTTAAAGCGAATCAAGTACAACCACTTAACAAATATCCAGTTGTTTTTGTACATGGATTTTTAGGATTAGTAGGCG +ATAATGCACCTGCTTTATATCCAAATTATTGGGGTGGAAATAAATTTAAAGTTATCGAAGAATTGAGAAAGCAAGGCTAT +AATGTACATCAAGCAAGTGTAAGTGCATTTGGTAGTAACTATGATCGCGCTGTAGAACTTTATTATTACATTAAAGGTGG +TCGCGTAGATTATGGCGCAGCACATGCAGCTAAATACGGACATGAGCGCTATGGTAAGACTTATAAAGGAATCATGCCTA +ATTGGGAACCTGGTAAAAAGGTACATCTTGTAGGGCATAGTATGGGTGGTCAAACAATTCGTTTAATGGAAGAGTTTTTA +AGAAATGGTAACAAAGAAGAAATTGCCTATCATAAAGCGCATGGTGGAGAAATATCACCATTATTCACTGGTGGTCATAA +CAATATGGTTGCATCAATCACAACATTAGCAACACCACATAATGGTTCACAAGCAGCTGATAAGTTTGGAAATACAGAAG +CTGTTAGAAAAATCATGTTCGCTTTAAATCGATTTATGGGTAACAAGTATTCGAATATCGATTTAGGATTAACGCAATGG +GGCTTTAAACAATTACCAAATGAGAGTTACATTGACTATATAAAACGCGTTAGTAAAAGCAAAATTTGGACATCAGACGA +CAATGCTGCCTATGATTTAACGTTAGATGGCTCTGCAAAATTGAACAACATGACAAGTATGAATCCTAATATTACGTATA +CGACTTATACAGGTGTATCATCTCATACTGGTCCATTAGGTTATGAAAATCCTGATTTAGGTACATTTTTCTTAATGGCT +ACAACGAGTAGAATTATTGGTCATGATGCAAGAGAAGAATGGCGTAAAAATGATGGTGTCGTACCAGTGATTTCGTCATT +ACATCCGTCCAATCAACCATTTGTTAATGTTACGAATGATGAACCTGCCACACGCAGAGGTATCTGGCAAGTTAAACCAA +TCATACAAGGATGGGATCATGTCGATTTTATCGGTGTGGACTTCCTGGATTTCAAACGTAAAGGTGCAGAACTTGCCAAC +TTCTATACAGGTATTATAAATGACTTGTTGCGTGTTGAAGCGACTGAAAGTAAAGGAACACAATTGAAAGCAAGTTAAAT +TCATCTTCTGAATTTAATATGCTATGTAAATCGTGCTGTTATCATGGCACATCAGATATAAGTAGCATCACAGTGTTGAA +TTTAAAAATAGTAAAGTGAAATAAAGCGCCTGTCTCATTAGCGAAAACTAAAGGGACAGGCGTATCTGTTTATGAGCTTA +ATAAATTGTATGAATAATATGGTTGATCGAATAACTGTTTATCATGATGATAAATTGAGTTTTTTAAAATAATGATATAT +TACATCATTGTTATAGCGTTTAAGAAATCAACAACTTTACGATAAATAGTGATTGCTTCGTCATTAGGTCTACGATCAAA +ATCATGCTCGTTTTTATTCACGCGTTCAAATGTTGAATGTGGAACATGATTCATGATATGTTCGCTTTCCTCAACGGGAA +CATCATAATCGCCATTACAATGCGCAATGAAAACAGGTGGAAGTGTTTTAAGTTCATCTGGTGCAATATTATATTTTGAA +TTAGTATAATCAGCAATGTTAATCATATTTATCCATTTACCTGTGCCACGTGCATAAACGTAGATTAAAAAACGTTGTGC +GATTTGATCTTGAACAACCGGTGTTGGTGAAGTGAGTTGTGCAATCATTGTTTCGTTTACGCTTTGAGCTATTTTTGCGT +AATAACTATTAGTTGTTTTAAAAGGTTCAGTGTTGATGCGACTATAACCATAAAAATCAATAACACCATCAATATCTCTG +TCTCGTGCAATTAATAGACTTAAATATGCACCTGATGATCTGCCAAAGGTAAAAATAGGGCAATTAGAATATTGTGATTG +AATCGCATCGAATGATGCGTAGACATCCTCAATAATGCAATCGAGACTTACTTCTGGTAATAAACGATAACTTAGTTGAA +TTAAATCGTAATGTTCCGTAAGGATATCGATATACTGTGGGGATAAATCGTTAGCTTTACCGAACATTAATCCACCACCG +TGGATGTAGACAATAGCGCCTTTTGTTGGTTGATTTTTTGCTTTAATAATTGTGTAAGGTAATGCAAATGCATCTTTAGT +AATTACTTTATCTTTAATTTCAGTCACGATTTAATAGGCTCCTTATTTTTGATATTGATGTCATTATAACACTGTCTTAA +ATTTCCATGAAAAATAGTCTTAAGACGATGAGTCATGATAATTCTGTTCCAATTGACGTAAAGCGTCACGGGTATGCTTC +TTTAGACCTTCCCCATAATCCATCATTTTAACAATATCTTTAAAAGCAGCATGTGGAATGGCTAAATCTTCTAAATCTGC +CATAGAAAATTCAAGATTGATATCATGTGGTCGCTGTTCAGCAAGTTTATGCACAAAGTCAGGTTCTGTGACAAAAGGCG +AAGACATGCCGACCATATCTGCATGTTGTAAAGCATCTAAAGCAGACTCTGGAGAATTAATCCCGCCACTTGCAATTAAA +GGGATACGACCTGCTAAATGTTCATAGACAATTTGGTTAACTGGTCGACCGAAATGATCACCTGGTGTACGAGACGTATT +TTGATAAATATGTCGACCCCAGCTAGCGATTGCTAAGTATTGGATGTTTGAAACGTCCATGACCCAATTGATTAATTGGT +TGAACTCGTCAATGGTATATCCTAAATCACTGCCTCTGGTTTCTTCTGGCGTTGCTCGAAATCCTAAAATAAAATTGTCA +GGTGCTTCTTTATCAATCACTTCTTGTACCGCACGCATAACTTCTAAACATAATCTTGCACGATTTTTTAATGAGTCGGC +ACCGTAATGGTCTGTACGTTTATTCGAAAAAGTTGAGAAAAATGTTTGAATCAGCAAACGTTGTGCAATCGAAATTTCCA +CACCATCAAAACCTGCTTTAATCGCGCGTAATGTAGCATCGCGATACTGCTGAATGATGCTATTGATTTTCTCATGAGAC +ATGGCGATAACATCGTGTTCAATCGGTGAATGCAATGTCATAGGGCTTGGTCCATACACCTTTCCAAAATTTAAAATGGC +TTGATTTGAAAAACGACCAGCATGCGCTAGCTGGATAATAGCGAGGCTACCATGTTGTTTCATCGTAGATGCCATGTTAG +TTAATCCAGGGATACAAGCATCATGATCAATATTAAAGCCATATTCAAACAATTGACCATAAGGTTCAATGTAAGCAGCG +CCGGTGACTTGCATTCCAGCTGAATTAGAGCGACGTGCAGCATAAGCCAAGTCTTCTTTTGTAATATAGCCTTCTTTTGT +TGATGTGTTTACGGTCATTGGTGATAATACAAAGCGATTCGAAATTTTGATGCCATTAGGTAAGTGGATTGATTGTAAAA +GTGGTTTGTATCGGTACATACTATGATTCCTTTTCTATTCAATATTGTTTTCAAAGTACCATGGAAAGAATGAATAATCA +ATGATGAACAGTCTTGATAGAATAGAATTGGTACATGGAAAGTATTTTTAAAATTAAACTAATGAATGGCATTTGTAGGT +CTGAAAATATGAATATGAAAAAGAAAAATAAAGGCGAAAAGATATAAAAGTTAATTGAAAAACGTTATCATATACGTGGG +TATATGAAGAGGGAATGGTATTAAGAACGCTAAAATGTTATGTCGGTTTGACATGACAGGATAAGTTTGGAGATGACGGA +TTGGTTAAATTAAGCGTATTAGACTATGCCTTAATAGATGAAGGTAAGGATGCACAAAAGGCATTGCAAGATTCAGTGAC +ACTTGCAAAATTAGCAGATCGACTTGGCTTTAAGCGAATTTGGTTTACGGAACATCATAATGTACCAGCGTTTGCGTGTA +GTAGTCCAGAACTTTTGATGATGCATACATTGGCGCAGACAAATCACATACGAGTTGGCTCTGGTGGTGTGATGCTGCCG +CACTATCGACCTTATAAAATTGCTGAGCATTTTAGAATGATGGCAGCGTTATATCCAAATCGTATTGATTTAGGTATTGG +TAATAATCCAGGTACTACTATGGTAAAGCAAGCTTTAGATGGAATAAATCCTACATATGATAGTTACGATGAATCGATTT +CGTTATTACGTGATTATCTTACAATAAAGGATAAACCAAGTGCGCATACGTTAGGTGTCCAACCACACATTGATCATTTT +CCAGAAATGTGGTTATTAAGTAGTAGCGCAACATCTGCCAAAATAGCTGCCGAACTAGGTATAGGGCTTTCTGTTGGAAC +ATTTTTGCTACCAGATATAAATGCGATACATACAGCGAAGGATAACATTGATATTTACAAAAAACATTTCCAAGCATCAA +CGATTAAAATGGACGCAAAGGTGATGGCATCTGTATTTGTCATTGTAGCTGATAACGAAGCGGAAGTAGCAGCATTACAA +CATGCCTTAGATGTTTGGTTATTAGGTAAATTACAATTTGCAGAATTTGAAGATTTTCCTTCAGTAGACACAGCACAAAA +GTATAAGCTTAATGATCGAGACAAAGAGATGATTCAAGCACATCAAGCACGCATCATTGCAGGTACACAAGAAAAGGTTA +AAGCACAATTAGATGATTTCATTGCTACGTTTGAAGTTGATGAGGTGTTAGTAGCACCGCTTATTCCAGGTATTGAACAG +CGTTGTAAAACATTAAAATTACTCGCGGAAATTTATTTGTAGCATTTTAAATAGAAGAGAAAGGATGAAGATAAGATGAA +AAAGTTAGCCAATTATTTATGGGTAGAAAAAGTAGGAGATTTGTATGTGTTTAGTATGACACCTGAATTGCAAGATGATA +TTGGGACAGTAGGTTATGTTGAATTCGTAAGTCCAGATGAAGTTAAAGTGGATGATGAAATTGTGAGTATCGAAGCATCG +AAAACGGTCATTGATGTGCAAACGCCATTGTCAGGAACGATTATTGAGCGAAATACAAAAGCGGAAGAAGAACCGACAAT +TTTAAACTCTGAAAAACCAGAAGAAAATTGGTTGTTCAAATTGGATGATGTCGATAAAGAAGCATTCCTAGCATTACCGG +AGGCTTAAATGGAAACGTTAAAATCAAATAAAGCGAGACTTGAATATTTAATCAATGATATGCATCGAGAGAGAAATGAC +AATGACGTATTGGTAATGCCATCTTCATTTGAAGATTTGTGGGAATTATATCGAGGCTTAGCAAATGTCAGACCGGCATT +ACCTGTAAGTGATGAATATTTAGCTGTACAAGATGCTATGTTAAGTGATTTGAATCGTCAACATGTTACGGATTTGAAGG +ATTTGAAGCCGATAAAAGGTGACAATATCTTTGTTTGGCAAGGTGATATCACGACGTTAAAAATCGATGCTATTGTTAAT +GCTGCAAATAGTCGTTTTCTAGGATGTATGCAAGCTAATCATGACTGCATTGATAATATTATTCATACAAAAGCGGGTGT +TCAAGTTCGACTTGATTGTGCAGAGATCATTCGACAACAAGGGCGCAATGAAGGTGTAGGTAAAGCCAAAATAACACGTG +GATATAATTTGCCAGCAAAGTATATAATTCATACGGTTGGTCCGCAAATACGTCGATTGCCTGTTTCAAAGATGAATCAG +GACTTGTTAGCTAAATGTTATCTTAGCTGTCTTAAATTGGCTGATCAACATAGTTTAAATCATGTCGCTTTTTGCTGTAT +ATCTACAGGTGTATTTGCTTTTCCTCAAGATGAAGCAGCAGAAATTGCTGTTCGAACAGTAGAAAGCTATCTCAAAGAAA +CAAATTCAACATTGAAAGTCGTGTTCAATGTATTTACAGATAAGGATTTACAACTGTATAAGGAGGCATTTAACCGTGAT +GCAGAGTAGTAAGTGGAATGCAATGTCTCTGTTAATGGATGACAAGACAAAGCAGGCTGAAGTATTGCGTACTGCGATTG +ATGAAGCAGATGCGATAGTGATTGGAATTGGTGCAGGCATGTCTGCATCTGACGGATTTACATATGTAGGAGAGCGTTTT +ACGGAAAATTTCCCAGATTTTATTGAAAAATATCGCTTCTTTGATATGTTGCAAGCGAGTTTACATCCTTATGGCAGTTG +GCAAGAGTATTGGGCATTTGAGAGTCGTTTTATTACATTAAACTATTTAGATCAACCTGTAGGTCAGTCTTACCTCGCTT +TAAAATCCTTGGTGGAAGGTAAACAGTACCACATTATAACTACGAATGCAGATAATGCTTTCGATGTAGCTGATTATGAT +ATGACTCATGTATTTCATATACAAGGGGAGTATATACTGCAACAGTGTAGTCAGCATTGTCATGCTCAAACGTATCGCAA +TGATGATTTAATTCGTAAAATGGTTGTTGCGCAACAAGATATGCTTATACCTTGGGAGATGATTCCAAGATGTCCAAAAT +GTGATGCCCCAATGGAAGTGAATAAACGTAAAGCGGAAGTTGGGATGGTTGAAGATGCTGAATTTCATGCGCAACTACAT +CGTTATAATGCTTTTCTAGAGCAACATCAAGATGATAAAGTGTTGTATTTGGAAATTGGAATTGGTTATACTACACCACA +ATTTGTGAAGCATCCTTTTCAGCGTATGACACGTAAAAATGAAAATGCCCTTTATATGACGATGAATAAAAAGGCATATC +GCATTCCGAATTCAATTCAAGAACGTACCATACATTTAACTGAGGATATCTCAACATTGATTACAGCAGCACTCCGGAAC +GACAGCACAACGAAAAATAACAACATTGGAGAGACAGAAGATGTACTTAATAGAACCGATTAGAAATGGAGAATATATTA +CTGATGGTGCGATTGCACTCGCTATGCAAGTTTATGTTAACCAGCATATCTTTTTAGATGAAGATATTTTATTCCCTTAT +TATTGTGATCCAAAAGTGGAAATTGGACGTTTTCAAAATACTGCTATAGAAGTGAATCAAGATTATATAGATAAACACAG +TATTCAAGTAGTTCGCCGAGATACTGGTGGTGGCGCTGTGTATGTTGATAAAGGTGCCGTTAATATGTGTTGTATTTTAG +AACAAGACACTTCAATTTATGGTGATTTTCAACGATTTTATCAACCAGCTATAAAGGCGTTGCATACATTAGGTGCAACA +GATGTGGTACAAAGCGGTAGAAATGATTTAACATTGAATGGTAAAAAAGTGTCAGGCGCCGCAATGACATTAATGAATAA +TCGTATTTATGGCGGTTATTCGCTATTACTTGATGTTAATTATGAAGCAATGGATAAAGTGTTAAAGCCTAATCGCAAAA +AGATTGCATCGAAAGGGATTAAATCTGTGCGCGCACGTGTTGGTCATCTTAGAGAAGCACTGGATGAAAAGTATCGTGAT +ATAACCATTGAAGAATTTAAAAATTTAATGGTGACGCAGATTTTGGGAATCGATGACATTAAAGAGGCGAAACGATATGA +ATTAACGGATGCAGATTGGGAAGCGATTGATGAATTAGCTGATAAAAAGTATAAAAATTGGGATTGGAATTATGGCAAGT +CACCCAAATATGAATACAATCGAAGTGAAAGATTATCTTCAGGTACGGTAGACATAACAATTTCTGTTGAACAAAATCGT +ATCGCAGATTGTCGTATTTATGGGGATTTCTTTGGACAAGGTGATATAAAAGATGTGGAAGAAGCATTACAAGGAACAAA +AATGACAAGAGAAGATTTAACGCATCAGTTAAAGCAATTAGACATCGTTTATTATTTTGGCAATGTTACGGTAGAAGCAT +TAGTGGATATGATTTTAAGTTAATATTGTTATTTTATGTATGCTGAATCATTGGAAGTGTTTGCTTGCTCTTGAAAAGGT +GACAATAGTGTTTGGTGAAGGTTGAACATATGAGTGGAAATTATTGCCTTTAACTATTCAAAGTATGATATATATATGGT +TTTTGTTTCTAAATGATTGGGTATTTGAAAATAGATGAGTTTAATATTTTAAGGAATATAATGATGTTTACTTTTATAAT +TCATATAGAATATTAAGCAATATAAGTCTGTTGATATATACAAAATATAATGACTGCTATAATGAGTAATCAATAGACAC +AAAGAGGAGATTATGTGATGAATAATAAAGTATTAGTAACCGGTGGTACAGGGTTTGTTGGCATGCGAATTATTTCACGA +TTATTAGAACAAGGTTATGACGTACAAACGACGATACGTGATTTAAGTAAAGCTGATAAAGTAATTAAAACAATGCAAGA +CAATGGCATTTCCACAGAGCGATTAATGTTTGTCGAAGCGGATTTATCACAAGATGAACATTGGGATGAAGCAATGAAAG +ATTGCAAGTATGTCTTGAGTGTAGCATCTCCGGTGTTTTTCGGTAAAACAGACGATGCAGAAGTGATGGCGAAGCCTGCA +ATTGAAGGTATACAACGTATTTTAAGAGCTGCAGAACATGCGGGTGTTAAACGTGTGGTAATGACTGCAAACTTTGGTGC +AGTTGGTTTTAGTAATAAAGATAAAAATTCAATCACAAATGAAAGTCATTGGACAAATGAAGATGAACCAGGCTTATCAG +TATATGAAAAATCAAAATTGTTAGCTGAAAAGGCAGCGTGGGATTTTGTTGAGAATGAAAATACAACAGTAGAATTTGCC +ACAATCAATCCAGTTGCAATTTTTGGGCCATCATTAGATGCACACGTTTCAGGAAGCTTTCATTTATTAGAAAATTTATT +GAATGGTTCAATGAAACGTGTACCGCAAATTCCGTTAAATGTTGTTGATGTGAGAGACGTAGCTGAACTGCACATTTTGG +CAATGACAAATGAACAAGCTAATGGCAAGCGATTTATTGCGACGGCTGATGGACAAATTAATTTGTTGGAAATTGCAAAA +TTAATTAAAGAAAAGAGACCTGAAATAGCTCAAAAAGTTTCTACTAAAAAATTACCAGACTTTATTTTGAGTCTAGGTGC +TAAATTTAATCATCAAGCTAAAGAAGGTAAACTTTTATTAGATATGAACCGCAATGTAAGTAACGAACGTGCAAAAACAT +TACTTGGTTGGGAACCGATTGCGACACAAGAAGAAGCAATTTTAGCAGCTGTCGATAGTATGGCTAAGTATCATTTAATA +TAATACAAAACACCGTCCATAAGTAAGTGGCTTTCCTCTTGATAAAAGGGGCATTCACTCAATATGTGACGGTGTTTTAT +ATTTCTACGTTAACACTATTGCTGTTCTTTTTGACGGCCTTTTAATAAAATTGTTGTCGCACCTACGATAATAATAAATA +GAATCGCACCAAATAATCCCATATATTTTACTGCGTTACCGAACACGATACCGACAGCTAAAAAGTCTGTATCTGAGAAT +GTTGTTGCAGCACCACCTAATTCGCCTAAAAATGGCAAGAATAATAATGGTAAAAACGTGATTAGGATACCATTTAGAGC +GGCGCCAGCAATAGCACCTTTAATACCGCCTCTTGCATTACCGAATACAGCAGCCGTTGCACCTAAGAAGAAGTGTGCAA +CTACGCCAGGTAAAATGACGACGCCACCAAATAAGAATAAGATAAACATACCGATGACACCTGTAATAAAGCTGACAAAG +AATCCAATTAATACTGCATTTTGTGCATAAGGGAACACAATAGGGCAGTCTAATGCAGGTTTAGAATTTGGTACAAGCTT +TTCAGAAATTCCTTTAAATGCTGGGACGATTTCAGCTAAGATTAAACGAACGCCCGTTAAAATAATAAATACACCAGCAG +CAAATGTCACACCTTGAATTAATGAAAAGACAATAAAGTTTTGACCATCACTAATAGATTCGTGTACATAACTAACGCCC +GCAAATAAGCATGCGATGAAGTAAAGTAATGCCATCGTAATCGAGATACTAATTGTACTTTCTCGTAAGAAACTTAAGCC +TTTTGGAAATTTAATCTCTTCCGTTGATTTAGACTTACCTTTGAATAATTGACCTACAGCACCTGCGGCAAAGTAACTGA +TTGAGCCAAAATGACCTAAAGCTACTTGGTCATTCCCTGTAATTTTTCGCATCGTAGGTTGGAGTAATGCAGGTAATACT +GCCATGATTAATCCTAATACGAGTGCGCCGATAACAATCGTTAGCCAGCCTTTAATATGACTGACTGTTAAAATGATTGC +TAAAAACGCAGCCATGTAAAATGTATGATGACCTGTTAAAAAGATATATTTTAAATTAGTGAAGCGGGCAATTAAAATAT +TAACAATCATGCCACAGACCATGATGAGTGCAGCTGTTGTTCCAAAATCTTTTAAGGCTAGTGAGACGATAGCTTCGTTG +TTAGGTACGATACCTTGCACACCAAATGCGTGTTGGAATATTTTGCCGAATGGTTCAAGAGATCGAACGACGACATCAGC +ACCTGCACTTAAAATTAAGAAGCCTAATATCGTTTTAATGGTTCCTGAAGTGATCGTTGCGGCAGGTTTTTTCTGAACGA +TTAAACCTATAAAGGCAATCAGTGCAACAAGAATGGCTGGTTGACTTAAAATATCGACTATAAAATTAAGGATTGCTTGC +ATAGGTCGTACCTCCTTTAAATCATGTTAAGTTGTTGTAATTTTTCTGAGAGCTTTTGTTGTAATTCAGCTTTATCTAAA +ATATTATCAAGAACTAAGACATCCCCTAGACGTTCGGCATTTTCAGCTAAATCTCTACCACAAATAAACAAGTCAGCCAT +CTCTGGACTTGCTGTCATAATGTCACTATGTTCAACTTCGATATCAGATGGTGCATTAAGTTGCCTAAGTGCTTCTTGTG +CGTTCATTTCTACCATAAAACTACTTCCTAAACCGTGGCCACATACTACTAAAATTTTCATATTAATCATGCTCCTTTAA +AATGTTTTTAATGTCTTGTGCATTTGTTGCAGTTAATAGTTGCTGGACTGTTTGGTTATCGCCCAGTACGGTTGCTAAAT +TTTGTAATACAGATAAGTGTGAATGATTGTCGATGGCACTCAATACAAAAATGAGAGATGCGTAGTGATCTTCATCACAA +AATGCCACATGTTGATTCAACTTTAATAGACTTAAACCAACTTGATGTACGTCATTGTTCGGTCTTGCATGTGCAATTGC +AATTTCAGGTGCGATAACGATATAAGGTCCAAGTTCATTAACGCTATCAATCATTGCTTGAACATAGCCTTGTTCAATAA +TTTGTTCTTGTAGTAATGGCTGAGAAGCTATAGTTATAGCTTCAGTCCAATCATTTACTTGTTCTTTTACAATGATGCGT +GTTGTTGACAAAATGTCTAATGACACGTTATTAAGCCTCCTTTGTCATAGTTAAAGCAATGTGTTGTTTAATTTTAAAAA +TATTCCCATCTAAGAAATCTTGTCGATATAAGTCGTTGCTTAAGCATTCGCTTAACTGTCCCAATGCCTTTAAATGTGCA +TTGGGGTGGTCCGTTGCTAATGTAATTACAAGGTGAACGGGATCGTTAGCTTTACTACCAAAGATAATCCCTTCAGTGAA +ATATGTTAGTGCGAAACCTACACCATTCTGTACATAATCAGTACCAGCGTGAATAAGTGCAATATGTGGACTAATGACCA +TATATGACCCGAATTGTTCAAATTGTTTTAAAATTGCAGCTGTATAATTTGAATAGACAATGCCATCATTGATTAAAGGT +TGCACAGCCACTGCAATTGCGGATTCAATTGATAATGGTTGTTTATTTATAATGATGCGATGTTCAGGCAATAAATCTGC +GAGTGACTTGCCATCAGTTGCCATTTTCATGACTCGTTGTTCTCTTGAGTCATTGATAATTTGATTCAATTTTTGACGAG +ATTGTTGATTGATAAATGGATCGACATGAATAACTGGTACAGCTGATATTTCACAAGGTACTGTTGAAATGACATAATCA +ATGTTATCTTGCAATAATCGACTTTCTTCCAATTGATAAATGGAATAGGCATCCCAAATGTGAAACTCAGGATACAGGTG +ATTTAGTTTTGATTTTAAAAGTTGTGACGTGCCTATACCAGAACCACATAGTAAGACAACCTTAATCATTGATTGTTTAT +GTGTTGCAACACGCTCTATACTTGATGCGAAGTGAATTGTAATGTATGTTAATTCATCTTCGTTGAAGCGAATAGCAGCA +TCTTGTTCAATTGGACTAATATGCTTGCTAACGGCTTCAATGATTTGAGGATAGCGACGCATAACTTCTTGCCTCAAAGG +ATTAGGTTGTAGCATATCGTATTTAATACGATGTATAGCTGGTTTGATATGTGTGATCAGACTGGTATGTAACTTGTTGT +CTTTTGACATATCAATGCCTAATTCTTGGCTAACACAAGTGATCAATTCATGTATATTTTGCGATAAATCATGGTATTCA +AAGGTAATTGAAGATGCTGTATGTTCAGTCATTTTAGAGCCTAGTAAATGTAACGTGATAAAGATAATTTCAGACTCTGG +AAATGTGACATTACAACTGCGTTCTAAGTTTTCTATCATTTTTGAAGCAATAGCATACTGATTAGTATGTCGCCATTTAT +CAATTTCATTGATAGGTATATCGAACGAAAAATTTTCATTTAAACGCTGAATGGCAATGAGTATATGATAGATTAAGCCA +TCGATAGCCGACTGAACTAAATGATAATTTTCACTATTTAATGTCTTAATAATGGCACGGCGAACCAATGCGATTGATTC +TGAATTAAAGATATCCGCCTCTATAAAAGGTGCAGCTTGTTTCATATATTGATGTATAAAGTGTGCATACGCTTTACGAT +AATGATCTTCCTCACCAATAATATTGAATCCTTTATTGTGGACATAATTTAACTTTAAATGGTATTGATCTAGTTGGGCT +TGAATCATTTTAATATCATCTGCAATTGTCCGACGCGAAACATTAACATCTTGCGCAAGTTGCTTTGTTGAAACAGGATC +GGTTGTTTCGAATAACTTTAAAGCGATATGTGTGAGTCGTTCATCTTTTGAAAAATGAATTTGATTTGTTAGATTGTGCT +CTAATTCATTTAATAGTGTCGCGTGAGCTGTTGTTACTTTGATGCCTGCAGCTTTATTACGGCTGACTTGGTAATGATAA +GTTTCAGCATATTGCTCAATATATGCTATATCATATTGAATGGTACGAGGTGATACACCAAGTTGATTAGCAATGGTATT +GATTGGAATAAACGTTTGCTCATGAATTAAAAGATACAAAATTTCGATTTGTCTATAACTTAACAACGTAATATCCTCCT +ATTTGTAATTGTAAGCGATTTCTTAAAAACGTAGATATGCAATCTCTTTCATATTTTAATCCGAAAAATTGCATATCAAA +ATGTTTATGGCGCAAGATTTTATAGGAACTTTTAAAATAAATTAGATATTCATGTTGACAATTTAAAAATGTCGCAGTAT +ATTTAGTTAGACATCTAACGAAATGGTGGTGCAATAAATGGAATTCACTTATTCGTATTTATTTAGAATGATTAGTCATG +AGATGAAACAAAAGGCTGATCAAAAGTTAGAGCAATTTGATATTACAAATGAGCAAGGTCATACGTTAGGTTATCTTTAT +GCACATCAACAAGATGGACTGACACAAAATGATATTGCTAAAGCATTACAACGAACAGGTCCAACTGTCAGTAATTTATT +AAGGAACCTTGAACGTAAAAAGCTGATCTATCGCTATGTCGATGCACAAGATACGAGAAGAAAGAATATAGGGCTGACTA +CCTCTGGGATTAAACTCGTAGAAGCATTCACTTCGATATTTGATGAAATGGAACAAACACTCGTATCGCAGTTATCTGAA +GAAGAAAATGAACAAATGAAAGCAAACTTAACTAAAATGTTATCTAGTTTACAATAAATGATAAGTGTGACTGGTAGAAA +TCAGTCACTTTGTCTTTAATATTATAGTTAGATATCTAATTGTTAGTAAGCTAATTATTGGAAAAGACAAGGAGTATTGA +ACAATGAAAGACGAACAATTATATTATTTTGAGAAATCGCCAGTATTTAAAGCGATGATGCATTTCTCATTGCCAATGAT +GATAGGGACTTTATTAAGCGTTATTTATGGCATATTAAATATTTACTTTATAGGATTTTTAGAAGATAGCCACATGATTT +CTGCTATCTCTCTAACACTGCCAGTATTTGCTATCTTAATGGGGTTAGGTAATTTATTTGGCGTTGGTGCAGGAACTTAT +ATTTCACGTTTATTAGGTGCGAAAGACTATAGTAAGAGTAAATTTGTAAGTAGTTTCTCTATTTATGGTGGTATTGCACT +AGGACTTATCGTGATTTTAGTTACTTTACCATTCAGTGATCAAATCGCAGCAATTTTAGGGGCGAGAGGTGAAACGTTAG +CTTTAACAAGTAATTATTTGAAAGTAATGTTTTTAAGTGCACCTTTTGTAATTTTGTTCTTCATATTAGAACAATTTGCA +CGTGCAATTGGGGCACCAATGGTTTCTATGATTGGTATGTTAGCTAGTGTAGGCTTAAATATTATTTTAGATCCAATTTT +AATTTTTGGTTTTGATTTAAACGTTGTTGGTGCAGCTTTGGGTACTGCAATCAGTAATGTTGCTGCTGCTCTGTTCTTTA +TCATTTATTTTATGAAAAATAGTGACGTTGTGTCAGTTAATATTAAACTTGCGAAACCTAATAAAGAAATGCTTTCTGAA +ATCTTTAAAATCGGTATTCCTGCATTTTTAATGAGTATCTTAATGGGATTCACAGGATTAGTTTTAAATTTATTTTTAGC +ACATTATGGAAACTTCGCGATTGCAAGTTATGGTATCTCATTTAGACTTGTGCAATTTCCAGAACTTATTATCATGGGAT +TATGTGAAGGTGTTGTACCACTAATTGCATATAACTTTATGGCAAATAAAGGCCGTATGAAAGACGTTATCAAAGCAGTT +ATCATGTCTATCGGCGTTATCTTTGTTGTATGTATGAGTGCTGTATTTACAATTGGACATCATATGGTCGGACTATTTAC +TACTGATCAAGCCATTGTTGAGATGGCGACATTTATTTTGAAAGTAACAATGGCATCATTATTATTAAATGGTATAGGTT +TCTTGTTTACTGGTATGCTTCAAGCGACTGGGCAAGGTCGTGGTGCTACAATTATGGCCATTTTACAAGGTGCAATTATC +ATTCCAGTATTATTTATTATGAATGCTTTGTTTGGACTAACAGGTGTCATTTGGTCATTATTAATTGCTGAGTCACTTTG +TGCTTTAGCAGCAATGTTAATCGTCTATTTATTACGTGATCGTTTGACAGTTGATACATCTGAATTAATAGAAGGTTAAA +TATTTCGTCCACTTCTGGCTGAGTATATTTCGGTCGGAAGTGTATTTTTCGAAAAAAATAAATATATGATACGATTATGA +AAAAATAAAGTGAGATTGGCATATGTGTAAATCTAAAATACTGTTGAAAAATATTTTTAGTGAAGAATCAGAAGTTAAAG +ATTTAACTGAAGAAAAATATAATCAAGATTACGAAGCATTAACATTTAGCTTTAAAGAGGAAACATATCAAAGTAGGTTA +GCTAAGAAAACACCGACTAAATCGGGATATTTCGTGACATGTTGGACAAAAGACGAAGATAATTATAATCGGCCATACAA +AATTGAAGAGTTTGCTGATTACCTGATTGTTGCTGTTATCGATGATGAATTAAATGGCTACTTTCTATTTCCTAGGGAAT +TATTGGTAGAAAAAGGTATCTTAGCTTCATCTAAGTATCAAGGGAAAATGGCTTTTAGAGTTTATCCTAAGTGGTGTAAT +CAATTGAATAAAACAGCAGGGCAAACACAAAAGTGGCAATGTAAATATTTTTTTGAATACTAATAAAAAGTCATATGGTT +GAACTTATAAGAGATGAATATATCATCTTTCGATAAGATAACCATATGACTTTATTTTTTATCATTTAATGATGAACGGT +TTCTTGTCCTACTTTATTCCAAGTGAGGATAAAGCTCAACATTGCAAACACACTAATTGCTGTTAATAAAATAAAACCGA +CATCCCATCCGAATTTATCAACTACAGCACCTAAGACGATGTTGGCCATTACAGCACCAAACAGATAACCAAATAATCCT +GTTAATCCAGCTGCTGTGCCAGCTGCTTTTTTAGGTACATAATCTAATGCTTGTAAACCAATTAACATAACTGGTCCATA +TATTAAGAAACCAATGGCAATTAATGAGACATTGTCTAACCAAGCATTGCCTGGAGGATTTAACCAATAAATTAATACAA +ATACTGTGACACCTAACATAAAGAAGAAACCTGCAGGTCCACGACGACCTTTGAATAATTTATCAGAAATGTAACCACAT +AATAATGTACCAGGAATTCCAGCCCATTCGTATAAGAAGTATGCCCAACCTGATGCTTTTAAGTCGAAATGTTTTTCTTC +ACTTAAGTAGACTGGCGCCCAATCAAGTACACCATAACGCACGAAATAAACAAATATATTTGCAAAGGCAATTGCCCATA +CCCATTTATTGTTCAGTACATATTTAAATAAAATTTCTTTTGTAGTTAATTCTGTTTCTAATGTTTTCTTATCGCTTGTA +GCAAAGTCATTTTTATAAATTTCGATTGGAGGTAAACCTTGAGATTGAGGTGTGTCTCTAATCAATACGTATGAAATTGC +GGCAATGATAAGTGCTAAGAGTGCAGGGTAAATGAATACACCTTCGAAACCTTTTAAATAACCAAAGTTGATAAATGCAG +TTGTTGTAATACCCCAAGCAGCAATAGGTGCCATAATACCTCCACCAACATTATGCGCAACGTTCCAAAGGGCAGTCTTA +CTTCCGCGTTCACTTACACTAAACCAGTGAACGAGAACACGGCCTGAAGGTGGCCAGCCCATACCTTGAAACCATCCATT +TAAGAATAATAGGACAAACATAATACCGATACCTGATGTAAAGAACGGTACAAATCCCATTAACAAATTGACGATAGCAG +TGAGTGCTAATCCAAGAACTAAGAATATCCGAGCATTGCTCCGATCACTTACAGTACCCATAAAGAACTTACTAAATCCA +TATGCGATGGAAACAGCAGAAAGTGCAAAACCTAGTTCCGCTTTTGTAAAACCTTGCTCTTGCAATGCCGGCATCGCTAA +CGAAAAGTTTTTACGTAATAAATAGTACCCAGCGTAACCGATGAAAATACCAAGAAATACTTGGAGACGTAATCGTTTAT +AGGTATCATCTATCTGATTTTCTGGCAAAGGCTTAATATGCTTTGCAGGTTTAAGAAAATTCATAAAATCCTCCTTAATA +TGTATTTATATGCATTTTGTGCGATAAACTTATATTAGACATGTATGAAAGCGTTGTCAATATGCTTTTGTGAAATCTTG +CATATTTAGTTATTGAAGTATTTTATAAACATTTAGAAAAAATAAATTGCTACACATAAATTTATAATTATGCAATTATT +ATGTCGTTATACATACTAGCTGTGTATCTACGAATGAGTACAAACATATTTTTATTTGCAAGGGGTAAATGGCATATAAC +TATCTTTTTTATGTAAGCTGGTATAAAATTTATACTAATAGGAGGGATAGTATGAATATAGTAGGGCATCATCACATATC +CATGTATACAAAAGATGCAAAACGTAATAAGGATTTTTACACAAATGTCCTTGGATTACGATTAGTTGAAAAGTCGGTTA +ATCAAGACAATCCTTCAATGTATCATTTGTTTTATGGGGACGAAGTAGGTACAGCCGGAACAATTTTAAGCTTTTTTGAA +ATTCCCAATGCGGGTCATAAGCAGCCAGGTACTGAAACGATTTATCGATTTTCATTATTAGTACCAAATCAAGCGGCACT +TCATTATTTTGAAAAACGTCTTGAGAATAATGGTATTAAGTCTGAACGTTTGTACTATCTTGGACAAGAAGGTGTTGTCT +TTAAAGATGAAGACGACTTAGAAATCATATTGCTTGTTAATGATAGTTTTGAAGTACCACATCAATGGCAACATAACGCT +TATAGTGAAATACCTCAAGCATATCAAATTTTAGGAATAGGGCCAGTCGAATTAAGAGTTAGAAATGCAGCGCGTACGGT +AGAATTTTTGGAAAATGTCTTAGGTTATCGCAAAAGAGATAATAAATCATTCGATGTGCTGACATTAGCACCACAAGGTT +TATATTCGGATTTTGTAGTTATTGAGCAACAGGGACAACGTGAAAGACCTGGACGAGGTTATATCCATCATATTGCAGTT +AATACACCACAAATGAGTGACTTAGATGCAATTTACAAGAAATTACAACAACAACCACAAAGTAATTCAGGTATAATTGA +TCGCTATTTCTTTAAATCATTATACTATCGCCATAATTCAATTATGTATGAATTTGCGACTGAAGCGCCTGGATTTACTA +TTGATACACCTGTTGAACAATTAGGAAGTCAATTGAACTTGCCTGACTTTTTAGAAGCAGAACGTGAACAAATTGAAAGT +AAGTTACACGAAATATAAAGGAGAATGTTTAATGGCCAAATTAGAAATGAATAAAAATACGCCTCTTGAGTTTGGTTTGT +ATTCCTTAGGTGATCATTTATTGAATCCATTGAAAGGTGAAAAAGTTAGTTATGAGCAACGTATTAATGAAATTATTGAA +GCAAGTAAATTAGCAGATGAAGCAGGTATTGATGTTTTTGCAGTTGGTGAAAGTCATCAGGAGCATTTTACAACACAGGC +ACATACGGTTGTGTTAGGTGCAATTGCCCAAGCGACAAAGCATATTAAAGTTTCAAGTTCTTCAACGATTATTAGTGCAA +CAGATCCTGTAAGAGTATTTGAAGACTTCGCGACATTAGATTTGATTTCTCATGGTAGAGCCGAAATTGTAGCTGGCAGA +GCATCAAGAACAGGTATTTTTGACTTGTTTGGCTATGATTTAAAAGACTATGATGAATTGTTTGAAGAAAAATTAGGTTT +ACTTTTAGAGTTAAATAAAACTGAGCGTATTACTTGGTCTGGAAAATATCGTCCAGAACTTAGAAATATGAAAATATTCC +CAAGACCAATCGATAATATATTGCCAATATGGCGTGCTGTTGGTGGTCCACCTGCAAGTGCTATTAAAGCGGGAAAACAA +GGTGTGCCAATGATGATTACAACCCTTGGTGGCCCAGCAATGAACTTTAAAGGTTCTATAGATGCTTATCGTCAAGCGGC +AACTGAAGCAGGTTTCGATGCTTCGCCTAAGTCTTTACCAGTAAGTACAGCGAGTCTGTTTTATACAGCTGAAACAACTC +AGGATGCTATGAGAGAATTTTATCCACATTTGAATACAGGGATGTCATTTATTCGTGGTGTTGGTTATCCGAAACAGCAA +TTTGCTAATTCGTCAGATTATCGAGAAGCGCTAATGGTTGGAAGCCCGCAACAAATTATTGAAAAGATATTGTATCAACA +CGAGTTGTATGGTCATCAACGTTTTATGGCACAGCTTGATTTTGGCGGTGTGCCATTTGAAAATGTTATGAAGAATATTG +AGTTAATTGGCAACGACATTATACCGGCGATTAAAAAGCATTTATCAAAATAGGAGGGGCGTCATCATGAATATTGTATT +ATTGTCAGGTTCCACAGTAGGTTCTAAAACGAGAATTGCTATGGATGATTTAAAAAATGAACTAGAAGTCATCAATGAGG +GACATCAAATAGAGTTGATGGATTTACGAGAACTTGAATTAGAATTTAGCGTTGGAAAGAATTATCTAGATACTACAGGA +GATGTATATAAATTAACGACGTCGTTAATGCAGGCTGATGTGATTTTTATTGGTTTTCCAATTTTTCAAGCTTCCATCCC +TGGTGCTTTGAAAAATGTGTTTGATCTACTTCCAGTCAATGCGTTTCGTGACAAGGTAATAGGACTTGTAGCGACAGCAG +GTTCTAGTAAACATTATTTAATTCCTGAAATGCATTTAAAACCAATATTGAGTTACATGAAAGCACATACGATGCAAACG +TATGTATTTATTGAAGAGAAAGATTTTTCAAATCAACAAATTGTCAATGATGATGTTGTATTTCGGTTAAAAGCGTTGGC +ACAATCCACAATGCGAACTGCCAAAGTACAACAACAAGTGTTTGAAGAAGAAAACAACCAATACGACTTTTAAAGTATAA +AAATAAGACGCTCGGCACACTAAATTTGTAAGTGTTTGAGCGTCTTTTCATATTAACTATATAGCCAATGAACGACGATA +AAGGCAAGTGATGACAAGCATATTGAGGTAATAATGATTGTCATAAGCGGTTTAAGTGCGCGATTTTTAAGATCTTTAAA +TGCAACATTTAACCCTAAAGCAACCATGGCCATTAATAAGCAAATTGTTGATACAGTATTTAAAATATTTAGCAATGCTG +ACGGAATAGTTACATATGTATTCACTAAGGCCATAATGACAAATCCAATTAAAAAGTATGGAATGCTTATTCGACCCTTG +CTAGATGATTCTGATGAACGGAAACGCATAATTAAAATAAGTACGATGGTTAATGGAATCAGTAAGAATACTCTACCAAG +TTTACCAAGAAGTGCAATTTTAAGTGCATCACTACCACCAAAGCCACCAGCTAAGACAACGTGTGCAATTTCATGAAGAC +TAACACCAGACCAAGCGCCATAAACATTTGTCGTCATTGAAAAGATAGCGTAGATAGCTGTATATATAAGTGAAAATATC +GTACCAATCAATGCGATGATACCGATACTAATAGCTGTATCCTTTTCACGTGATTTGAATATTGGAGCGACTGCGGCAAT +AGCAGCAGCACCACAAACGCCTGTGCCGACACCTAGTAATAATGCGATGTTTTTGTCACCATGCAACAGTTTGTTGACAA +AGAGCATCATTACAATACTGAAAATAACGACACCTACATCGATGGCTAATAGTTTACTACCTTGACCGATAATATCGAAT +ATATTGAGTTTAAGTCCATATAGGATGATTGCAAATCTTAATAAATATTTAGATGAAAACGTAATACCTGAGCTATATTG +TTCAGGATATCCTCTAAAGTGACGATATAGAATAGCGATTAATATCGCGATAGTTAATGCGCCAACCTTATCTAGGATTG +GCAATTTAGCTGCTAAAAAGCTAAATAATGCGACTATAAATGTTAGTGATAGCCCAATCATAAAATGCTTATTTTTCAAT +GATGCCATGAGCAGTGCCTCCTTTAATAGCATTTTAGCACTGTTTTGTCGTATTTTTAAATATAAATTTGGAATGAATAA +TAAAGTAGTGATTAAATTAAGTTGTGTGATAGGAAACTTGGACATCAATCAAAGTAATAGGCACTACAACGCTTATTGGC +GGGGCCCCAACAAAGAAGCTGACGAAAAGTCAGCTTGCAATAATGTGCAAGTTGGGGATGGGCCCCAACATAGAGAAATT +GGGTCCGTAATTTCTACAGACAATGCAAGTTGGCGGGGCCCCAACATAGAGAATTTCGAAAAGAAATTCTACAAGCAATG +CAAGTTGGGGAAGGACAACAAATTTAAGATACAATGCGTAACATTAATATGTTATTATAATGATAATTTACAGAATTATA +TGAAAAATGAATGAGGATGTGATGGTATGTTTGGAATGAAAGTGAATGAACAAATAACATTAAAAATTTTAGAAGCTCAT +GACACAGAAGCGCTTTTCAATTTAGTCAATCGTTCAAGAAATTCACTTAGGGAATGGTTACCTTGGGTAGATGCAACTGA +GCAACCATCAGATACGCGTGCATTTATTAAAAGAGGACTTTTGCAATTTGCTGATGGTAATGGATTTCAGTGTGGCATTT +GGTATGAAGGAACGCTAGTTGGTGTCATCGGTTTACATGAAATTAATCACATGCACAGAAAAACTTCATTAGGGTACTAT +TTAGATAAAGAATTTGAGGGTCATGGGATTATGACACAAGCAGTTGAGGCATTGATAAAGTATTGTTTCGAAGAGCTTGA +CTTAAACCGAATTGAGATTAGTGCCGCAGTTAATAATGAAAAAAGCCGGGCTATTCCTGAAAGGCTGGGATTTACTAGAG +AAGGTATGTTACGTGACAATGAATTACTAAATGGTATTTATTCATCGAGTTACATCTATAGTTTATTAAAATCAGAATAC +GACCAAAAATGACAAATTAGACTTACAAAAGAGTGATGACATTTAAAATGGCAGCGCTCTTTTATTTAATTTTTGAAAAT +AAAAGGTTGTTGACAGTATTATTTTATAACAATATAATGATTTTGATAATTATTATCAACTAGATGATGTTTATGGGAGG +ATGCTTTAAAACAGCCGTTTTAAGTGTAATGTATTATTTTAGCGTGTAGGGAATGCGAAAATAATATTTATAAGAACACA +TCTATGGGGATAATAGAATTTCTATAATGAGGTGTCAAAATGAAAAAGTTAACAACGCTATTATTAGCATCAACGTTATT +AATTGCTGCATGTGGGAACGACGATAGTAAGAAGGATGATTCAAAGACATCGAAAAAAGATGATGGTGTTAAAGCAGAAT +TAAAACAAGCAACAAAAGCATATGATAAATATACTGATGAACAGTTAAATGAATTTTTAAAAGGTACAGAAAAATTTGTT +AAAGCGATTGAAAATAATGATATGGCCCAAGCAAAAGCGTTATATCCAAAAGTTCGTATGTATTATGAACGCTCTGAACC +AGTTGCAGAAGCATTTGGAGATTTAGATCCTAAAATTGATGCACGTCTTGCAGATATGAAAGAAGAGAAAAAGGAAAAAG +AATGGTCAGGATATCATAAGATTGAAAAAGCATTATACGAAGATAAGAAAATTGATGATGTGACTAAAAAAGATGCACAA +CAATTATTGAAAGATGCAAAAGAATTGCATGCCAAAGCTGATACATTAGATATCACACCAAAATTAATGTTACAAGGTTC +TGTTGACCTATTAAATGAAGTTGCAACTTCTAAAATCACAGGTGAAGAAGAAATTTATTCACATACAGATTTATATGATT +TTAAAGCGAACGTTGAAGGCGCACAAAAAATTTATGACTTATTTAAACCTATTTTAGAGAAAAAAGATAAAAAATTAAGT +GATGATATCCAAATGAACTTCGATAAAGTGAATCAATTATTGGATAAATATAAAGATAACAACGGCGGTTATGAGTCATT +TGAAAAAGTATCGAAGAAAGACCGTAAAGCATTTGCGGATGCTGTTAATGCATTAGGAGAGCCACTAAGTAAAATGGCTG +TGATTACTGAATGACAAATTATGAACAAGTTAACGATAGTACGCAATTTTCAAGACGTACATTTTTGAAAATGTTAGGTA +TTGGCGGTGCCGGTGTTGCAATTGGCGCAAGTGGTGTTGGTAGCATGTGGTCTTTCAAATCAATGTTCAATACACCAGAA +GATCCGGAAAAAGATGCGTATGAATTTTATGGTAAAGTGCAACCAGGCATTACCACACCCACGCAAAAAACATGCAATTT +CGTTGCGTTAGATTTGAAGTCAAAAGATAGAGATGCAATTAAGGCAATGTTTAAAAAGTGGACGGTTATGGCTGATCGTA +TGATGGATGGTGATACAGTTGGCAAGCCGAGTAACAATCCTTTAATGCCACCAGTAGATACCGGTGAATCGATAGGATTA +GGTGCAAGCAAGTTAACGATTACCTTTGGGATTAGTAAGTCTTTGATGAAGAAAATTGGGTTATCTAGTAAAATTCCCGA +TGCCTTTAAAGATTTACCGCATTTTCCGAATGATCAGTTAATAGACGATTACAGCGATGGTGATATTATGATTCAAGCAT +GCTCAAATGATTCGCAAGTATCCTTTCATGCGGTTCATAATTTAGTTCGTCCATTTCGAGATATTGTTAAGGTACGTTGG +GCGCAATCTGGTTTTATCTCTGCTAAAGGTAAGGAAACACCTAGAAATTTAATGGCATTTAAAGATGGAACAATTAATCC +TAGGAAGAATAATCAACTTAAAGATTATGTGTTTATTGATGACGGATGGGCGAAACATGGAACTTATTGCGTTGTCAGAC +GTATTCAAATACACATTGAAACGTGGGATCGTACTGCGCTGGAAGAACAAGAGGCTACATTTGGTCGGAAACGACATAGT +GGTGCACCGTTAACAGGTGGGAAAGAGTTTGATGAAATTGACTTAAAAGCGAAAGATAGTCATGGCGAGTATATTATTGA +TAAAGATGCCCATACGAGGCTAGCGAAAGAAGCAAATACGTCAATTTTACGTAGAGCCTTTAATTATGTGGATGGTACGG +ATGACCGCACAGGTAACTTCGAAACAGGCTTACTTTTTATTGCTTTTCAAAAAGCGACAAAACAATTTATCGATATACAA +AATAATTTAGGTAGTAATGATAAATTAAATGAATATATTACACATAGAGGTTCTGCTTCATTTTTAGTATTACCAGGTGT +TAGTAAGGGAGGATACCTTGGTGAAACATTATTTGACTAAATTTGTAGCAATGCTAATAACTGCTGCTATGGTGTGTAGC +TTTGGGTTACTGAAAAGTCAGGCAGCAGAACAACAAAGTATTAGTGATGTATATAGTGTGATAACGGATGCGAAATCTGC +ACTTTCTAATAATTCGATATCGAATGACAATAAGCAGAAAGCAATTGAGCAAGTGGTAAGTGCAGTTAAGAAATTATCGC +TTGAAGATAATAGTGAAAGTAATGCTGTCAAATCAGATGTGAGAAAGCTTGAAGATGCAAAAGCGAATGATAATCAAAAA +GATACACTTTCGCAATTAACGAAGTCATTAATTGCTTATGAAGAGAAATTGGCTAGTAAAGATGCGGGTTCTAAAATTAA +ACTATTGCAACAGCAAGTCGATGCTAAAGATGCTGCGATGACAAAAGCGATTAAAGATAAAAATAAAGCGGAATTAGAAT +CTTTGAACAATAGTTTGAATCAGATTTGGACAAGTAATGAAACAGTGATTCGCAATTATGACGCAAATCAATATGGACAA +ATTGAAGTCGCATTATTACAACTTAGAATTGCAATTCATAAGTCACCATTAGATACGGCAAAAGTGTCACATGCTTGGAC +AACTTTTAAATCAAATATTGATCATGTCGATAAAAAAAGTAATACGTCTGCAAATGATCAATACCATGTATCACAATTAA +ATGATGCGTTAGAGAAGGCGATTAAAGCTATCGACGACAATCAATTGTCGGATGCTGATGCTGCGCTTACACATTTTATA +GAAACTTGGCCGTATGTTGAAGGTCAAATTCAAACTAAAGACGGTGCTTTGTATACGAAAATTGAAGATAAAATACCATA +TTATCAAAGTGTATTAGACGAACATAATAAAGCACATGTGAAAGATGGTTTAGTAGATTTAAATAACCAAATTAAAGAGG +TTGTTGGCCATAGTTATAGCTTCGTCGATGTGATGATTATCTTTTTACGTGAAGGGCTAGAAGTGTTGTTAATTGTAATG +ACATTGACTACCATGACGCGTAATGTAAAAGATAAGAAAGGGACTGCAAGTGTGATTGGTGGTGCAATTGCCGGACTTGT +ACTGAGTATTATCTTAGCAATTACGTTTGTAGAAACTTTAGGGAATAGTGGCATTCTTCGTGAAAGTATGGAAGCGGGAT +TAGGTATCGTTGCGGTCATATTAATGTTTATCGTTGGTGTTTGGATGCACAAACGTTCAAATGCAAAACGTTGGAATGAC +ATGATTAAAAATATGTATGCTAATGCGATTAGTAATGGTAATTTGGTATTGTTAGCGACGATTGGTTTAATATCTGTGTT +GCGTGAAGGTGTCGAGGTTATCATTTTCTATATGGGGATGATAGGTGAGCTAGCGACCAAAGATTTTATTATTGGTATTG +CTTTAGCTATCGTTATTTTAATCATCTTTGCATTATTATTTAGATTTATAGTTAAATTAATACCTATTTTCTATATATTT +AGAGTGTTGTCGATCTTTATTTTTATTATGGGATTCAAAATGCTTGGCGTAAGTATTCAAAAGTTACAATTATTAGGTGC +GATGCCAAGACATGTTATTGAAGGATTCCCAACGATTAACTGGTTGGGCTTTTATCCAAGTTATGAACCATTGATAGCAC +AAGGTGCTTATATTATGGTAGTTGCTATCTTAATCTTTAAATTTAAAAAATAAAAAACAGGCCGAGTGCCTGTTTTTTTT +GTTGCTATATTGGAAATATTCGGTATTGCAGTATAACGATAATCACAGCATTGATTCGTATAAGGTTAATGTGTTGGCGG +TTTGCCTCGGCATGTGAACTTAACGATGAACATACTGAACTCAAAGAGCAATATGAGTGGCAATGTGAGTAATATATTTA +ATGTTAAATCGGGTGGTGCAATGATACTTGCTAATACAAAGCAAGCGAAATAAATATATTTACGATAATGTTTCAATGAT +GTGGTATCTATAAGACCGAATTTTGCAAGACCCATAAATAATATTGGTAATTGAAATAGAAGACCAAATGTGAATAACCA +ACGTATGAGTTCAATCAAATATGCTTTAAAGCCAATGACAGGCGAAATGTTTAAAGTTAATGATAATTTTAACGCGAATT +GAATGATCATTGGAAAGCCAACATAAAATGCAAAAGCGACGCCAGCACAGAATAATAACACGCTGAAAAAACTATATTTA +TAAATAAATTGACGTTCATTATTATGTAATCCAGGTGCAATGAATGCCCACAATTGATAAAACATGACTGGTGAAATGAA +ACAAAACGCGATGAAAAATATAATCATCACGTATATTTGTATCATTTCTGTGAATGAAAATGCATGTAAGGACACATGTG +CACGGGTAATATACGTTATGAATGGTGTCATCCACCAAAATGATGAAACATATACGACGATGACCGTAATGACGAACGAC +AATAAAATTTTTACTAACCGATGGCGTAGTTCGCTAAAGTGAACCAGTAAGGTGTGGTCAGTGCTATTGCTCTCGCTGTT +GTTTCGATTCCTTACTGGGTGTATCGTGAGACTCTTTATCTAAATCTTCTGTTGCAGATTTAAATTCTTTTAAAGTAGAA +CCGATGGCACGGCCAAATTGTGGTAATTTTTTCGGACCAAAAATAATTAAAGCGATAATGCTAATGACGACAAGACTTGT +TGGGCCTGTGATGCCTAAAATAAAAGTGTTAGTTATCATGATAATCAACCTCACTCATAAGTAGTATATAGTATTTACTT +TATACCGAACGATAATGATTATCAATAAAATTTTATGAAATAATTAAAATGTTGCTATAAATATAGTTTATTGAGGGATT +AATTCTGAAATAACAGCTTGGTCTTGCAAGTCGATATAAGTACAAGTGCCCGCTTGAATATCGATATGCCATTTATAAAT +GCCAGCTTCAGCCATTTCATCACAAAATGTTTCAAAATCTGTTTGCCCTTGTTGATGTCTAGTTAAGACGTCTTGAACTA +TTGTTTTGTTTGATTTTTGAGCAACAGGATGATTACTTTTTACAGATGACGTAACGATATCATCTTCTGATTGATGTACG +TATGTTGCAGTGCCATCTTGAATGTTGACGATATTGTAAGTCATCCCCATATCTTTAAAAGCTTTGAATAGTTTTGGAAA +GTCAACACCAGTAAATTGTTGATGTGCTTGTTGAATTGCAGATAATGTAAATGCCATAATAGTCTCCTTTGAATGGTTTG +ATTTAGTGAAAATGCGTGCATCATACTAAAATTAGAATGCTGTTAATTATGCAGGAAGGTGTAACGAAAGTAAACAATTA +TGACTTGTAGGCGTTGAGGTGTTTGTTTCAAAATGTCATACATAGGTGGATAATCATATTTTTTGGTGTAGATTTGACAA +ATATAGTTGTCAAAAAGACGCATATCATTTACAGTGTAATTAAGGAGTTGATGCGATGTGCGTAATCGATTGAAAGAATT +ACGAGCACGAGATGGCTTAAACCAAACGCAACTTGCTAAACAAGCGGGCGTTTCAAGACAAACCATATCGCTAATTGAGC +GAAACAATTTTATGCCATCAGTATTAACGGCAATAAAAATTGCTCGCATTTTCAATGAAACGGTGGAAACTGTTTTTATT +ATTGAGGAGGATGAGGCATGAAAATACTAAGATATATCGGATATCTTTTACTGGGTGGACTTGTAGGGGGTATCATAGGT +GGAATTTTAGGTAATTTTGATGGATTTGGTATTGAGAACTTGACGTTTGCGACATATAACAATGTCGTTGTAATATCGAT +TGTTGCGACGATTATTATCATATTGGTAGAAGCCATTGTTTTGATGAATCAAAGACGTGCATTGAAGTATAAGCAACTTG +TAGATAAAGAGGTAGATATCGATGCAACAGATCAATATGAGTTGCTTGCGAATCGTTATGTTTTAAATGGAAGTATATTA +AGTATTCTGCAGACAGTTATTGCTTTTTTAGTATTGCTTATTTTTGTGGTAGGGCACGCTGCAGCAAATGCAATACTATT +CTTTTTAATACCATTTTTTGCTAGTGCTATTTTCAATACACAATTTACACTGTTTAATAGAAGGTTTGATGACAGAATAC +CAAAAATTGCAGATAAGAATTACACTGAAAAGCGATTGGAAATATTGGATGAAGGTGAACGCCATATAGAATTAATTGCA +TTATTTAAAACATATGCGATCAACTTATCAATATTGATACTAGCCATTGTAGTAATTGGGCTTTATTCAATTACTACTGG +AATTAATCAAAGCTTTAGTTTGCTACTTATCATTGCTATTTTCATATATAACGCCTTTAGTTATTTATTGAAGAGAAGAC +GTTTTTATTAAAATTAAACAAAGAGGAGCATGGATATGACAACATTGTTAAACGTAGATAGTGTGAACAAACAATACAAA +GATTCGGATTTTAAATTGCAAGATGCATCTTTAACGATTTCTACTAATGAGACAGTTGGATTAATTGGGAAAAATGGCTC +AGGTAAATCGACATTAATTAATATTCTAGTAGGCAATCGACATAAAGATAACGGTAGTATCACATTTTTTGGAGAAGAAC +ATACCGTGGATGATGTCGAATATAAAGAACACATAGGTGTAGTGTTTGATGATTTGAGAGTGCCTAATAAATTGACTATT +AAAGATATTGATAAAGTATTTCAATCTATTTATATGACTTGGAATAGTCAAAAATTCTTTGATTTAATCAAATATTTCGA +GTTACCACTACAAACTAAAATTAAAACTTTTTCAAGAGGGATGCGAATGAAGATAGCTTTAACGATTGCGCTTTCTCATG +ATGTGAAGTTATTAATCTTAGATGAAGCAACTGCAGGTATGGATGTTTCTGGAAGAGAAGAAGTAATGGAAATACTAGAA +GATTTTGTCGCACAAGGTGGAGGCATCTTAATATCATCGCATATTTCTGAAGATATAGAACATTTAGCTGATAAATTAGT +GTTTATGAAAGATGGACGAATGATTTTAACTGAACAGAAAGATATACTTTTAGCACAATATGGAATTGTTACGACAGAAG +ATAAAGATGTTGAAATTCCTAAGCATTTAATCATTGCTTCTAGATTGTCAAAGGGGAAATATCAAATTTTAGTTAAAGAT +TATGCAGAAATTGAAAATGCAGAGCCTTTAAAACACATTGATGACGCTACAAAAATCATAATGCGAGGTGAAGTATAATG +AAAGGTATGTTCCTAAGTAGTTTTTATGCAACGAGAAAGCAAACATATATTTATTTTATAGTCGCTATTATAGCTGCAGG +ATACTTTGCAGTATTTAATCCGTTGATGAGTTCGGCAATGGCTGGGGTTATGTTAATCACACCTATTACTGATAATATTA +AACATGAAAAAGACTCAAGATGGATGTATTATGTATCTACTTTACCGGTTAAACGTAGTGATTATATTAAGTCATACTTT +GCCTTTTATTTAATCTTATTCGGTGCAAGTTTAATGATTGGATTAGTTGTGACTACGATTGTGACCCAAAGTGTGATGAT +TGGTATTATGTCAGGTTTAATGAGTTTTGGTATCATAGGGGCATACTCTATCATTTTCCCATTGACATTTAAATTTGGCG +CTGAAAACTCCAATGTCATTATGATATGTGCATCTATACTACTACTTATTTCTTTCGTGGTCTTTTTCTTTATATATGGT +ATGGTTAGTGGTGCATCTGCATTAGAATTTGAAAAAATTAGCACTGAAGGATGGCTAGTTGTCATAGCATATGCGGTCAT +TGGTATAGTTATAACGAGCGTTTCTTATATGTTGTCTATTAAAATTTTTAACAAACAAGAACTATAATTGTCGATTTTGG +TGATGATAGTATTTGAAATAATATTTAGAGCAGGTGATAAATCTTTACGATTGTCATCTGTTCTTTTTGGTGTGGAATGA +AATGTGGGGGATAAGTATAGGTGACATATCTATATTGATTTATTTGTTTTGAGGTGGTTATGTTGTGTGGGAATTATTTC +CTTTTAGATAGCGGGGATTAGAGGATATATGTTATTTATAAGTATCATTTGATGATTGTATAGGCTAACGATTTCCTCGG +AAATATTTAAAAACCTCGATCATGTAGCATAACTGAAGTTTGTCACAAAAGTATAATGTGAAGTTCGACACTTTTGGATT +CAGTTCAAATACTTTGACCGAGGTAAATACTATTTATTCATGTTTATTACGTAGACGTTGATTTTTAAAATAAATCAGCA +TGATGCAGAAGGTTGCAACTGATACTGAGATAATCATTACATGGTCGTGACCTTTAAATAAAAGGCTGACAATATAAGAC +ATAACGAGTATACCTAGTGAATATGAAATATACTTCGCGTTTGTCAGTTCATTATGGAAATAAGGCGTGATTAACCATAA +TCCAATATAGAATATTAAAACACTGATATACATCATATTAATTTCAAACAAGTCATTTAGTTTATTGTTATTACTAAAAA +CAATTGCAGCATTAATCACACCTAAAGCGATATTGATTAATAGATGCGTATACGATAAACGGAAACCGATAGATGTTAAT +TTATGATTAATATAATTTTCAGTAATGATCCAATATACACCGAAAAGACTAATTAAAATCATAAATTGGAATATATAAAT +GTAACTAAAATGATCAATGCTAAATGATGACGAAGCTAAACCAACCAGTACCTCGCCAAAGATAATAATTGTTAGTAACG +AAAAACGTTCTACTAAATGCATCATATTAACAGGTGATAATACAAGATATTTCTGAAATGGAATAAGTCCTGTCGCTGCA +ATGAATACGCCTAAAAATCCAGGGATGTAATGGATACTTTGTGGTAGTACTAATGATAGAAATGATAAAAATGAAATCAC +AAAGGCTACGCTCGCAAAAGCTTGACATGTACGCTTATCGCCATAATCTAACCCTGTACGTATATGTAATAAATACTGTA +ATCCGATACTTAAATACATAATTGCCACGCATAAGAAGAATGGGAAGAATGTCTTTTCAAAGTCCGGATATAGGCTGTTA +GATAGGAAGACCATGATGAACATATTAAACATCATAAACGAGACGTCTTTGAATGTAACTTGACCAAATCGATTTGTAAA +AAATGTTTGATGAGACCACATTAACCATAAGAACAAACTCATGACGATGTATTTGAAAAATAAATCAGCTGAAATGGAAC +CGTTTTGTGTTGTTAAAATCACATGTGCAATTTTTTGAATGGCATAGACGAAAATTAAATCAAAGAACAACTCATGGAAT +CCTGCACGCTTTTCAGCTAAATGTTTTGGTGTTAATGCATTAACCATAAAATTTTAACTCCTTTAAGATGTGTAATTAAT +TTACTAAGTATACTATTTATTTTTTCTAGTGAATAGGGGCAGATTTGGCGATGAAGTGGAAGGAGAGGTGACTGCAAGGT +AATTGCGGAATTAACAATCATCAGCGATTTAATATTTGACTGGAGACGTCATGGTAATAAAAAATTGATGAGAAATTGAT +GGTGAAACCAGCTGTGAATAGCGATGCAATGATAGATAGAATTTAATTAGAGTCATTACGCGAAATGATTAATGATAATT +TGTGGTAAATCAAAGCATAATTTTGTACTATAGATGAGGATGATAGAGCATATTTAAGAGGGTGAAATGTTAAAGTGAAA +CCGTTTACGTTTCCGATTGCCCAAACAAATTACATCATTGTATAATATGATTTGTTAAATGCATAACAAGAATGAAAATG +TAACATACGTAGCAATTGGTTTCATAAATTGGATGTTAGTGGCGTATTGGTTCATTAGACGTATTAGTAATAAAATTGTA +TATATCATAAGGAGATGAATATGACATGACGAGAGTCGTATTAGCAGCAGCATACAGGACACCTATTGGCGTTTTTGGAG +GTGCGTTTAAAGACGTGCCAGCCTATGATTTAGGTGCGACTTTAATAGAACATATTATTAAAGAGACGGGTTTGAATCCA +AGTGAGATTGATGAAGTTATCATCGGTAACGTACTACAAGCAGGACAAGGACAAAATCCAGCACGAATTGCTGCTATGAA +AGGTGGCTTGCCAGAAACAGTACCTGCATTTACGGTGAATAAAGTATGTGGTTCTGGGTTAAAGTCGATTCAATTAGCAT +ATCAATCTATTGTGACTGGTGAAAATGACATCGTGCTAGCTGGCGGTATGGAGAATATGTCTCAATCACCAATGCTTGTC +AACAACAGTCGCTTTGGTTTTAAAATGGGACATCAATCAATGGTTGATAGCATGGTATATGATGGTTTAACAGATGTATT +TAATCAATATCATATGGGTATTACTGCTGAAAATTTAGTAGAGCAATATGGTATTTCAAGAGAAGAACAAGATACATTTG +CTGTAAACTCACAACAAAAAGCAGTACGTGCACAGCAAAATGGTGAATTTGATAGTGAAATAGTTCCAGTATCGATTCCT +CAACGTAAAGGTGAACCAATCGTAGTCACTAAGGATGAAGGTGTACGTGAAAATGTATCAGTCGAAAAATTAAGTCGATT +AAGACCAGCTTTCAAAAAAGACGGTACAGTTACAGCAGGTAATGCATCAGGAATCAATGATGGTGCTGCGATGATGTTAG +TCATGTCAGAAGACAAAGCTAAAGAATTAAATATCGAACCATTGGCAGTGCTTGATGGCTTTGGAAGTCATGGTGTAGAT +CCTTCTATTATGGGTATTGCACCAGTTGGCGCTGTAGAAAAGGCTTTGAAACGTAGTAAAAAAGAATTAAGCGATATTGA +TGTATTTGAATTAAATGAAGCATTTGCAGCACAATCATTAGCTGTTGATCGTGAATTAAAATTACCTCCTGAAAAGGTGA +ATGTTAAAGGTGGCGCTATTGCATTAGGACATCCTATTGGTGCATCTGGTGCTAGAGTATTAGTGACATTATTGCATCAA +CTGAATGATGAAGTTGAAACTGGTTTAACATCATTGTGTATTGGTGGCGGTCAAGCTATCGCTGCAGTTGTATCAAAGTA +TAAATAATAAGAAAACAGGTTATCACAACAGTATTAATTACATGTTGGCATAACCTGTTTTTATTTGTTTATGGATTTAT +TGGGTAATATTAGTCATTTGATGGTTTAATTGCAAATGCTCTAACAGGGAACCCAGGTGCATCTTTTGGTTTAGGGCTGA +TAGCGTAAATGATGGCGCCACGAGTTGGTAATTGATCTAAATTAGTTAATAACTCGACTTGGTATTTATCCTGACCAAGA +ATATAACGTTCGCCAACTAAATCACCATTTTTTACAACGTCCACAGATGCATCGGTATCGAATGTTTCATGACCAACAGC +TTCAACACGACGTTCTTCAATTAAGTACTTCAAAGCATCTAATCCCCAACCCGGTGCATGTTGTTGTCCGTTCGCATCTT +TGTTTTCAAACTTTTCAATATTAGGCCAACGTTTTGACCAATCGGTACGAAGTGCAACAAAAGTGCCAGGTTCAATAGTA +CCATGCTCTTTTTCCCATGCTTCTATATGCGCACGTGTTACGATGAAATCATTGTTGTTCGCTACTTCTGTTGAAAAGTC +TAATACAATTAACGGCAATACCAATTCTTTTAAATCAATGTCTTCTAAATAACGTTTATTCTCGACAAAGTGAATTGGTG +CATCAATGTGAGTACCATATTGCGTTACAATATTCCAACGTTGCACATAGAAACCATGATCTTTAACCGTGAATAAAGTT +GAAACTTCGCCTTTTTCAAACTCACTAAAACGTGGTATTTCCGGATCAAATGTATGCGTTAAATCAACCCAAGTTGCTTG +TTTTAAAGTATTTAATTGTTGCCATAAAGGATATTGTGTCATAAAATCACCCGTTTTTAGTTTATTATATGATAAATGCT +GCGATTATTCTTGGCGTTTAGCTTTAACAGCATTCACAAGCACAGTCAATGCATCTTTAACTTCTTCTTCTTTTCGCGTT +TTTAAACCACAGTCAGGGTTTACCCAGAATAATGAGCGGTCGATTTGTTGTAGTGAACGATTGATTGCTGTAGTAATTTC +TTCTTTTGTTGGAATACGTGGACTATGAATATCATATACACCTAGACCAATACCTAAATCATAATTAATATCTTCAAAGT +CTTTAATTAAATCACCATGGCTACGAGATGTTTCAATTGAAATAACATCAGCATCTAAGTCATGAATAGCATGAATGATT +TGACCGAATTGAGAATAACACATATGTGTATGGATTTGAGTTTCATCACGAACTGAAGACGTTGCAAGTTTAAATGATAA +AACAGCATCTTTAAGATATTGTTCGTGATATTCAGAGCGTAATGGTAAGCCTTCACGTAATGCAGGTTCGTCAACTTGGA +TAACTTTGATTCCTGCAGCTTCAAGTGCTAATACTTCTTCGTTGATTGCTAAAGCAATTTGATCTTGAACGACTTTACGT +GGTAAATCAACACGTTCAAATGACCAGTTTAGAATTGTTACAGGTCCAGTTAACATACCTTTAACTGGTTTATCTGTTAA +GCTTTGTGCATAAACTGTTTCATCAACAGTTAAAGGCGCTGTCCATTTTACATCACCATAAATGATTGGTGGTTTTACGG +CACGTGAACCATATGATTGCACCCAACCGAATTTAGTTACTAAGAAACCTTGTAATTTTTCTCCGAAGAATTCAACCATG +TCATTACGTTCAAATTCACCGTGAACTAATACATCTAAGCCAATGTCTTCTTGAATTTTAATCCATCGAGCAATTTCATT +TTTTAAGAATGTTTCATATGCTTCGTCTGTAATGCGTTTGTTCTTCCAATCTGCACGGTATTTTCGAACTTCTCGGCTTT +GTGGGAATGATCCAATAGTTGTTGTTGGTAAATCCGGTAAGTTCAAACGTTTTTGTTGTTGTTCAATACGTTGCGCGAAT +GGTGATTGTCTTGAAGTACGCACGCTTTCGAAATCATAATCTAAGTTTTTGAATGATTGATTTTGGAAACGCTCATAACG +TGCTTTTAATTTATCATATTTAACACTATCGTTTTGATTAAATAGGCGACGCAATGCATCTAATTCGTCTAATTTTTCAG +TTGCAAAGCTTAAGCCTTCGCCAACACTTGTATCTAATGTTTCATCATCTAAAGATACTGGAACATGTAATAATGAAGAT +GATGGTTGAATGACAAGTTCATTAGTGTGTGCTAACAATTTATCGATTAAGACTTTTTTAGCTTCAATGTCACTTGCCCA +TACATTACGACCATCAATAATTCCAGCGTATAATGTTTTTGATTTATCAAAATCTCCAGCTTCAATTTGTTTAAGGTTAT +AGCCATTATCATGGACAAAGTCTAAACCTATACCACCAACAGGTAAAGAACTTAAGAATTTAAGATGTGCACGTTCAAAG +TATGTTTGAATGACTAATTTTTTAGCAACACCAGCTTTTTCGAAATAGTCATAAGCTTCACGTGTAATATTTTCATAGCT +TTCGCTGTCGTCTGTAACTAAGATTGGCTCATCAACTTGAATGTACTCAGCACCTGCATCAATTAATGATTCAAACACTT +CTTTATAAAGTGGTAATAACGTTTTAACTTTTTCTTCAAAAGTTTGGTGACCGCCTTTTGATAATTTAACAAAAGTAATC +GGACCAACAATGACAGGGTGAGCGTTAACGTTTAAAGATTGGGCATATTTAAAGCGATCTAATAATACATTGCGACTCAC +TTTAGGCTCAACATTGTCCCATTCAGGTACGATGTAATGATAGTTAGTGTTAAACCATTTTATAAGTGCACTTGCAACAT +GGTCTTTATTACCGCGAGCAATATCAAATAATAAATCATCATCAATAGTTCTTCCTTGGAAACGTTCAGGGATGATGTTG +AATAATAATGACGTATCTAATATATGGTCATATAAAGAGAAATCACCAACTGGGATGCTATCTAAGTGATAGTACTTTTG +TAATAATAAATTTTCTTTATGTAGATCAGTTAATGTTTGATCTAATTCTTCTTTAGAAATCTTCTTTGCCCAATAACTTT +CGATGGCTTTTTTCCATTCTCTTTTTCTACCTAATCTTGGGAATCCTAAGTTTGATGTTTTAATTGTTGTCATAATATTG +CCTCCTTGTGAGCAGTAATAGATTTTGAGTATGCTGCAAGTTCTAATGAATCTTCGACATTTTGAAACGGTGTGATAATG +TATAAACCATTAAAATATTCATGAACAGTATCGATTAAATCCTTTGAAAGCTTAAGACTTAGTTCTCGTGTTTTGGCTTT +ATCATCTTTAACTGCTTCAAATTGTTGTAAAATTTCATCTGACATCTTGATTCCTGGCACTTCATTATGCAAAAAGAGTG +CGTTTTTGTAACTTGCGATAGGCATAATGCCTATGAAAAATGGTTTGTTCAAGTGCTTAGTGGCATGGTAAATTTCAATG +ATTTTCTCTTTGCTGTACACGGGTTGTGTTATAAAATAAGACATTCCGCTTTCTATCTTTTTCTCTAATCTTTTGACGGC +ACCATATAATTTACGAACATTAGGGTTAAAGGCGCCAGCGATGTTGAAGTGTGTACGTTTCTTCAGCGCATCACCGTCAG +TGTTAATACCTTGATTAAATCTTAGAGCGAGTTCAGTTAATCCTTTAGAATTAACATCATAGACATTGGTTGCACCTGGT +AAGTGACCAACTTTTGAAGGATCACCAGTTATGGCTAATATTTCGTTAACGCCAATGAGCGATAATCCAAGTAAATGGGA +CTGCAAGCCGATTAAGTTTCGGTCTCGACATGTAATATGTACGAGTGGTTCAATATTGTAATATTGCTTAATTAAGCTAG +CAGCAGCAATATTGCTAATTCTGACAGTTGCCAATGAATTATCTGCGAGTGTTACCGCATCTACATTAGCTTTATCAAGT +TTAGCGATATTTTCAAAAAATCTATCCGTGTCTAAATGTTTCGGTGTATCCAATTCGATAATAACGGTTGGACGTTCTTG +AACCTTAGATGTTAATGATTGTCTAACTTTATTTTGAGATGGATTGAAAAGTGCTTTCGTTGGTATCGGAATCACTTTTT +TGTCATTAACAGGTTTAAGTGTCTGAATAGATTCTTTAATAAATTTGATGTGCTCTGGCGTTGTACCACAGCAACCACCA +ATTAAACGAACACCTTCGCGAATTAGATTTTGAGCAACTTGACCGAAATATTGTGCATTGTCACTATACTTAAATTCACT +ATTTTCAATATCTAATAAGCTGGCATTTGGATAACAAGATAAGAATGCGTGCTCTGGTAATTCAATATGTGTGAAAGACT +CTTGCATATGGTGCGGGCCATGATGACAATTGAGTCCCACGATGTTTGCACCACATTGAACGAGTTGTTTTAATCCTTCA +TTGATTGCCTGACCATTAACTAAGTAATTTGTGTTTGAAGCGGTTAATTGAGCAATGATTGGAATGTCGTATTTCTTTCT +CGTTCGTGAAATGACATTTGTTAACTCTTCTAGGTCGTAATACGTTTCGAAAAGTAGCGCGTCAACGCCTTCTTCAATTA +AGGTGTCTATTTGAATTTCAGTATGATAAAGAATAGTTTGTAAGCTGATATCCTCTTGTTTGATACCTCTAAACCCACCA +ACTGTGCCTAATATATACGTATCTTTATTTGCTGCTTTTTTTGCGATGCGAACGGCGGCTTGATGTATTGCTTTAACTTT +ATCTTCAAGACCGAATCGTTTTAACTTTTCAAAATTTGCACCATAAGTATTGGTTTGAATGACATCAGCACCGGCTTCAA +TATATGAACGATGGATGCGTTCAACTTTATCTGGATGGCTAAGATTATATGCTTCTGGACAGGTGTCTAATCCTTCAGAG +TATAAAATGGTTCCTATAGCGCCATCAGCTACTAAAACATTATCTTTCAATTGTGTGAGGAATTGACTCATTGAATGCCT +CCTTTAATGCGTATTTGATGTCTGCAATGAGTTCATCAGGATCTTCGAGACCAACACTTAATCGGAATAGACCGAAAGTG +ATACCACGTTCTTGTCTCACTTCTTCAGGTAGTGCAGCGTGAGACATTGTTGCTGGATGTGAAAGGATCGTTTCAACACC +GCCCAGACTCACTGAAACGAGTGGTAATGTCAGTGCATCGACAAATTGTTGTGCTTTAGACTCATCAGCTAAACGAAAGC +CAATAACGGCACCGCCATTTTTAGCTTGTTCTAAATGAGCAGTAGTGAGTCCCGGATAATAAACTTCTGAAATTTCATCT +TGCTTTATTAAAAATGACACGATTTTTTGAGCGTTTTCGACAGATTGTTTAAATCTGATTGGAAAAGTTTTTAAATGTTT +AGCAAGTGTCCAGCTATCCTGAGCAGATAACATATTGCCTGTACCATTTTGTATTAAATAAAGAGCGTCACTAATTGCCT +CATTATTAGTTATGACAGCACCAGCAATTAAATCGCTATGTCCACTTAAAAATTTTGTAGCACTATGAATGACAATATCA +GCGCCAAGTAATAAAGGTGATTGACCTAACGGTGTCATAAATGTATTGTCCACAGCTACCAGTAGTTCATGCTTTTCGGC +TATTTTAGAAACAGCTTTGATATCAGTAATTTTAAAACAGGGATTCGATGGTGTTTCGATATAAATTAATTTTGTGTTTG +ATTGAATGGCACCCTCGATTTGTTCGAGCTTTGTAGTATCTACGGTTGTAAATTCAATATTAAATCGATTCAAAATTTGC +TCAGTGAGGCGAAAAGTACCGCCATATACATCATCGGGTAAGATGACATGATCACCAGATTTGAAAGTCAAAAGTACTGC +TGAAATAGCAGCAATACCTGATGCAAAAGCAAAAGCGAATTTTCCCTGTTCTAATCGTGCTAACTTCTCTTCTAAAAGTT +CACGGTTAGGGTTGCCACTTCGTGCATAATCATATTTAACATCGCCACCAAGACTTGTTTGATGGAATGTTGAAGAATCA +TAGAGTGGTGGGTTAGCTGAATGATATTCCACACCTCTACGCCAATCGAATATCACTTCTGTCTCTTTTGAAAGTGTCAT +ACAATCTCTCCAATCTGAGCTTTATCTAATGCTTGGATGATATCGCGTTCGATGTCTTCATAATTTTCAACACCTAGTGA +TAAGCGGATTAAATACTCATCAATGCCACGTTTATCTTTTTCAGCATCTGGCATATCAACATGTGTTTGGGTGTAAGGGA +AGGTCACTAATGTTTCAGTACCTCCTAAACTTTCTGCAAAAATGCAAATGTCTAAATTTTCTAATAATTTAGCGACGCTA +TAGGCCTTGTTAAGTCTTAAACTAAGCATGCCAGTTTGCCCGCTATATAGTACTTCGTCAATTGCTTGAAGTGACTGACA +TTTTTTAGCAAGTTTTCTAGCGTTTGATTGCGCACGCTCAATGCGTAAATGCAAAGTTTTAAGTCCACGTAACAACAAAT +AACTATCTATTGGTGAAAGTGTTGCGCCAGTCATGTTGTGAAAATCAAACAACTGTTGCGCGAGTGATTCATCTTTGACG +GTTACGACACCTGCTAGTACATCGTTATGTCCGCCAATATATTTCGTGGCTGAATGTAAGACTATATCAGCACCTTCTGC +TAGTGGTGTTGAAAGATAAGGTGTTAAAAAAGTATTGTCGATAATTGACAATAAGCCTTTAGCTTTACAAAGTTGATAGT +ATGGCTTTACATCAATAGCAATCATTTGTGGGTTAGATATTGGTTCAATGAATAATGCAACTGTTTTATCAGTGATTTCT +TTTTCAACTTGTTCATAATCTGTAAAATCAACGTACTTAAATTTGATATCGTATTGTTGCTCGTAAAATTCAAATAATCT +AAATGTGCCACCATATAAATCGAATGAAACTAAAATTTCATCATGAGGTTTAAATAGATTACATATTAATTGAATGGCTG +ACATTCCACTTGATGTAGCGAATGATGCAATACCATGCTCAAGTTTGGCAAAACAGGTTTCAAATGTTGAGCGTGTAGGA +TTTTTAGTACGTGTATAATCAAAACCTGTCGATTGTCCTAGTTTTGGATGCTTGTAGGCAGTAGATAAATGGATTGGATT +CGCTATAGCACCGGTTGAATCATCGGTTAATGTGATTTGGGCTAACTGTGTATCCTTCATATTAAGACCCTCCTATAAGA +AAAAATAAAAAAAGCTTCCGTCCTTCGTACCCGAATGAATCGGATAAAAAGGACGAAAGCTTATGTTTCGCGGTACCACC +TTTATTTGTTATTCCATCGCTGAAATAACCTTATTCAGTACGCATTAAAAGTAAATATGCTTACTGAACAATTATCACAA +TTAAAGTCAGTAAGTAAGGATATAGTAATGTGCTATCCCATACTTATTAACAAAAAATCGTGCGTAAAGAATCCAGTACG +CCATTTAACATCAATGTTAATACTGTATCGCTATAACGGGCGAACCCGTAGACACCTCATATTGGCATCAACACTCCAAG +GCCATTTTCAAACACGCTTTCAAAATCTTCTCTCAGCTACTAAAGACTCTCTGTATAAGCAGGGTGTGTTTTACTTTCCT +CTTTATTGTGTTTACGTTTCATTAAACTGTTATAAGATATTAATTAGCTTACAGAGTAAAAAAAGATTTGTCAACAATTA +TTCAGAAAATTTTGATTTAAAAGTTAATTTGTTTGTGAAATTGTAATTGGTATCTTGAAGTTGAAAAATGAATTATTTTT +TAAATAAAGTGTGGTTAATGGTTGTCTGACTCATTTAGAATACATAAAATATATTTAACTGTTGTTATCAAATAAAAAGT +GATGTGAGTGAATTGTCAAAAAGTGAAGATCAACGTATTACTAAAACAAAAGATGAACAAATTAAGCAAATAGATATATC +GGATATCAAACCGAATCCGTATCAGCCCCGAAAAACTTTCGATGAAAATCATTTAAATGATTTGGCAGATTCAATTAAGC +AATATGGAATTTTGCAACCAATTGTGCTTAGAAAAACAGTTCAAGGTTATTACATTGTAGTTGGTGAAAGAAGGTTTAGA +GCTTCGAAAATTGCTGGTCTAAAATACGTATCAGCGATTATCAAAGATTTAACAGATGAAGATATGATGGAACTGGCGGT +CATCGAAAATTTACAACGAGAAGACTTAAATGCGATTGAAGAAGCTGAAAGTTATCAACGTTTGATGACAGATTTGAAAA +TTACACAACAAGAAGTAGCGAAACGATTGAGTAAGTCGCGCCCGTATATAGCGAATATGTTGAGGTTATTACATTTGCCG +AAAAAGATTGCTGACATGGTAAAAGATGGGCGACTGACAAGTGCACATGGACGAACGTTATTGGCAATTAAAGATGAACA +ACAAATGCTTAGGTTAGCGAAACGGGTTGTTAAAGAAAAGTGGAGTGTCAGATATTTAGAAAACCATGTTAATGAATTAA +AAAATGTTTCGTCAAAGTCGGAAACAGACAAAGTAGATATAACTAAGCCTAAATTTATAAAGCAGCAAGAACGACAGTTG +CGAGAACAGTATGGTACCAAAGTAGATATATCAATAAAAAAATCGGTTGGTAAAATCTCATTTGAGTTTGATTCACAAGA +AGATTTTGTGAGAATAATTGAACAATTAAATCGTAGGTATGGTAAATAGTTACACAATTTTATATAATAACTCTTTGTGC +AAGTGTAAATAAATTGTAATCAGTGAACATTTGATTCTAGATATATTGAGACTTTCGTAGGTTGGAAGTATATAAAAATA +TGAATACAATATTGATATATCATATAGAAGGGAGTAACGCTTATCATGAATCAAGTCATGAATATTATTTCATCTCTATT +TGAGCCATTAACAAAAATAGAAACATATGAAAACATTGCAACTAAAATCGCTATGATTGTTATTTATATTATCGTAGCCC +TCATAGTTATTAAAATACTGAATAAAATGATTGAACAGGGATTTAAGATTCAAAATAAAAGTAAAAAGAGTAACAAAAAG +CGCTCTAAAACTTTAATATCTCTTGTTCAAAATGTAGTGAAGTATATCGTTTGGTTTATAGTTATTACGACGATTTTAAG +TAAATTTGGCATTAGTGTTGAAGGGGTAATTGCCAGTGCTGGTGTCGTAGGCTTAGCAGTAGGTTTTGGTGCTCAAACTA +TAGTTAAAGACGTAATTACAGGATTCTTTATTATATTTGAAAGCCAATTTGATGTAGGTGATTATGTTAAGATAAATAAC +GGTGGTACAACTGTAGCAGAGGGAACAGTTAAATCAATTGGACTTCGTTCAACACGAATCAATACAATTTCAGGAGAATT +AACAATTTTACCAAATAGTAGTATGGGTGAAATAACGAACTACTCAATTACAAATGGTACAGCTATCGTTAAAATTCCAG +TGTCTGTCGAAGAAAACATTGATAATGTTGATAAAAAACTAAACAAACTATTTACTTCTTTACGTAGTAAATATTACTTA +TTTGTTAGTGATCCGGTTGTTATTGGTATTGATGCTATTGAAGATACAAGAGTAATATTGAGAATATCTGCAGAAACAAT +TCCAGGTGAAGGATTTGCTGGAGCTCGAATTATTCGCAAAGAAGTACAAAAAATGTTTTTACAAGAAGGTATTAAAACAC +CTCAACCAATTATGACTGCTTATAATCATAGTGAAAACGGTGTTTAGTAGTTTATAATACATGGAGGTCATATTTAATGG +CGTCAAAATATGGAATAAATGATATAGTAGAAATGAAAAAACAACATGCGTGTGGAACAAACCGTTTTAAGATTATTAGA +ATGGGTGCAGACATAAGAATTAAATGTGAAAATTGTCAAAGAAGTATTATGATTCCACGTCAAACGTTTGATAAAAAACT +TAAAAAAATCATCGAATCTCATGATGATACACAAAGATAGGAGAATGATTAATGGCTTTAACAGCAGGTATCGTTGGATT +GCCAAACGTTGGTAAATCAACATTATTTAATGCAATAACAAAAGCAGGTGCTTTAGCAGCGAACTATCCATTCGCTACGA +TTGATCCTAATGTAGGGATAGTAGAAGTGCCAGATGCTAGATTACTTAAATTAGAAGAAATGGTTCAACCTAAAAAGACA +TTGCCGACTACATTTGAATTTACAGATATCGCTGGTATTGTGAAAGGTGCTTCAAAGGGAGAAGGGTTAGGTAATAAATT +CTTATCACATATTAGAGAAGTAGATGCGATTTGTCAGGTCGTTCGTGCATTTGATGATGATAACGTAACTCATGTTGCTG +GTCGAGTAGACCCTATTGATGATATTGAAGTTATTAATATGGAATTAGTACTAGCGGACTTAGAATCTGTTGAGAAACGT +TTGCCTAGAATTGAAAAATTAGCACGTCAAAAAGATAAGACTGCTGAAATGGAAGTACGTATTTTAACAACTATTAAAGA +AGCTTTAGAAAATGGTAAACCCGCTCGTAGTATTGACTTTAATGAAGAAGATCAAAAATGGGTGAATCAAGCGCAATTAC +TGACTTCTAAAAAAATGCTTTATATCGCTAATGTTGGTGAAGATGAAATTGGTGATGATGATAATGATAAAGTAAAAGCG +ATTCGTGAATATGCAGCGCAAGAAGACTCTGAAGTGATTGTTATTAGTGCAAAAATTGAAGAAGAAATTGCTACATTAGA +TGATGAAGATAAAGAAATGTTCTTAGAAGATTTAGGTATCGAAGAACCAGGATTAGATCGATTAATTAGAACAACTTATG +AATTATTAGGATTATCAACATATTTTACTGCTGGTGTGCAAGAAGTACGTGCTTGGACATTTAAACAAGGTATGACTGCA +CCTCAATGTGCTGGTATCATTCATACTGATTTTGAACGTGGATTTATCCGTGCCGAAGTAACAAGTTATGACGACTATGT +ACAATATGGTGGCGAAAGTGGCGCTAAAGAAGCGGGCAGACAACGATTAGAAGGTAAAGAATATATTATGCAAGATGGCG +ATATCGTTCATTTCAGATTTAATGTATAAACGATAGAGTGAAGTTAATTAAATAGTATATATGTAGAAGAGGCGGAATCA +ATTGTTCGCCTCTTTTAATTATGCGTATAATTTATTAAAAGAATGGAATAATTTTACTCGCGTTAATAATATCTTGAGTG +CTGAAAAATTGTTTGCCTTCGCCAGTATAAGCAGGCTCTAAAACAAGATTAGCCTTTGCACAATAAAGCCATTCAGGATG +AATACCACTATTAAGTATCTCTTGGAATTCTTGAAAATCTTTAGACCAATCAATATTTAAATTCATTCCTTACCACCTCA +CAACTATTATAGAACATATGTTCGCTTTTGTGAAGTGTATTTTTAAAAATATCATAGAAAGTTTCAAATGAATTAATATC +AAAAATGATATGAAGAGTAGTTTGAATTATTATTGTAAAAAGTAATGGCGCATGATATAATTCTTTATTGTGAGTAATGA +AAATTATTCCTTGCTTATCTGTTTTAAGATTGATAAGCCGTATAGACCACAAGGAGGTGCAAATATAAAATGAGAACATA +TGAAGTTATGTACATCGTACGCCCAAACATTGAGGAAGATGCTAAAAAAGCGTTAGTTGAACGTTTCAACGGTATCTTAG +CTACTGAAGGTGCAGAAGTTTTAGAAGCAAAAGACTGGGGTAAACGTCGCCTAGCTTATGAAATCAATGATTTCAAAGAT +GGCTTCTACAACATCGTACGTGTTAAATCTGATAACAACAAAGCTACTGACGAATTCCAACGTCTAGCTAAAATCAGTGA +CGATATCATTCGTTACATGGTTATTCGTGAAGACGAAGACAAGTAATAATTAGAGGGGGCGTTTAAATGCTAAATAGAGT +TGTATTAGTAGGTCGTTTAACGAAAGATCCGGAATACAGAACCACTCCCTCAGGTGTGAGTGTAGCGACATTCACTCTTG +CAGTAAATCGTACGTTCACGAATGCTCAAGGGGAGCGCGAAGCAGATTTTATTAACTGTGTTGTTTTTAGAAGACAAGCA +GATAATGTAAATAACTATTTATCTAAAGGTAGTTTAGCTGGTGTAGATGGTCGCTTACAATCCCGTAATTATGAAAATCA +AGAAGGTCGTCGTGTGTTTGTTACTGAAGTTGTGTGTGATAGCGTTCAATTCCTTGAACCTAAAAATGCGCAACAAAATG +GTGGCCAACGTCAACAAAATGAATTCCAAGATTACGGTCAAGGATTCGGTGGTCAACAATCAGGACAAAACAATTCGTAC +AATAATTCATCAAACACGAAACAATCTGATAATCCATTTGCAAATGCAAACGGACCGATTGATATAAGTGATGATGACTT +ACCATTCTAATAAAAATTAACGAAATTAAAGCGAAAAAATTATCAAAGGAGGCACACAATCATGGCAGGTGGACCAAGAA +GAGGCGGACGTCGTCGTAAAAAAGTATGCTATTTCACAGCAAATGGTATTACACATATCGACTACAAAGACACTGAATTA +TTAAAACGTTTTATCTCAGAACGCGGTAAAATTTTACCACGTCGTGTAACTGGTACTTCAGCTAAATATCAACGTATGTT +GACTACAGCTATCAAACGTTCTCGTCATATGGCATTATTACCATATGTTAAAGAAGAACAATAATATATAATTTATTGTC +AAACCCCGTAGGCATAGGCTTACGGGGCTTTTTGTGTTTTGGGGTATAGAAAAAGGGCAAAAAAGGATGATGTGAATGTT +TTGTGTTCGGAATTTGCACAAAGATATGTTTATATTGCAAAAATAATATGAATTTAGATGCATAAAAAAAGAACTACGCA +TTTTAAATAAAATGCATAGCTCTTCTTTTTCTTGCATACGAATTAAAATAACTCGCGAGACCTATAAGTCTCTTTCCTCA +CTAGATAGTTTATACTTTTGGTCTGTTGAAGTCAATAATTTTATCTAAAGCTATAAAAAATCTTTTGATAGCTAATGCAT +TATTATAATAGCTTTCGTTTCTTTTATATCGCTTTTGAAGTTGGTCCAAATCGTGATATCTTGCTTGGATAATTGCATTA +CTACAAACTTGATTATGTAATTCTAGCGTAGCGAAAGTATCTATGAAATTTTTTATTCCGAACATGTTTCTAGATATGCC +TATATTATTCCCTTTTTCAAATAAATATTGAGGTAATCTACTGTCATAATTTAGATTTGCTATGATGGGTTGGTTATGAG +CTGATTTGTTTCTTATATTTTTAACTAAAGGCATTAAAATATTAGCAACTCTCAATTCTTCGTCATTGTACTTCTTGTAA +TAGAAGTTGAGAAACGAAACGAATTGACCTAGTTGCATGAATTCAATGCAAACCCATGCGGGTGGATTTTGATAGTATTT +ATTCAACTTCTCGGGTAGTTGTCCTCGTTTATTCATATGCTTGAATATTTCGTTTTTATTTTTGATTTTGGTTTCCATAA +CTTCTTCTGGTGTTCTTGAATTTGTGTCAAAATTTGAATTGCTATATGATTTATCAATACATAAGAACTCATCTATTATT +TTATAACCATCTTCTTGGTTATTTTCTGTTATTAGTTTTAAGACTAGATACTTTAAACTATGTTCAATATCTAAAGTTAA +ATGCAACATTGTGTATCTTAATTTCATATCTATAGTTGCTAAATCTGATAAATAAGCAAATTCTATGAAATAGCCGCCAT +TCTTTTTTTCGAAATTTTTTCGGAAATAAGCTAGTTTGAAGAAGTAATTATTTTTTCTAAGAATTTCATTTGCTTTTTCG +GTGTCAATAATATTAAAAAATATATTCATCTGTTTTAATTTTGCTATTTGCTCATCAAAATTGAGCATAGGCTTAATTTC +TGCTAGTGTATCATCTTTTTCCAATTTTAACTCCCCAATCGTTCAAATTTATTCATCATATCTTTCGCCATCTGATTAGT +AACATGTGTGTATATCTCTAGATTTTTTTTATAATCTGAATGACCTACATGCTCTTGCATTGCTTTTAAGTTAATTCCTA +ATTGAGCAAGTGTAGATATATGCGAATGATGTAATGTATGCGTCGTTATAGGTTTCTTAATAGAACTAATATCAGCGCCC +CCCTTTAATAATGTGGCTAATTTTGTTCGAGTCGATAGGGCTACCAGACGTATTTGTGAATATGTACTCTCTATCAATAA +ACTTATCATTCCAAGCATAAGTGTTCTTAGTAAGTCGATGCTTTGGGTAGTGAGCCCTGTGGCCTTATAGCTATTACTTC +TTTCAGTTGTCTCCTTTACTCCGAATGCTCCCGTCTTTTTTCAGTTATCCAATTAACTTTACCGTCGATATCTAGCGTTT +TATCTTCATAGTTTATATTTACTCTCTTTATTGCAAGTAGCTCACCGATACGCATGTCATTAGCAATTTGAAACTGTACC +ATAGCTTTTACCATTTTATAATTACGTTTTGTCGTTGGATATTTTTATACTTAATTACATAGTCGAAACAATCCAGTAAC +TCCTTAACTTCATTATCTTCTAATGTGTTATTATGTTTAGCTAGTAACGCATCACTATAGGGATATCTATTTTATCTATT +ACACATATAACTTTGAATTGCTTGCTATTTTAAATTAACAAATTTTATTCTCTTAGATTTTGTCCAATTATGTGTAGACG +ATTTATAGTTATTAAATTCAGAGTGGTAGCAAATTAAAGTTAATCAAGAGTTAAGATGAATTTAATTCATGAACACGTCT +ATTATTTTTATAATTGTAGCAAATAAAGCTTTACATCAAGGAGGTAATTAAATATGTTCAAAAAATATGACTCAAAAAAT +TCAATCGTATTAAAATCTATTCTATCGCTAGGTATCATCTATGGGGGAACATTTGGAATATATCCAAAAGCAGACGCGTC +AACACAAAATTCCTCAAGTGTACAAGATAAACAATTACAAAAAGTTGAAGAAGTACCAAATAATTCAGAAAAAGCTTTGG +TTAAAAAACTTTACGATAGATACAGCAAGGATACAATAAATGGAAAATCTAATAAATCTAGGAATTGGGTTTATTCAGAG +AGACCTTTAAATGAAAACCAAGTTCGTATACATTTAGAAGGAACATACACAGTTGCTGGCAGAGTGTATACACCTAAGAG +GAATATTACTCTTAATAAAGAAGTTGTCACTTTAAAAGAATTGGATCATATCATAAGATTTGCTCATATTTCCTATGGCT +TGTATATGGGAGAACATTTGCCTAAAGGTAACATCGTCATAAATACAAAAGATGGTGGTAAATATACATTAGAGTCGCAT +AAAGAGCTACAAAAAGATAGGGAAAATGTAAAAATTAATACAGCCGATATAAAAAATGTAACTTTCAAACTTGTGAAAAG +TGTTAATGACATTGAACAAGTTTGAAATTAAGCTAAATTAGTATATATAGTGTTTTATCGCTAATACTTTGAAAGTTAGG +TATCTAAAGGTGCCTAGCTTTCTTTGTTATGATTAGCACCATCATATAGAAAATTCTTTAATGACGTTTATGCCGAAATC +TACAAAAATAATTTCTCTTTTATGGTTAATCAATATGTTTTTACATTCGACAACCTTAAAATTAACTTCTTTCAATTTCA +TAATAAAGTCTCTATAAAATAACTTAGTTTAAAAACGATTCGTATCTTTCAGATTCAAATACCATCATTTTCTCCTAATA +CTTACACTTTAATTACAATTATGTAAGTTGTTTTCAGATACATCCTATTTTATATTTTGTCAGAGGTGATCTTTTGAAAA +CGATTTTAAAAACAATAACATATCTTGTACTTACTATCATTGGCGCTTATGCTGCTTTATTCATTTTAAAAACAATAGAC +TCCCATGGTATAACAGATCAATTTAACCCATTAGTAAAGAAGGATGATTCTTATGTTAAAACGACAGAGGTGTCTACTAG +AATGGATGATCAACTCCGAAGTTATAGTCAAAGTGTTTTTAATAAAGAAGGGAAAGAGACGCAATTAATGTATACTGCTA +CATTTGATGTTAAACCGCATAGATACTTGAAAATTACACATAAAGGTCATCATGTAGAAACTTTTGAAGAAGTTGAAAAG +GAAGAAGTACCAAAGAAAGCATTAGACAAACTGAGTCGATAATAGCATGCTTATATTACATGGTTCATTTATAAAAGGAG +TACGAACGAAAGTAACGCATGACGATTAATTTAAAAATATTTGTAATAATTATGAATAAAATTAAAAACAAGGGGTAATA +CAATCTATATAGCATATAAGCTTTTGTTATGAGTTTCAAAAATAGGAAGAGAGAGTGATATTATGAAATTAAAATCATTA +GCAGTGTTATCAATGTCAGCGGTGGTGCTTACTGCATGTGGCAATGATACTCCAAAAGATGAAACAAAATCAACAGAGTC +AAATACTAATCAAGACACTAATACAACAAAAGATGTTATTGCTTTAAAAGATGTTAAAACAAGCCCAGAAGATGCTGTGA +AAAAAGCTGAAGAAACTTACAAAGGCCAAAAGTTGAAAGGAATTTCATTTGAAAATTCTAATGGTGAATGGGCTTATAAA +GTGACGCAACAAAAATCTGGTGAAGAGTCAGAAGTACTTGTTGCTGATAAAAATAAAAAAGTGATTAACAAAAAGACTGA +AAAAGAAGATACAATGAATGAAAATGATAACTTTAAATATAGCGATGCTATAGATTACAAAAAAGCCATTAAAGAAGGAC +AAAAAGAATTTGATGGTGATATTAAAGAATGGTCACTTGAAAAAGATGATGGCAAACTTGTTTACAATATCGATTTGAAA +AAAGGTAATAAAAAACAAGAAGTTACTGTTGATGCTAAGAACGGTAAAGTATTAAAGAGTGAGCAAGATCACTAAAAAGA +TTCGTTGCATTAACGCTTGTAAAATGTCTTAATTCATATTAATTAATAAAATACCCTCACAACGTAATGTAATATATCCA +GTATAGATTTGAAAGGATATATGCCAAACGTGTTGTGAGGGTTTATTTGCATCTATTTATCCATGCGAATATCGACTTCT +TCTAAATGTTTCTGATATTCTTTAACCTTACTTTCTAAAAACATTTCATATGGTGCATCAAAGAAGTCAGCTAAATGCAT +GGCATCTTTAAATTTAGGTTCATGGTGATGATTCTCCCATTCCCAAATTTGATGTGCTTCATATTTAGTACCATATTTCT +CATTTAATTGCTGTGCTAATTCGTCAATTTCTAAATTATGTTTAGTTCGTAAGTTATATAAAATATGCATATTCATTGTT +CATTAACCTCGCTTCTTGATTTAAAATTCAAAGATGCTCTAATAATATGTGTATATTCTAGTCCTATAACATAAAATAAT +CAAGCTATGAATTCGGCAGAAAAAATAATGTAAATGTATATGATATTATCGGTACAGAGACTTTAAATAACGATAGCTAC +ATTGAATAAAATTGATATTCAATTACTACTTTTAAAAATATTTGGATAAAAATAATTTGAATTTGTTTTAGAATTGTAAA +TAAGGGGTACTACTTAGAATAACGCAAAATAAATAGAAAAAGGAGACTGAAAATTATGTTTGGATTTATTGGAATGTTAA +TTGTCGGTGGCTTAATTGGATGGGCTGCTGGTGCTATTATGGGTAAAGATATCCCAGGTGGTATTTTAGGCAATATTATC +GCAGGTATTATTGGATCATGGGTAGGTGGCAAACTATTCGGACAATGGGGTCCTGAATTAGGAAGTATTTACATCTTGCC +AGCATTAATTGGTTCAATTATCTTAATTGCAATCGTAACGTTAATTTTAAGAGCTATGCGTAAATAATAAAAAAATTCAA +TTGGAAATTGAAATAAAGATGGCATTCAGTTGTCATCTTTTTTATTTGCGTGAAAATGGTCAATATGTATTATAATATTT +GAAAATGTAGATAAGATAGGATTAAAAAGTAATAAATTAATATGAGGAAATTTTGGAAATAAAAAAGCGAGTATTTGACA +AATAAAAAAAGGATGACGTTGGGTTGCGCCATCCAGGAGTATTATGTTCATGAATATAAAAGACTGTGTAGTCATTTAAG +AACTACACCCATATAGTAACACATCACTTGATTAAAGACAATACTATTTTTAGAGTCATTAATTTAAAATATGAATTAAT +TCTTTTTTTAGAATAGAATATTAAGATTGATATCGAATTAGTAGTCAAAGTGTTATGGTAGATATGAAATACATAAGGTA +AGGAGTAATATTATGACGATTTATTTAGTTAGACATGGCGAATCAAAATCGAATTATGATAATAAACATTTTCGATCTTA +TTTTTGTGGACAATTAGATGTGCCGTTAACGGATACTGGCACAAAAAGTGCAGACGATTTATGTGATTATTTTAAAGAGA +AACAGATTAAACATGTATATGTTTCAGACTTATTAAGAACACAGCAAACGTTTGAACATATTTTTCCATATGACATTGCA +TCAACGACTACCCCTCTATTAAGAGAACGTTCACTTGGCGTATTTGAGGGTGAATATAAAGATGAAATCAGTGCGAATCC +GAAATATGAAAAATATTTCAATGATCCAAACTTTAAAGACTTTCGTCATAGTTTTTCACAAAAAGCGCCTGAAGGAGAAA +GTTATGAAGATGTATATCAACGCGTAGAACATTTTATGAATCATGTTGTCAATGAAGATACACAAAAAGATGATATTGTC +ATTGTTGCACATCAAGTTGTCATTCGTTGTTTGATGGTTTATTTTAACAAAGTTTCAAGGGCAGAAGCTGTGGATTTAAA +GGTAGAAAATTGCAAACCATATATCATTGAATAGAAGTATAGAGGTTCTGAAAAACTGTGTTTTATAGCGGCCTTCAGAA +CCTTTTTTTCATTTGGAATTTAAGGGGAATCATTCTTTTCAACAGACTTACCACCAATGGCTAATGTGATAATTGTGCTT +GTGAACAAAATAATTGAAGTTATAAATGACGCTAATGTAAAAGGTGTAAATTGTGTGAGCGATAATATCAATGATAAAAA +GAACCAAGAGATAGCCCCTAGAAATTGTAAAATAGCAGAAGAAATTCTAAAACTCATATAATCATATATAGCTAAAACAA +GTGAAGGTAATGCGACTAATACCATTATAATGATGTTGAGGGTGAATAAATATGGCTGTTCAAAAGTTACTGTGTTTGGT +CCTGTTGGATGCATTGCTGCTAAGAACAAGCAAACAATCGTCGATAAAATTGCTAAAATAATAAAAGTAATTCGAACTTT +CATCATGATCATCCTTTGTTTATAGAGTCAATATAAGTATGGAATATGTTAGGTATATAGTCAAATGCGTCAACTAATGG +GAATTTTGGCATAGATAGAGAATTTAAGGCAATTAAAAAGGCATCAAACAGTAATATGCTGCTTGATGCCCAAATGATGA +CTTTAGCTAAATTGATTAGTCACTTTTAAAGATAAAGAATTGTCATGAATTAAAACTCATGTAATGATGTGTTACATTTC +GCAATGATGGCTTTCAGTTATTTATCGATAACATCACTCTTGATACCTTTAGATTTTAAGAAATCTTTAATTTTATCTTG +TTGCTTTTTATTAACATCACCGGCATATTTTGTTGGCACGTCGACAACATTGATTTTATTTTGCGGTTGATAGCTAAGCT +TTTCAATATCTTCATCAACATTGGCGATTGTACTATTTAAAGCTTTGAAGTAATTCATCATTAATTCAACGGGTTTCTTA +TATTCTTTAGGAATATTGTTTTCAGTGACAAATTTCTTGAAATGCAAATCGTTTTTAACAGCTAAGTTAGATAAGTGGCT +AAGTGTTTCTGCTTGTTTTTCAGTCACTTTTGTTTGACTGTCAATTTGTTTATCTAGTTTATGTTGCATAATATATTTGT +TATCAAGTATATCGCTATTTACAGACAAATACTTTTCTATAGCTTGCTTCATCTCTGCATCACTAATATCACTATTTTTC +TTATCTGAGTTAAAGATATCTTTTGTTTCTAATTTTTTAGCGCTTTTAGGTGCATGGATGCCAGTACTTGTATGATGATC +TTCGTTATCAGATTGATCGGACGCGCAACCTGTAAGAATTAATGTCGATGCTAAAAATGTACTTAGTAGTAATCTCTTTT +TCATAATGTAATATAACTCCTTAGTTTATCTTTAATTGAAAAAATATGTATTCATGTTTAATAGAGTAACATTGAATTAG +TTTGGAATGTCACGATGACCTTGCAATGACCATAGACGTAAATGATTACGTGCATGAGTTGCTTTTTCTATCAATAATGC +ATCATTTTGGACGTTGTTAAGGATAGCTTTATCTATAAATAACTGCATAATTGGTTGTACTAATTTAGACGTAGGTATCG +TACGTAAAAGCATAATAATTTCGTTCACATACTTTTCTTTCTCAATATCATTTTTCATATTGATTTGTTTGCGAGAGGTA +CATACTTTAAGCATTATCGCACATCTCGTTGTATATATTAAGTTTATCATAACATGATTTTATGTCGGGATAAAAAAATA +ACAGCATCTTAACAAATGTAAGATACTGTCAGTGAAATGAATGAAACTTTAGTTTCTGATAATATAGTCAAAGGCATTTA +ATGCTGCATTTGCACCAGCGCCCATTGAAATGATAATTTGTTTGTTCTTCTGATCTGTGACATCGCCAGCAGCAAATATT +CCAGGAACATTCGTATTATTGTTACGATCAATCACAATTTCACCACGTTCGTTTAATTCAACAGCATCGTTTAACCATGA +TGTGTTTGGAAGTAAACCAATTTGAACAAAGATACCATCTAAGTTAAGTAGATGTTCTTCGCCGGTGTTCATGTCTTCGT +AACGTATACCTGTAACATGGTCTTCTCCGACAACTTCAGTAGTTTTGGCATTTGTTTTGATATCAACATTTGATAAAGAA +CGTAAACGATCTTGTAACACGTTGTCTGCTTTTAATTCGCTAGCGAATTCGAATAATGTAACATGATTAACGATACCAGC +AAGGTCAATTGCTGCTTCAACCCCAGAGTTACCGCCACCGATAACTGCTACGTCTTTATTTTCAAATAGAGGTCCGTCAC +AGTGAGGGCAGAATGCAACACCTTTATTAATCAATTGCTCTTCACCTGGAATGTTTAGCTTACGCCAACCTGCACCAGTA +GCAATAATGACTGTTTTACTTTCTAAGACAGCACCGTTTTCTAACGTAACTTTAATTGCTTCGTCAGTCTTTTCGATATC +TGTAGCACGTATACCTGTCATTGCATCAATGTCATATTGATCAATGTGCGCTGCTAAGTTAGAAGAAAATTCAGAACCAG +TTGTTTCTTTAACAGTAATGAAGTTCTCAATACCAGCAGTATCATTAACTTGGCCACCGATACGATCAGCAACTATACCA +GTACGTAAACCTTTACGTGCTGTGTAAATCGCTGCACTACCACTAGCAGGACCACCACCAACGATTAAGACATCATAAGG +TTCTTTATTTTCAAACTCAGATGCATCTGCCGTACTGCCTAGTTTCGAAAGAATATCTTGGATTGTCATACGACCATTGC +CAAATTCTTCGCCATTTAAAAAGACAGCAGGGACTGCCATGATGTTTTCAGATTCTTCACGGAACACTGCACCATCAATC +ATAGAATGCGTGATGTTAGGGTTGATCACACTCATTAAGTTAAGTGCTTGAACGACATCAGGACATTTTTGACACGTTAA +ACTAATGAATGTTTCAAAATGGAATGAACCTTCTAATTTTTTAATTTGGTCAATGATTGACTGTTTTTCTTTAGGTGCAC +GACCACTAACCTGTAAAATTGCTAAAACAAGTGAGTTAAACTCGTGACCTAATGGAATACCTGCAAATGTTACACCTGTT +TCTTCGCCAGGACGATTGACTGAGAAACTTGGTGTACGTTTTAAAGATTTTTCAGAAAGAGATAGTCTAGGTGACATATC +AGTAATTTCTGTCAACAAATCTTTAAGTTCTTTGGATTTATCATCTGAACCAAGGCTGGCAACGAATTCAACGTTGCCCT +CCATTAGTTCTAATAGTTGTTTAAGTTGTTGTTTTAAATCAGCATTAAGCATGGTTGTAATGCCTCCTTAGATTTTACCT +ACTAAATCTAAACCAGGTTGCAATGTTTTAGCGCCTTCTTCCCATTTAGCTGGGCATACTTCGCCAGGGTTTTTACGAAC +ATATTGAGCTGCTTTGATTTTGTGAGCTAATGTACTAGCGTCACGGCCAATTCCGTCAGCGTTAATTTCAGATGCTTGTA +CAACACCGTCTGGGTCGATAATGAATGTACCACGTTGAGCTAAACCAGTAGCTTCATCTAATACATCAAAATTACGAGTG +ATTGTTTGTGATGGGTCACCAATCATAGTGTAAGTGATTTTGCTAATTGCATCTGAATGGTCATGCCATGCTTTGTGTAC +GAAGTGAGTATCAGTTGATACTGAGAATACATTTACGCCTAATTTTTGTAATTCTTCATATTGGTTTTGTAAGTCTTCTA +ATTCAGTTGGACAAACGAATGAGAAGTCAGCAGGATAGAAGCATACTACGCTCCAAGAACCTTTTAAATCTTCTTGTGTA +ACTTCTTTAAATTGATCTTTTTTTGGATCGAAAGCTTGCGCTGTAAATGGTAAGATTTCTTTGTTAATTAATGACATAAA +TATCTTCCTCCTAAGAATTTAAGTATGAATTAGAACTATCAATTGATTGCGCTTAATTATAATAATTCTAATCTCTTAGT +TAGCATTATTACATTTTGATCCAGAATAGTCAACTGGATAACTTTGTAAAGTGAATGATTACTTTTAAAATAAAGAAAGA +TAATATAAAGTGCTTTGATAATGGATTTTGTAGTTGATGATTTAAAAGGTTGTGTCTATATTTAATATCTTGATTTTAAT +GTAAAAAATGTAAAAAAAGAAGATTTGTATTCTCAACTAAGTCAACCTTATTGATAATGGTATGAGAATATTTGTTCGAG +ATGGATGAAGGTAATGAGTGAGAAACTGGATTTTTAAAGTATGAGACAATATTTTAAAAAGTTCAATTATTAACTTATAA +GCAAATAATTGCTATAAAAAAGTTTGGACGTGTACAATTGCAATATGAAGATTTTAAATTAATTGTAAAGTATCGAGGAG +TGGGTAACGTGTCAGAACATGTATATAATCTTGTGAAAAAGCATCATTCTGTTAGAAAATTTAAGAATAAACCTTTAAGT +GAAGACGTTGTTAAGAAATTGGTAGAAGCTGGACAAAGCGCTTCGACGTCAAGTTTCCTGCAAGCATACTCAATTATTGG +TATCGACGATGAGAAGATTAAAGAAAATTTACGAGAAGTTTCTGGACAACCTTATGTTGTAGAAAATGGCTATTTATTCG +TCTTTGTTATTGATTATTATCGTCATCATTTAGTTGATCAACATGCTGAAACTGATATGGAAAATGCATATGGTTCAACG +GAAGGTTTGCTAGTAGGTGCAATCGATGCAGCATTAGTTGCCGAAAATATTGCGGTAACTGCTGAAGATATGGGGTATGG +CATTGTCTTTTTAGGATCATTAAGAAATGATGTTGAACGCGTTCGAGAAATTTTAGACTTACCTGACTATGTCTTCCCGG +TATTTGGTATGGCAGTAGGGGAACCCGCAGATGACGAAAATGGTGCAGCCAAGCCACGCTTACCATTTGACCATGTCTTC +CATCATAATAAGTATCATGCTGATAAGGAAACACAGTATGCACAAATGGCAGATTACGACCAGACAATCAGCGAGTACTA +TGATCAACGTACAAACGGGAATCGCAAAGAAACATGGTCGCAGCAAATTGAGATGTTCCTAGGAAACAAAGCAAGATTAG +ATATGTTAGAACAATTGCAAAAATCAGGCTTAATACAGCGATAGCAAGATACCAAAATAACCCGCCCCCCTCTAGCTTAA +AATGATAAGTATAGCTAGAGGGGGCGGGTATTTCTTGCAATGAATTAGTGTGAAGTTAATGCAGCATTATCATTTGAATC +GAAAGTATCTTTATCCCAATGTTTAGTTAACTTGGCGGTACCTGTACCAGCTAGCATTGAATCGTTCACGTTTAATGCTG +TTCTACCCATGTCAATCAATGGTTCAACGGAGATGAGCACGCCGGCTAAAGCGACTGGCAAGTTTAACGTTGACAACACC +AATATGGATGCAAATGTAGCCCCGCCACCGACGCCAGCAACGCCGAATGAACTAATAATCACGACAGCGATTAACGTTAC +AATAAATTGTAAATCAATTTCTACATTAGCGACGGGTGCGACCATAATTGCAAGCATGGCAGGGTAAATGCCTGCACAAC +CATTTTGTCCAATCGACAATCCAAATGTCGCAGCGAAATTGGCAATACCTTCTGGCACGCCTAGACGTCTTGTTTGTGTT +TGTACATTCAATGGTAAGGCACCCGCGCTTGAGCGTGATGTGAATGCAAAGATTAATACTTCCAAAGTCTTTTTAACATA +GCGAATTGGGCTAATACCTAACAGGCTTAAAATAATTAAGTGAATGATATACATCGTAATTAATGCAGCGTACGATGCGA +TTAAGAATTTTCCTAAAGTCCAAATGGCGCCAAAGTCACTTGTCGATAATGTGTTGGCCATAATTGCTAATACACCGTAT +GGCGTTAAACGTAAGACGAACGTCACAATCGCCATTACTAGTGAATAGATAGCGTCAATCGCACGCTTAAGCAATTCACC +ATGATCAGGTTGTTTGCGTGCTACGCGTAAATAAGCAAATCCTATAAACGAAGCAAATATCACGACAGCAATCGTGGAAG +TTGCACGTTGTCCAGTGAAATCTAAGAATGGATTTTTAGGCAATAATTCCAAAATTTGTTGTGGTAACGTATGTGCTGTT +AAATCTTTCGCTTGTTTAGCAATTTCGCTTCCACGTGCTTGTTCAGCGTTACCAAGGTTAATTGTTGATGCATCTAAACC +AAACACCAAGGCATACACAACACCAACAATCGCAGCAATGGTGACAGTGCCAATTAAAAAGATAAAAATGAGACTACCAA +TTTTAGCAAACTTTTCTCCGATTTGAATTTTAGTGAATGCAGCTACAATAGAAATGAAAATTAAAGGCATAACAATCATT +TGCAACAATGCAACGTAACCTTGTCCGACAATGTTGAACCAGTCACTTGTTGATGTAATAACATTCGAATGTGTGCCATA +AATAAGATGCAATAACACACCGAATACTATACCAATCCCTAAAGCTGTAAACACACGTTTCGCAAAAGATATATGTTTGC +GAGCCATCATGTGCAATATTACGATGAAAATCACCAATACAATAATATTAATCAGTGTAAGAAAAGCATTCATGAACGTC +ACTCCTTAAATTTTTGAATATAATTCCGACTAGTATGCTAAGAATAATATTAATTTTAAAATATTTCAAGAGGGCATTTG +CAATTAAATGTAAACATGATGAAATTCGACCGTTGGCATAAGGGGATTTGTAAACAAATCGTTTTGAATTCAGTAGTGAT +AGGTGGAAAGGGTAAATAGTGTGATGAGCTAGTCGATTAAAGTTAATATTTGAAAATGAAGCAGGGCATTAAATGCAATA +AATTAAATAAGTTGTCATTAAAGCATTAATAATAGAAATGATTTTAACAGGAAAAAAGTGATGAATATTTGGAAAAGATA +TATATCGTGCACTGTCATGAGAGATAAGATATGAGAAAACAAATTTATCCTTGAATCGAGATGTGGCTATGGCGGTTTGA +TAAAAATTGCTAACCTCATATTTATTGAATGAAAAGATAATAAATGATCAACCTTGAAAAGACGTTTCGAACAAACACGT +TGTAAAAAATTCTGTCGAATCAAAATTAGCATCTTTTGAGTGTTCGTGTGTGCAGGTGGTAGTTCAAAAGCTACCGTAAA +CAAATATTTTTGTTTGAGCAAAAAGGCGTTTGTAGTAAATGTGAATATGAGTGTGCACTTGTGCGATTCGTCAGTTACTA +ACACAGCCAAACATTTAAATTGAATGGACTTATTAAGATGAATCTATTTAGAAGTACACAGAAGCCATCCCATTTAAAAG +TAACATACTATAAAAATTTATTCAAAATTCAATATTAATCATAATGAATAATTAAAAAACCTGAAACAAATTAGTTTGAT +ATGCTCTCTTCAAAGTAGATATTGAATGGAGTATATCAGCTTGTTTCAGGTTTTATGAATTGAGTATTAAATTTGAAAGT +AAATTACAGTTCTATAGGTTTATAAAAATCAGTTTTTAAATCAAAGCTATATATGAATTGAGTATTAAATTTGTGTTCAA +ATCACAAGTTTACAGGTTGAATAAACGTGCGAAGAAACCTTTTTTCTCTTCTTGAGGTTTTGGTTCTGACATTTCTGACT +GTGAAGCTGTGCTGTCATTTTCTGCTTTCGCTTCCAATGAATCGTTCGAAGTAGCTTGTTTAGAAGCTTCTGCATGGGCT +TCTGCTACTTGTTGTTCAGCATCATGTTGCGCTTCTGCATCTACTGTAGCTTCTTTATTTGAAGTCTCAGCTGCATTGTC +ATTTTTAGACTGCTCAACGCTGGCTTTTTCAATTGCTTCAGTTGCAGAAATTTGATCCTCTTTATCTACTTTAGTAGCAG +TGCCATCGCCTGCATTTTCTGCTTTAGTATCCGTCTTAGCGTCTGTTGATGTATTCACTCCAGCAACCTTCGAATCTTTA +CTGTCAGCTTTATCTTCAGTAACAGCATTCTTTTCAACTTTATTTGTTGATTCAGCTAATGCTTTTGCTTGAGAGGCTTG +CGCTTCTTGGATAGCCTCTTGTGATGTTTGGATGGCTTTTAAGGATTCGCTATTTGAATCGATTTTAGATGTAAGTTGAT +TTTGAAGTTCTTTTAATTCTTGTTGTTGCTGATGTACTTGATTCATCATTTGACCAAGTAGGTGACGTTCTTCGCGCATT +TTCTGGATTTCTAGGCGTAAATCTTCTACTAGTTGAGCGACATTTTGATTAGTAGGTAAGTTTTTGTCATCGTTTTTGAC +AATGACTTGCAGGAAGTCTTTTTCTTTTTCTAATTCATCAAACGCTAGATCATAACTATTTGTTTGTTTTACTTTGTCGG +CAATGTCTTTGAAAAGCTCGATATCTTCTTCTTTGAAATCAGTTGCTTCACGACCACGATATTCAGTCTTGCTTAGTTGG +TAACCACGTTCTTCTAAATGTTGAACAATTTTACGTACTTGCTTCTCACTTAGTTCTACGCGTTGTGCAAATTCTTTAGT +TAACATACTTTAAAGTCCTTTCTGCATATACTTTATGTATACTGACGTTTGTCTACGGGCAGTGAAACGTCAGTCTTGCT +TCATACATAGTTTAAACGTTATTGTTAGTGATTTCAAGTTTGTTTAAAATGGTTTACGTAAATCCATTTCTTCTAATAAA +GCATAAATAATTTCACGTGCGATACCATTGGCAAATACATACTTCAAAATATAATTTGATTTAACACTTGTACCGTAGAT +ATCCTTTGTCAGTTTAATATTTTTCTTTAAAAATAAGTCCACTTTTTTAGTGTGACCATCATTAGTTGGATGAATGTAAT +CAATTTCAGTTTGTCTTTCAAATTGATGTTCGTATAAAAACTGTCTACGGTGTTCAATAATGAGGCCGTTATCTTGTGTT +AAATCTGCAGGTTCTTTAGGGACTTCCATCATTAAAAAGTTAATACTTCTAGAATGAACAAGGTTTGATAATAAGTTTAG +TTCATGTTCAATTTGATGTAATGTTTGCGTCATTAACTCATCATGGTTGGGTAGGTCTTTTGCGAATAAATAAATTAAAA +ATGCTGAATTGGTTGTGGCTTCATAATGTGCAGTTGCTAGACTGACAACAACATCGTTTTCTAAGCCTACGATAAAGACA +TAATCGTTTTCGGTTTTGTTGTTTTCTAATGAGCGTTTGATGATACGTTTATCTTCAGTTAAAGATAAATTTAACTTAGT +ATCGTAAAGTTTAAGTGCTTCGTTGTAATGTGGATCTTTGACAGATTGAATGGTTTTAAATTCCATAAGAACACCTCCCC +AATTTAAATAATATTATAGCATAATCGCCTGCTGTAAAAGACTGTTCATAAACTTTTAAATGGTATAAAAAACTGTACTA +TCTTAAATTAGACAGTACAGTAATCTCATTTTGAATTCAGTGTGATAACTAAGCTTTGGGACCTTTAGATGCTTCAGCAA +AATGTGTAATATCAATCTCTTCATAAGCTGAATTATTTTCATGCACTTCTTGATGTGATGATTTGTCACGAACCGCTACA +ACTAACATTTTATCGTCTAAAATAAGTTGTTTATATTTTTCTAATTCATCAGGCGCTAAGTTGTAGCGTGATAAAACTGC +ATGTTCACCATCTTCTCCTGTTAACAGTTTAGTCATTCTATCACTAAATGTTCCACTTGTTGAGATAAGGGAGATTTCAG +AGTCGTGTAAGTCATTTAGGTGTAATTTACTTTTACTAATAATTGTTAGCTCTGATTCTAAATAACCTTCAGATTTCTTT +TGATTGATTACGTTGTATAATTCGCCAGTGTCATTTACTACAGTAATATCTGCCATAGTTGTCGCCCCTTTAAAAATTTG +TTTATTTAATCTTTTACCCTTCTTATTATAAAGTAAAACCCTTACATTATTAAGTTATAAGTCTTCATTCGCATTAAACG +TCTCTGTACATTTATAAAACTATTAAAAGATTACCTAAGCAGTTATTGAAAAAATGCCGAAAATTTGCTATTATCGTTAA +ATAATTTACATAAACTCATATAATCTAAAGAATATGGCTTTAGAAGTTTCTACCATGTTGCCTTGAACGACATGACTATG +AGTAACAACACAATACTAGGAGTAGCTTCAGCCATTAAATTGTAACCATGGTGGGTGATTTATATCATTTTATATGATGG +TCACAGTTTATTTGATGAAACTTCTTTTACATTGATTGCATGACAAATTCGTTATGCATGTTCGTTCACTCATAAACCCT +GAAACTATTATTTAGTTTGGGGATTTTTTTGTATCTAGCACCAATTTAAGAGCAAAATGTTTCACACAAATCTGAGGAGG +TTTTAAGAGTGGAGTTACTAGGACAAAAAGTAAAGGAAGACGGCGTTGTCATTGATGAGAAGATTTTAAAAGTCGATGGA +TTTTTAAATCATCAAATTGATGCAAAGTTAATGAATGAAGTTGGTCGCACTTTTTACGAGCAATTTAAAGATAAAGGGAT +TACTAAAATCTTAACCATTGAAGCTTCCGGTATCGCACCTGCAATCATGGCTGCACTGCATTTTGATGTGCCATGTTTAT +TTGCGAAAAAAGCAAAACCTAGCACTTTGACGGATGGTTATTATGAAACATCTATTCATTCATTTACTAAAAATAAAACA +AGTACGGTCATTGTTTCAAAAGAGTTTTTATCAGAAGAAGATACTGTACTTATCATCGATGACTTTTTAGCAAATGGTGA +TGCTTCATTAGGATTATACGATATCGCACAGCAAGCGAATGCTAAGACAGCTGGTATTGGTATTGTTGTTGAAAAGAGTT +TCCAAAATGGGCATCAACGTTTAGAAGAAGCAGGTTTAACAGTTTCTTCTCTCTGCAAGGTTGCTTCACTAGAAGGAAAC +AAAGTGACATTGGTGGGAGAAGAATAATGAAAAATTTAATCCTAAGTGTTCAACATCTTTTAGCTATGTACGCAGGTGCT +ATCTTAGTTCCAATCATTGTTGGTACAAGTTTGAAGTTTACACCTGAACAAATCGCTTACTTAGTTACAGTAGATATATT +TATGTGTGGGGTTGCCACATTTTTACAAGCCAATAAAGTAACAGGAACAGGATTACCAATCGTTCTTGGATGTACATTCA +CGGCTGTTGCGCCCATGATTTTAATTGGTCAAACGAAAGGAATAGATGTACTTTATGGTTCGCTATTTTTATCAGGGATA +TTAGTTATTATCATCGCGCCTTTCTTTTCACATCTTGTAAAATTCTTCCCACCAGTAGTAACGGGTAGTGTTGTTACTAT +CATTGGTATCAATTTAATGCCAGTAGCAATGAATTACTTAGCTGGAGGTCAAGGTGCAAAGGACTATGGAGATGTTAAGA +ACATTTTGTTAGGTTTAATGACATTAATCATTATTCTTCTTTTACAAAGATTCACAACTGGATTTATTAAGAGTATTGCC +ATATTAATTGGACTCGTTTTAGGAACGATAGGTGCTGGCTTACTTGGGATGGTCGATATTAATCAAGTCAATCATGCCGG +TTGGTTAGGCATCCCAGTGCCGTTTAGATTCTCTGGATTTAGCTTTGATGTGACATCGACGTTAGTGTTCTTTATTGTAG +CTATCGTTAGTTTAATTGAGTCGACAGGTGTCTATCATGCGTTAAGTGAAATTACCGGTAAGAAGTTAGAAAGAAAAGAT +TTTCGTAAAGGTTATACTGCGGAAGGTCTAGCGATAGTGTTAGGTTCTATATTCAATTCATTTCCGTATACAGCCTATTC +GCAAAATGTAGGACTTGTTTCTTTATCCGGCGCTAAGAAAAACAATGTTATATACGGCATGGTCGTGTTATTACTTATAT +GTGGTTGTATACCTAAGCTTGGCGCATTAGCAAATATCATACCGCTACCTGTGTTAGGCGGTGCGATGATAGCTATGTTT +GGCATGGTAATGGCATATGGTGTTAGTATATTAGGACATATCGATTTTAAAAATCAAAACAATTTATTAATTATCGCTGT +ATCAGTAGGATTAGGTACTGGTATAAGCGCTGTACCACAAGCATTTAAAGGTTTAGGTGAACAATTTGCATGGTTGACTC +AAAACGGAATTGTTTTAGGCGCAATCTCTGCAATTATTCTTAATTTCTTTTTTAATGGAATAAAGTATAAACAAACGGAA +GAAAATGTGAAATAATATAACTAATTAATTTGAAAAATGGAGGCTGTTTTTAATGTGGGAAAGTAAATTTGCAAAAGAAT +CATTAACGTTTGATGATGTGTTATTAATTCCAGCACAATCTGATATTTTACCGAAAGACGTTGATTTAAGCGTACAATTA +TCAGACAAAGTTAAATTAAATATTCCAGTTATTTCTGCTGGTATGGATACTGTAACTGAATCTAAAATGGCGATTGCTAT +GGCTCGTCAAGGTGGTTTAGGTGTTATTCATAAAAATATGGGCGTTGAAGAACAAGCGGACGAAGTTCAAAAAGTAAAAC +GCTCAGAAAATGGTGTCATTTCAAACCCATTTTTCTTAACGCCAGAAGAAAGCGTTTATGAAGCAGAAGCATTAATGGGT +AAATACCGTATTTCAGGTGTACCAATTGTTGATAATAAAGAAGATCGCAACTTAGTAGGTATTTTAACAAACCGTGACTT +ACGTTTTATTGAAGACTTCTCGATTAAAATTGTAGATGTAATGACGCAAGAAAATTTAATTACAGCTCCAGTGAATACAA +CACTTGAAGAAGCAGAAAAAATTCTCCAAAAACATAAGATTGAAAAGTTACCATTAGTTAAAGACGGACGTCTAGAAGGT +CTTATTACTATTAAAGATATTGAAAAAGTTATCGAATTCCCTAATGCAGCAAAAGATGAACATGGTCGTCTACTTGTAGC +CGCAGCAATTGGTATTTCAAAAGATACTGATATTCGTGCTCAAAAATTAGTCGAAGCAGGTGTGGATGTCTTAGTTATCG +ATACAGCACATGGTCACTCTAAAGGTGTTATCGATCAAGTGAAACATATTAAGAAGACTTACCCAGAAATCACATTAGTA +GCAGGTAACGTAGCAACTGCAGAAGCAACAAAAGATTTATTTGAAGCGGGTGCAGATATTGTTAAAGTTGGTATTGGCCC +AGGTTCAATTTGTACGACGCGTGTTGTAGCAGGTGTTGGTGTACCACAAATTACAGCAATTTATGATTGTGCAACTGAAG +CACGCAAACATGGTAAAGCTATCATTGCTGATGGTGGTATTAAATTCTCAGGAGATATCATTAAAGCATTAGCTGCTGGT +GGACATGCGGTTATGTTAGGTAGCTTATTAGCAGGTACTGAAGAAAGCCCAGGCGCAACAGAAATTTTCCAAGGTAGACA +ATATAAAGTATACCGTGGTATGGGCTCTTTAGGTGCGATGGAAAAAGGTTCAAACGACCGTTACTTCCAAGAGGACAAAG +CGCCTAAGAAATTTGTTCCTGAAGGTATCGAAGGACGTACGGCTTATAAAGGTGCGTTACAAGATACAATTTACCAATTA +ATGGGCGGTGTGCGTGCTGGTATGGGTTATACTGGTTCACACGATTTAAGAGAATTACGCGAAGAAGCACAATTTACACG +TATGGGTCCTGCTGGTTTAGCAGAAAGCCATCCACATAATATTCAAATTACGAAAGAATCACCGAACTACTCATTCTAAT +TAAGATAAAGGAGAACGACAAATATGGAAATGGCAAAAGAACAAGAGTTAATCCTTGTCTTAGACTTTGGTAGCCAATAC +AACCAATTAATTACACGCCGAATTCGTGAAATGGGCGTTTATAGTGAATTACACGATCATGAAATTTCAATTGAAGAAAT +TAAGAAAATGAATCCAAAAGGTATTATCTTATCAGGTGGTCCAAATTCAGTTTATGAAGAAGGTTCATTTACAATTGATC +CGGAAATATATAATTTAGGAATTCCAGTACTTGGTATTTGTTACGGCATGCAATTAACTACTAAATTATTAGGTGGTAAA +GTTGAACGTGCCAATGAACGTGAATACGGTAAAGCAATCATTAATGCGAAGTCAGATGAGTTATTCGCTGGCTTACCAGC +AGAACAAACTGTTTGGATGAGTCATTCTGATAAAGTTATTGAAATTCCAGAAGGCTTTGAAGTTATCGCTGATAGCCCAA +GCACAGACTATGCAGCAATCGAAGATAAGAAACGTCGCATTTATGGTGTTCAATTCCATCCAGAAGTACGTCATACAGAA +TATGGTAATGATTTATTAAATAATTTTGTCCGTCGTGTTTGTGATTGTAGAGGTCAATGGACAATGGAAAACTTTATCGA +AATCGAAATTGAAAAGATTCGTCAACGCGTAGGAGACCGTCGTGTATTATGTGCGATGAGTGGCGGCGTAGATTCATCTG +TTGTAGCTGTACTATTGCATAAAGCAATAGGTGATCAACTAACATGTATCTTTGTAGACCATGGCTTACTTCGTAAAGGT +GAAGGCGACATGGTTATGGAGCAATTCGGTGAAGGTTTCAACATGAATATTATTCGTGTTAATGCGAAAGATCGCTTTAT +GAATAAATTAAAAGGTGTTTCAGATCCTGAACAAAAACGTAAAATCATTGGTAATGAATTTGTATACGTATTTGATGATG +AAGCATCAAAACTGAAAGGTGTAGACTTCCTTGCGCAAGGAACACTATATACAGACGTCATCGAATCAGGTACTAAAACA +GCACAAACAATCAAATCACACCACAATGTTGGTGGATTACCAGAAGACATGGAATTCGAATTAATCGAACCAATCAATAC +ATTGTTTAAAGATGAAGTACGTAAATTAGGTATTGAGTTAGGTATTCCAGAACATTTAGTATGGAGACAACCATTCCCAG +GACCTGGTCTTGGTATTCGTGTACTTGGAGAAATTACTGAAGATAAACTAGAAATCGTTAGAGAATCAGACGCGATTTTA +CGCCAAGTGATTAGAGAAGAAGGTCTTGAAAGAGAAATTTGGCAATACTTCACAGTGTTACCAAACATTCAATCAGTAGG +TGTTATGGGAGACTACCGTACGTATGATCACACAGTAGGTATTCGTGCAGTAACATCTATCGACGGTATGACAAGTGACT +TCGCACGCATCGATTGGGAAGTCTTACAAAAGATTTCTAGTCGTATCGTAAACGAAGTAGATCACGTCAACCGCGTAGTC +TATGACATTACATCAAAACCACCAAGCACAATTGAGTGGGAATAATTATATATAATAAAACCACCTGTTTGCACGGGTGG +TTTTTAATTTGAGTAGTTGGAAAATTACAATAAGGACGGAAAAGGGGTAAGGTGTTCTAAGTTTGCGTTTTAATTCATCA +ATAAGGCAATTTTCCGTGTTTATTTTTATATAAAATTTCATATTGTTTAGGATCTTCTTGTTTTAAATCATAAATTATAG +TTTCGTCTACTATTTTTTCTAATTCAAGTGTATTAAAGCTCCAGTCATTTGTTGTTATTATAAATATTTTTTGGACTGGG +TTGTTATTTATATGATTAATTAGCCTAGGAGTATCTGTTTCTATTATTAGAGCAGATTCTACAGGATAGTAAATAGCAAA +GTCTCCGTGATAAATTGTGTTTCTTTCATAGAGGTTAACCTCTAAGGTTAAATTACTCAATTTGAAGATTGTGTTTTGAG +TTATATTTTTAGGAATAATTCGATTATCTACTATTTGTTTTAGGTTGCCTATTGAACGAGTGTGTATCGTCCCTTTTGTG +TAATCGGAATTATTTAAAGCTCTTAAAACAGTTGTTAATTTATACTCGTTAACATAACCCTTTACTAAAGCCATCCGTTT +ATCGCTACCTATAAAATCTTTCAGTTTTTTGTTGAATTCATTATTGTTGTGCATAATATTTTATCCTTTCCATTAGTTGA +TTGTTTTCATTTGTTCAAATCTCTATCCATTTACTCGGTTACAGGGCTGTGTATTTGCAGAGTAATATTATGATTGAAAT +AACCAACGCGTTCCGTATTCGTTTTGAGTGACGCAGCTAGCTGCGATAATAGTGACATGTGGCTTATAACACATTGTGTA +ATTTATTGTGTGTTTTGTGATGTCTAATATTGATTGCAGATTGTTGTATATTTCACTTAAAGGGCATTTATATAAAAAAA +GAGGCCAGATGACGTATCATCTGACCTTCAGCTAAAACGCTGAGAATATGTCATTCTTCAAGAGAATGAAGCCGGAATGG +TTACCGGGAATTTGTAAATCAATTATATCAGATGTAATATTACAATTCAATTAGAAAAGAACTTTATCTTTAATTGCTTT +TCGATTTCATTCATGTCATCTTCTGAAATTATTATCGTTCCAGTAGGATCAGAATCATTTATTTTGCTTATACGTCTTTT +GCTTATTGTTGTAATTGCTGATATATTAACGTATGTATCTTTCCCTTTGTGCTTGGAATAAACACCTATCACTCTATACA +GTTCATTAGCTTTTTGATTTAAGTTAGACAAAAGCTTTTTATCTTCTGTGCTAAGTTTAATGTCAATTTCTCTATATTTT +TGGGCTAATGCTTGTACTTTACTACTGATATCGATAATCTGTTTTTTTAAATCTGTTGCTGTTAGATTTAGCACCGATTC +GTTTAACTTCAAATAATTTTTATTACCTTTGGAAGAAAGTGGAACTATTGTAACTGTTTCTTTTCCTTTATTGTCTTTGT +TATCTAATATTACACAAAAATGATTACCAGAAAACTCACTTCCAATATTACTCCCTAGTTTTACATATACCACTGTTCCT +CTACTATATGATTTATAATATCTTTTTTTATTGCTTGTAACATCGCTATGTATAGCAATTGAATAAAACTCTAGCCAATG +AGGCATGTATATAACTTTCATGTTTTTACTGTCGGATTTTGAATAAGCTTGTTTTAGTTTTTTGTTTGACGTATTTAATT +TACTATTTGCTTGATTGATATTTTTAGACATTAAATAGTGTTCTCCTAATCAATTTATTCTTCTTTATATCAGCATTATT +AGCACTGAAATTAACATGAATGGTACGAAAATAGATGTTACTATAAAACCAACAATTTTTATTAGGTGGAACTCATCAAT +TTTGTATTTAGCAAATTTCGCAATGACCTGCTATTTATTCGCTTTTTATAGTTCATGTATGTGTAAAAAAGTCTATATTG +AACATCTAATTATAAGCGAATTCCTAATGCTAAAAGCAATCTAACTTACTTAAATATTATTATGAGTTTTTATTGAAATT +AAATAAATTTAAACTATTTCTTAGTCTCTCCATATCGTTGGAAGTTGGTTCTTTATATAATCAGATAAATTAGAATCTTC +AAGTAATGTATCTTTAAAAAGTTCAGTTTCTAAATCAACTTTAAAATTTGCTTTAAAACATCATGTTCCATTTTTAAATG +TTGAACTTCTTTGCGTAATTTTATTAACTCTTTTTCATCACTTTTTAAATTATCTTGATGATTGAAGGGTCCAGTATTTT +GGTGTTGTTTTATCGAATTTGAGATAATTGAGGGTTTTAAATCATACTTTCGTATAATTTCATTCTTAAGCTTACCATTT +TTATATAATCTAACCATTTGTAACTTAAACTCTGAACTAAATGTTCTTCTTTCTTTTGTCATAATAAAATTGACTACTTT +CTTAATTTAACAATATCTATTCTCATAGAATTTGTTCAATTAAGTGTAGACGATTCACTGACACATCATAAGTACGTAAG +GAATTTTGATCGTTCATTGTTATTGCTTTATGTTTTAGGGAATGTGCTATTGAAGGGTTTAAAGATGTTTGAAATGTCTT +GGTTTCTTGACTTATATAGTTATGAAAGAGGGCGTTGTCCTTTATAGCATTTCTGAGATTAGAAATGCTATAAATAAAGT +GATTAAGATTTTTTGTTTTCATGTAATTTAAGAAATGGATTTGTTTTTTGTGGGTTATTTTTAATTCTGTTCTTAAATGG +TTTGAATTTTGCGATTGCAAATATGATAGCTGCTTCTTTTGACAAAGTAATAGAACCTTTTTCTTCTGGTACCATGATAA +TCACTCCTTGTGCAATTGGATTTAAATATTATAAAATAATTTTACTATACAAAATATATTGTGTAAATAGTTTCTTATTG +TTACCAAATAATATGTTTGGAAGCGTTATGTAATGCCAATCAACACCGAAGTTCTCATGTCCTCTTTCCTTTTACGACTA +CATATATTGTTAATAATATTGAAATAATAAACTGATAGTGATAATATTTATTTAACTGATCCATGAGAGTAGGAACCCCA +TCATACGGGAATTTCGAAATCATGGGTTTTTTGTTTTAAAGGATTGATGCAATAGATAAAGTAGCCATTCTAATATATGG +AGTATTTATCAGCTGAAAATTAAATCAGATATGGGTTTTCTTCATTAAATAATTTTTTTATAAGGAAACAGAGGCAACGC +TACTGTCACCTCTGCTTGAAATAATTAGGATGAAATACTCTGAGTCTAAAGTCCACACACAATTCGCTCAAATCCTCCAA +ACCTCCGACTACATCTCCACCCCCCCAAACACCAATACTTACCAAAGCATTGATATGAGTGCCAATACTGGCAATGTGCC +TTGTTTGAAAAAGATACTAATATTGCTTGAAAGGCCACCATAAATAGCAACGCCAATGATATACACTAAAATAGCTGCGC +ATATTTCTTTTGGATTACTGCTGATAAACAAACCGTATATTAGCAAAACTCCGATTAAACCGTTATATACGCCTTGGTTC +TTCAAAAGTAGGTTAATATTTTTGTCTTTCAATTTATCGACGCTTATATTAAATGTCTCGCTAGTCTTTTTGGAAGTTGT +AGCAATCGTTTCAAGGTACATAATATAGAAAAACTCTAATGCCACAAATATGATTAAAATTGTTGAGATGATATTCACTA +TAACGCTCCTTTATTATTAAATATTTTCTTGTAAAAATGATTGCAGTGTCTGTGGTTGATCATTGACTAATTGTTGGAAA +TCATTGGATTCTTGGTCTAATAGTCCTCTTGCTCCTGCGTCGTACATTGATGCCAATAATGCACCAAAGCCTTTAGGTTC +ATCATACATTTCTGCAAATGTCTCTAATGAAACGGGCTCATATTTAATTTCTGTGCCTGATGCCTCAGATAAAATTGCAG +CAAGTTCTTTCATATCATAACTGTAGCCTGATAATAAGTAGCGTTTGCCCCAAGTATCTGGATTTTTAATAATAGCAATG +ACACCTCTAGCAATATCATTTCTAGTAATATAATTAATACGACCATCGCCAGCTGGATAAATCAGTTTATGCATATTCAT +CAATTCTGGTAAATATGGTTTAAGTGGATCCATGTACATTGCCATTCTTACATACGTATAGTCAATGCCACTTGTTGCCA +ATAGACGTGCTGCATAACCAAAATAAGGACTCATATGGAATGGATTATTATGCTGATCTGCGTAATAACCTATGAAAATG +ATATGAGCAACGCCGCTCTGCTTTGCCGCATATACTAAATTTTCCACTTCAGGAATACGTTTGAATGATGGATGGATAAT +ACTTGGAATAAACACAACGGTATCCATTCCTTTAAATGCTTCTACCATGCTTTCTTGATTAAAATAATCTAATTGTCGAA +CAGGAACTTTTCCGCGCCAATCTTCTGGAACTTTCTCAACATTTCTAACACCAATGTGAAAATGATCTATGTGATTTGCA +ATGGCTTGATTTGTAATATGTGTGCCTAAATGACCTGTAGCACCTGTTAACATAATATTCATTCACTTCATCTCCTAATC +TTTATATACATAACATAATACTTATTTGATGGTTTTCAAAACATTTGATTTTATAAAAAATTCTAATCTGTATTTATTGT +CGACGTGTATAGTAAATACGTAAATATTATTAATGTTGAAAATGCCGTAATGACGCGTTTTAGTTGATGTGTATCACTAA +TATCATTGAAAATTTTAATCAGGTACTACGACAATATGATGTCTGTTTTGTGTCTGAAAGTTTTACAGTTTTTAAAATAA +AAATGGTATAAAGTGTGATTTGTATAAAAAAGAGTCTCGACGGATAAGAATTGATTAATAACAGTTAGCATTTTATTAAT +TACCTTAACAATGATTCAAGTTTAGTTAAATGAGGTTTAATTTGAAAGGGGATAGCGCCTCAATATAATGTAGGTAGATT +GTTCATATTACGTAATTGAAAAATCAAATTTAAATAGATTGGGGCTAAAAATTATGAAATTTAAAGCGATAGCAAAAGCA +AGTTTAGCATTGGGAATGTTAGCAACAGGTGTAATTACATCGAATGTACAATCAGTACAAGCGAAAGCAGAAGTTAAACA +ACAAAGTGAATCAGAGTTAAAACACTATTATAATAAACCAATTTTAGAGCGTAAAAATGTGACTGGATTTAAATATACTG +ATGAGGGTAAACACTATTTAGAAGTCACAGTAGGGCAACAGCATTCTCGAATCACTTTACTTGGATCTGATAAAGATAAA +TTTAAAGACGGAGAAAACTCAAATATAGATGTGTTTATCCTTAGAGAAGGTGACAGTAGACAAGCAACAAATTACTCAAT +TGGTGGCGTTACAAAATCAAATAGTGTGCAGTATATTGATTATATCAATACGCCAATTTTAGAAATCAAGAAAGATAATG +AAGATGTACTTAAAGATTTTTACTACATTTCAAAAGAAGACATCTCATTAAAAGAACTTGATTATAGATTAAGAGAACGT +GCGATTAAACAACACGGCTTGTATTCAAATGGTCTTAAACAAGGTCAAATTACAATTACAATGAATGATGGCACAACACA +TACAATCGATTTAAGTCAAAAACTTGAAAAAGAACGTATGGGTGAGTCAATCGACGGCACTAAGATTAATAAAATTCTAG +TAGAAATGAAATAATACTTTCTAACAACAAAGCGCTATGTTGAATAGTGCTTGTTATGGAAATATATGGAAGTTAAGCGA +CGTACTGTTGCTTAGCTTCTTTTTTTGAGGGGAAAAGTTACAAAACTCACACAAACAGTCGCACCACGCATTATCTTTTG +CTTAAATAGCTTAATCATATTTTATGAATAGTTAAAAACAGGTTAATGTGAATATCCGAATACAGCTCCTATAATATGGG +TGTATGATTCAAATTACGTAATAAAACAATCTAATTATAATAGATTGGAGCATACAACTATGAAAATGAAAAATATTGCA +AAAATAAGTTTGTTATTAGGAATATTAGCAACAGGTGTAAACACTACAACGGAAAAACCAGTTCATGCCGAAAAGAAACC +TATTGTAATAAGTGAAAATAGCAAAAAATTAAAAGCTTATTATAATCAACCTAGTATTGAATATAAAAATGTGACAGGTT +ATATCAGTTTCATTCAACCAAGTATTAAATTTATGAATATCATAGATGGTAATTCTGTTAATAATATTGCTTTAATTGGC +AAAGATAAGCAACATTATCATACGGGTGTACATCGTAATCTTAATATATTTTACGTTAATGAGGATAAGAGATTTGAAGG +TGCAAAGTACTCTATTGGGGGTATCACGAGTGCAAACGATAAAGCTGTCGACCTAATAGCAGAAGCAAGAGTTATTAAAG +AAGATCATACTGGTGAATATGATTATGACTTTTTCCCATTTAAAATAGATAAAGAAGCGATGTCATTGAAAGAGATTGAT +TTTAAATTAAGAAAATACCTTATTGATAATTATGGTCTTTACGGTGAAATGAGTACAGGAAAAATTACAGTCAAAAAGAA +ATACTATGGAAAGTATACATTTGAATTGGATAAAAAGTTACAAGAAGACCGTATGTCCGATGTTATCAATGTCACAGATA +TTGATAGAATTGAAATCAAAGTTATAAAAGCATAACACATATACTTGATGACGAAATAAGTTGAAATTGAAATAGAGAGG +TTAAGTGACGATCAAACGTTGCTTAACTTCTTTTTAATGCTTAAAAATTATTTCAAAGGCACATAGAAACGCTATATTAA +TCTCATACTCACTCATTATTTTTTGCTTAAATTACTTAATAATACTTCAATAATTGTTAAAAGGGGTTTAATGTGATTAT +CTTAGAACGCCATCTATAATGATGTTGTATGATTCAAATTACGTAAAAAGACAATCGAATATAATATAGATTGGAGCATA +CAATTATGAAAATGAGAACAATTGCTAAAACCAGTTTAGCACTAGGGCTTTTAACAACAGGCGCAATTACAGTAACGACG +CAATCGGTCAAAGCAGAAAAAATACAATCAACTAAAGTTGACAAAGTACCAACGCTTAAAGCAGAGCGATTAGCAATGAT +AAACATAACAGCAGGTGCAAATTCAGCGACAACACAAGCAGCTAACACAAGACAAGAACGCACGCCTAAACTCGAAAAGG +CACCAAATACTAATGAGGAAAAAACCTCAGCTTCCAAAATAGAAAAAATATCACAACCTAAACAAGAAGAGCAGAAAACG +CTTAATATATCAGCAACGCCAGCGCCTAAACAAGAACAATCACAAACGACAACCGAATCCACAACGCCGAAAACTAAAGT +GACAACACCTCCATCAACAAACACGCCACAACCAATGCAATCTACTAAATCAGACACACCACAATCTCCAACCATAAAAC +AAGCACAAACAGATATGACTCCTAAATATGAAGATTTAAGAGCGTATTATACAAAACCGAGTTTTGAATTTGAAAAGCAG +TTTGGATTTATGCTCAAACCATGGACGACGGTTAGGTTTATGAATGTTATTCCAAATAGGTTCATCTATAAAATAGCTTT +AGTTGGAAAAGATGAGAAAAAATATAAAGATGGACCTTACGATAATATCGATGTATTTATCGTTTTAGAAGACAATAAAT +ATCAATTGAAAAAATATTCTGTCGGTGGCATCACGAAGACTAATAGTAAAAAAGTTAATCACAAAGTAGAATTAAGCATT +ACTAAAAAAGATAATCAAGGTATGATTTCACGCGATGTTTCAGAATACATGATTACTAAGGAAGAGATTTCCTTGAAAGA +GCTTGATTTTAAATTGAGAAAACAACTTATTGAAAAACATAATCTTTACGGTAACATGGGTTCAGGAACAATCGTTATTA +AAATGAAAAACGGTGGGAAATATACGTTTGAATTACACAAAAAACTGCAAGAGCATCGTATGGCAGACGTCATAGATGGC +ACTAATATTGATAACATTGAAGTGAATATAAAATAATCATGACATTCTCTAAATAGAAGCTGTCATCGGAAAAACAAGAA +GTTAAGTGACAACGGTTTACATGTTGCTTAGCTTCTTTTATTATGCGTAATGATGTAAAAAGACGAATATTCATTTGTTT +GTAAAAGTGGCATTTCTATGTCTTAAAAGTGACGAAACTTCAAATGTGCCAAGTGTTGAATCACATCAAAATCATTTTTA +TTTAACGAACATTATGGATTTCTTAATTTACTTAACGATGATTCAAATATAGTTAAACAAGGTTTAATGTGAATGGAGCA +ATACGCCATCTATAATAAAGCTGTATGATTCAATGAATGTAATCGAACAAATCTAATAATTACGAATGGAGCATACAACT +ATGAAAATAACAACGATTGCTAAAACAAGTTTAGCACTAGGCCTTTTAACAACAGGTGTAATCACAACGACAACGCAAGC +AGCAAACGCGACAACACTATCTTCCACTAAAGTGGAAGCACCACAATCAACACCGCCCTCAACTAAAATAGAAGCACCGC +AATCAAAACCAAACGCGACAACACCGCCCTCAACTAAAGTAGAAGCACCGCAACAAACAGCAAATGCGACAACACCGCCT +TCAACTAAAGTGACAACACCTCCATCAACAAACACGCCACAACCAATGCAATCTACTAAATCAGACACACCACAATCGCC +AACCACAAAACAAGTACCAACAGAAATAAATCCTAAATTTAAAGATTTAAGAGCGTATTATACGAAACCAAGTTTAGAAT +TTAAAAATGAGATTGGTATTATTTTAAAAAAATGGACGACAATAAGATTTATGAATGTTGTCCCAGATTATTTCATATAT +AAAATTGCTTTAGTTGGTAAAGATGATAAAAAATATGGTGAAGGAGTACATAGGAATGTCGATGTATTTGTCGTTTTAGA +AGAAAATAATTACAATCTGGAAAAATATTCTGTCGGTGGTATCACAAAGAGTAATAGTAAAAAAGTTGATCACAAAGCAG +GAGTAAGAATTACTAAGGAAGATAATAAAGGTACAATCTCTCATGATGTTTCAGAATTCAAGATTACTAAAGAACAGATT +TCCTTGAAAGAACTTGATTTTAAATTGAGAAAACAACTTATTGAAAAAAATAATCTGTACGGTAACGTTGGTTCAGGTAA +AATTGTTATTAAAATGAAAAACGGTGGAAAGTACACGTTTGAATTGCACAAAAAATTACAAGAAAATCGCATGGCAGATG +TCATAGATGGCACTAATATTGATAACATTGAAGTGAATATAAAATAATCATGACATTCTCTAAATAGAAGCTGTCATCGG +AAAAACAAGAAGTTAAGTGACAACGGCCTACATGTTGCTTAGCTTCTTTTGTTATGTTCGATGATTTGAGAACCCGAATT +TTCGATGGGTCCAAATATGACGTGGAAGAGACCTGAATTTATCTGTAAATCCCTATCTATCGGGTGTGAAGCACAACGGG +ATCAGTTTTATTTAACGAACATTATAGATTCCTTAATTTACTTAATAATGATTCAATGATTATTAAACATGGTTTAATGT +GAAAGGTCAAATACGCTAACTATAATAAAGCTGTATGATTCAATAGACGTAAGCGAACAAATCTAATAATTACGAATGGA +GCATACAACTATGAAAATGACAGCAATTGCGAAAGCAAGTTTAGCATTAGGTATTTTAGCAACAGGAACAATAACGTCAT +TGCATCAAACTGTAAATGCGAGTGAACATAAAGCAAAATATGAAAATGTGACAAAAGATATCTTTGACTTAAGAGATTAC +TATAGTGGCGCAAGTAAGGAACTTAAAAATGTTACTGGTTATCGTTATAGCAAAGGTGGCAAGCATTACCTTATCTTTGA +TAAAAATAGAAAATTCACAAGAGTACAGATATTTGGTAAAGATATTGAAAGATTTAAAGCACGTAAAAATCCGGGATTAG +ACATATTTGTTGTTAAAGAAGCGGAAAACCGTAATGGCACAGTGTTTTCATATGGTGGTGTCACTAAGAAAAATCAAGAC +GCTTATTATGATTATATAAACGCACCAAGATTTCAAATCAAGAGAGATGAAGGTGACGGTATTGCTACGTACGGTAGAGT +ACACTACATTTATAAAGAAGAGATTTCACTTAAAGAACTCGACTTTAAATTGAGACAGTATTTAATTCAAAATTTTGATC +TGTATAAAAAGTTTCCTAAAGATAGTAAGATAAAAGTGATAATGAAAGATGGCGGCTATTATACGTTTGAACTTAATAAA +AAATTACAAACAAATCGCATGAGTGACGTCATTGACGGTAGAAATATTGAAAAAATAGAAGCCAACATTAGATAATTCAA +TGAAATATGGATAATAGTAAAATATGGATAGTATAGAGGAGTTAGGCAACATAAGTTGCTTAGCTTCTTTTTTGTGTTGG +AGAGATGAAAATGAAGCGTATCGATGAATAATAAAAACACCAATAAAACTTGTGGAAATAGTTGATACTTATAGTCGCGC +GTTGTCCTTTTCGTGACATGAAACAATGTGGAAAACATAGTTAAATTGAGGGAAAGTGTGAATAGTTAAAAAAGCTGCGT +TAAGTTTAAAAAATAGATTAACGCTGTTAGGATTCCATTAATTAGCTTAACATTGGTTCAAAAATAGTTAAAAAGAGGTT +AATTCATAGCTTAGTATTACGCTTATATAATGATAGTAGATTGTTCGTATTACGTAATTGAAATAATCATATAAAAATAT +ATTAAGACAAAATTTATAAATAGATTGGGAGAATAGTACTATGAAATTAAAAACGTTAGCTAAAGCAACATTAGTATTGG +GATTGTTAGCTACTGGTGTAATAACAACAGAAAGTCAAACAGTAAAAGCGGCAGAATCAACTCAAGGTCAACACAATTAT +AAATCGTTAAAATACTACTATAGCAAGCCAAGTATAGAGTTAAAAAATCTTGATGGTTTGTATAGACAGAAAGTGACAGA +TAAAGGAGTATATGTTTGGAAGGATCGAAAAGATTATTTTGTTGGCTTGCTTGGTAAAGATATTGAAAAATACCCTCAAG +GTGAGCATGATAAGCAAGATGCATTTTTAGTCATCGAGGAGGAAACTGTTAATGGAAGACAATATTCAATTGGTGGTTTA +AGTAAGACAAATAGTAAAGAATTTAGTAAAGAAGTCGATGTTAAAGTAACAAGAAAAATTGATGAATCATCGGAAAAGTC +TAAAGATAGTAAATTTAAAATTACTAAAGAAGAAATCTCGTTAAAAGAGTTGGACTTTAAATTAAGAAAAAAATTGATGG +AAGAAGAAAAATTATATGGTGCTGTTAATAATAGAAAAGGTAAAATTGTAGTTAAAATGGAAGATGATAAGTTTTATACT +TTCGAACTTACAAAAAAACTACAACCGCATCGCATGGGTGACACGATAGATGGTACCAAAATCAAAGAAATTAATGTTGA +GCTAGAATATAAATAATCTTTGGACAAGCAGACTAGTAATTGTAGGGAAGTTAAGCGATAACATATTGCTTAGCTTCTTT +TTTATTTTGTTATGATGAAAAAAGGAGCGGGTTTATGATCAAGTTTTTGGAAAAACGGTTGATACTTATAGTCGCGCGTT +GTCCTTTTCGTGACATGAAACAATGTGGAAAACATAATTAAATTGAGGGAAAGTGTGAATAGTTAAAAAATTAGTATTGT +GTTATAAAAAATAATTAATACTGTTAGGATTTCATTAACTAACTTAACGTTGGTTCAAAAATAGTTAAAAAGAGGTTAAT +TCATAGCGCAGTATCTCACTTATATAATGATAGTAGATTGTTCGTATTACGTAATTGAATTAATCATATAAAAATATATT +AAGACAAATTTATAAATAGATTGGGAGAATAGTACTGTGAAATTAAAAACGTTAGCTAAAGCAACATTGGCATTAGGCTT +ATTAACTACTGGTGTGATTACATCAGAAGGCCAAGCAGTTCAAGCAAAAGAAAAGCAAGAGAGAGTACAACATTTATATG +ATATTAAAGACTTACATCGATACTACTCATCAGAAAGTTTTGAATTCAGTAATATTAGTGGTAAGGTTGAAAATTATAAC +GGTTCTAACGTTGTACGCTTTAACCAAGAAAATCAAAATCACCAATTATTCTTATTAGGTAAAGATAAAGAGAAATATAA +AGAAGGCATTGAAGGCAAAGATGTCTTTGTGGTAAAAGAATTAATTGATCCAAACGGTAGATTATCTACTGTTGGTGGTG +TGACTAAGAAAAATAACAAATCTTCTGAAACTAATACACATTTATTTGTTAATAAAGTGTATGGCGGAAATTTAGATGCA +TCAATTGACTCATTTTCAATTAATAAAGAAGAAGTTTCACTGAAAGAACTTGATTTCAAAATTAGACAACATTTAGTTAA +AAATTATGGTTTATATAAAGGTACGACTAAATACGGTAAGATCACTATCAATTTGAAAGATGGAGAAAAGCAAGAAATTG +ATTTAGGTGATAAATTGCAATTCGAGCGCATGGGTGATGTGTTGAATAGTAAGGATATTAATAAGATTGAAGTGACTTTG +AAACAAATTTAAAGTAAGTAATCAATGACTCTAAAGTAATAAATTTGAAGCAGCTTAACGATGAAATGTTGAATAGATAC +GTACACCTTACATTAAGGAGCGTATTTAAAACAACCTTGTCGTTAGGCTTTTTTTTACGTTTTATAACGCAGGTTATGAG +TGAGCATACTAAAAATTTACATTGCTCTTGAAAGTGATGCCCATTGAATATTAATTAGCTCTTCATTAACAATGATTTAA +GTTTAATTAAACGAGTGTTAATGTCAGTCTGTCTCAATGCCCTTTATAATAAAGGTGTATTATTCAAATTACGTAATAAA +AGCAATCCAATATATTAAGATTGGAGCACATGAATATGAAATTTACAGTGATAGCTAAAGCGATATTTATATTAGGAATA +TTAACAACAAGTGTAATGATAACAGAAAATCAATCGGTTAATGCAAAAGGAAAGTATGAAAAAATGAACCGTTTATATGA +TACAAACAAGTTACATCAATACTATTCAGGACCTAGTTATGAGTTAACAAATGTTAGTGGCCAAAGTCAAGGTTATTATG +ACTCTAACGTTTTGCTTTTTAACCAACAAAATCAAAAGTTCCAAGTGTTTTTATTGGGAAAAGATGAAAATAAATACAAA +GAAAAAACACATGGTTTAGATGTCTTTGCGGTACCAGAATTAGTAGATTTAGATGGAAGAATATTTAGTGTTAGTGGTGT +AACAAAGAAAAACGTAAAATCAATATTTGAGTCTCTAAGAACGCCGAACTTACTAGTTAAAAAAATAGACGATAAAGACG +GTTTTTCTATTGATGAATTTTTCTTTATTCAAAAGGAAGAAGTGTCATTGAAGGAACTTGATTTTAAAATAAGAAAACTG +TTGATTAAAAAATACAAACTGTATGAAGGGTCAGCTGATAAAGGTAGAATTGTTATTAATATGAAAGATGAAAATAAGTA +TGAAATTGATTTAAGTGATAAATTAGATTTCGAGCGTATGGCAGATGTCATTAATAGTGAACAAATTAAAAACATCGAAG +TGAATTTGAAATAATCAATGATATATATAGAATGAAAGCTTAAGAAGCGGTTTAATAATCCCATGTTTAATGATTTTGAT +ACGTGTTTTTATAATAAAAACATATCGAACATTGACTACGTTATTAAGCTGCTTTTTTGTACACTTTATAACCAATAGCT +TAAGATTTAAAACTAATCGGAAAGAACAATGATTCACCAAAAAAATATTTATGTTGCTATTAAAAATCAGTTAATACGAA +TGTTAAAATACGTTTGATTTTCATTAATAATGATTCAAGTTTATTTAAATGAGCGTTAATGTCAGTCTGTTTTGATGCAC +CTTATAATAAAGACAGATAGTTCAAATTACGTAATAATAACAATCCAATACATTAAGATTGGAGCAAAAAAATATGAAAT +TAACAACGATAGCTAAAGCAACATTAGCATTAGGAATATTAACTACAGGTGTGTTTACAGCAGAAAGTCAAACTGGTCAC +GCGAAAGTAGAACTTGATGAGACACAACGCAAATATTATATCAATATGCTACATCAATACTATTCTGAAGAAAGTTTTGA +ACCAACAAACATTAGTGTTAAAAGTGAAGATTACTATGGCTCTAACGTTTTAAACTTTAAACAACGAAATAAAGCTTTTA +AAGTATTTTTACTTGGTGACGATAAAAATAAATATAAAGAAAAAACACATGGCCTTGATGTCTTTGCAGTACCTGAATTA +ATAGATATAAAAGGTGGCATATATAGCGTTGGCGGTATAACAAAGAAAAATGTGAGATCAGTGTTTGGATTTGTAAGTAA +TCCAAGTCTACAAGTTAAAAAAGTTGATGCTAAAAATGGCTTTTCGATAAACGAGTTGTTTTTTATTCAAAAGGAAGAAG +TATCATTGAAGGAACTGGACTTTAAAATAAGAAAACTCTTAATCGAAAAATATAGATTGTATAAAGGAACGTCTGATAAA +GGTAGAATTGTTATCAATATGAAAGACGAAAAGAAGCATGAAATTGATTTAAGTGAAAAATTAAGTTTTGAACGTATGTT +TGATGTAATGGATAGTAAGCAAATTAAAAATATTGAAGTGAATTTGAATTAGTTCGAGTTAATAGCATAATAGCTTAAGA +AGCGGCTTAACGACAAAATGTGAATTGGCATTTGTGTCCTTATATAAGGAACTGTGTTAAATACATTACTGTTGTTAAGT +TGTTTTTGTAATTCAAAGAGCAGAACAGAGTAACATCATCAGTTGTAGTAAACGATAATCCGGTAAAACAACTAAATGAA +ATAATGAAAGTCATTTAACCTGAACATTAAAATATATTTGTTTTTCATTAAGAATAATTCAAGTATATTTAAATCGAGGT +TAATTATCGTATGAAACGATGCACGTTATAATAAAAATGTATGATTCAAATTACGTAATGAAAACAATCCAATATATTAA +GATTGGAGCAAATAAATATGAAATTTACAGCATTAGCAAAAGCGACATTAGCTTTAGGAATTTTAACAACAGGAACTTTA +ACAACAGAAGTTCATTCAGGTCATGCAAAACAAAATCAAAAGTCAGTAAATAAACATGACAAGGAAGCATTATACCGATA +CTACACTGGAAAGACTATGGAAATGAAAAATATTAGTGCTTTGAAACATGGTAAAAACAACTTACGTTTTAAGTTTAGAG +GTATTAAGATTCAAGTTTTACTGCCTGGAAATGATAAAAGTAAATTTCAACAGCGTAGTTATGAGGGGTTAGATGTTTTC +TTTGTTCAAGAAAAAAGAGATAAGCACGATATATTTTATACTGTTGGTGGTGTAATACAGAATAATAAAACATCTGGAGT +TGTCAGTGCACCAATATTAAATATTTCAAAAGAAAAGGGTGAAGATGCTTTTGTGAAAGGTTACCCTTATTACATTAAAA +AAGAAAAAATAACACTAAAAGAACTGGATTATAAGTTGAGAAAGCATCTAATTGAAAAATACGGACTTTATAAAACAATC +TCAAAAGATGGTAGGGTCAAAATTAGCTTGAAAGATGGCAGTTTTTATAACCTTGATTTAAGATCTAAATTAAAATTTAA +ATATATGGGGGAAGTCATAGAAAGCAAACAAATTAAAGATATTGAAGTTAACTTAAAGTAAATCATTACGAATAATTAAA +AGTAATTGAAGCGGCTTAACGGTGAAATGTAAATTGGTGCGCATAGCTTATACAAAAAGGATGCATCAATCGATATCGTC +GTTAAGCCGTTTTGGTTTGTGTGTCATGAATCCTATCCCAATCTCCATAAAGGTAAAATTTCCACCACCAACATCAAAAT +TCTCCACATCGCAACATAACCAAATGTTATAATAAATCTATTACACAAAGAGATAAATTACTTATTCAAAGGCGGAGGAA +TCACATGTCTATTACTGAAAAACAACGTCAGCAACAAGCTGAATTACATAAAAAATTATGGTCGATTGCGAATGATTTAA +GAGGGAATATGGATGCGAGTGAATTCCGTAATTACATTTTAGGCTTGATTTTCTATCGCTTCTTATCTGAAAAAGCGGAA +CAAGAATATGCAGATGCCTTGTCAGGTGAAGACATCACGTATCAAGAAGCATGGGCAGACGAAGAATACCGTGAAGACTT +AAAAGCAGAATTAATTGACCAAGTCGGTTACTTCATTGAGCCAGAAGATTTATTCAGTGCGATGATTCGTGAAATTGAAA +CGCAAGATTTCGATATCGAACACCTGGCGACGGCAATTCGTAAAGTTGAAACATCAACATTAGGTGAAGAAAGTGAAAAT +GACTTTATCGGTCTGTTCAGCGATATGGATTTGAGTTCAACGCGACTAGGTAACAATGTCAAAGAACGTACTGCTTTAAT +CTCTAAAGTCATGGTTAATCTTGACGACTTACCATTCGTTCACAGTGACATGGAAATTGATATGTTAGGTGATGCATATG +AATTCCTAATTGGGCGCTTTGCGGCGACAGCGGGTAAAAAAGCAGGCGAGTTCTATACACCACAACAAGTATCTAAGATA +CTGGCGAAGATTGTCACAGACGGTAAAGATAAATTACGTCACGTGTATGACCCAACATGTGGTTCAGGTTCACTGTTGTT +ACGTGTTGGTAAAGAAACACAAGTGTATCGTTATTTCGGTCAAGAACGTAACAATACTACATACAACTTAGCACGCATGA +ATATGTTATTACATGATGTGCGTTATGAGAACTTCGATATCCGTAATGATGACACATTGGAAAACCCAGCCTTTTTAGGC +AATACATTTGATGCGGTTATTGCGAACCCACCGTATAGTGCGAAATGGACTGCAGATTCAAAGTTTGAAAATGACGAACG +ATTCAGTGGTTACGGCAAACTTGCGCCTAAGTCTAAAGCAGACTTTGCCTTTATTCAACACATGGTACATTACCTAGACG +ATGAAGGTACCATGGCCGTTGTACTCCCACATGGTGTATTATTCCGAGGTGCTGCAGAAGGTGTCATTCGTCGTTATTTA +ATTGAAGAAAAGAACTACTTAGAAGCTGTGATTGGTTTGCCAGCGAATATTTTCTATGGGACAAGTATTCCAACATGTAT +TTTAGTATTTAAAAAATGTCGCCAACAAGACGACAACGTACTATTTATCGATGCATCCAATGATTTTGAAAAAGGAAAAA +ATCAAAATCATTTAAGCGATGCCCAAGTCGAACGTATTATAGACACATATAAGCGTAAGGAAACAATTGATAAATATAGC +TACAGCGCGACACTACAAGAGATTGCCGATAACGATTACAACCTAAATATACCGAGATATGTCGATACATTCGAAGAAGA +AGCACCGATTGATTTAGATCAAGTCCAACAAGATTTGAAAAATATCGATAAAGAAATCGCAGAAATTGAGCAAGAAATCA +ATGCATACCTGAAAGAACTTGGGGTGTTGAAAGATGAGTAATACACAAAAGAAAAATGTGCCAGAATTGAGGTTCCCAGG +GTTTGAAGGCGAATGGGAAGAGAAGCAGTTAGGGGATCTTACAGATAGAGTAATTAGGAAAAATAAAAACTTAGAATCGA +AAAAGCCTTTAACAATATCCGGACAGTTAGGTTTAATTGATCAAACAGAATATTTTAGTAAATCAGTTTCGTCGAAAAAT +CTAGAAAATTATACACTAATAAAGAATGGAGAATTCGCGTATAACAAAAGTTATTCTAATGGATACCCATTAGGGGCTAT +TAAAAGATTAACTAGATATGATAGTGGTGTATTGTCCTCTTTGTATATTTGTTTTTCTATTAAAAGTGAAATGTCTAAAG +ACTTCATGGAAGCATATTTTGATTCGACACACTGGTATAGAGAAGTTTCTGGAATTGCAGTTGAGGGTGCAAGAAATCAC +GGATTATTAAATGTTTCTGTGAATGATTTTTTTACTATTCTAATTAAATATCCAAGTTTAGAAGAACAGCAAAAAATAGG +CAAGTTCTTCAGCAAACTCGACCGACAAATTGAATTAGAAGAACAAAAGCTTGAATTACTTCAACAACAGAAAAAAGGCT +ATATGCAGAAAATTTTCTCACAGGAACTGCGATTCAAAGATGAGAATGGTGAAGATTATCCAGATTGGGAAAATAGCAAA +ATAGAAAAATATTTAAAAGAGAGAAACGAACGTTCTGACAAAGGGCAAATGCTTTCAGTAACTATAAATAGTGGCATTAT +AAAATTTAGTGAATTGGATAGAAAAGATAATTCAAGTAAAGATAAAAGTAATTATAAAGTAGTTAGGAAAAATGATATTG +CATATAATTCTATGAGAATGTGGCAAGGGGCTAGTGGTAAATCAAATTATAATGGGATTGTTAGCCCTGCATATACTGTG +CTTTATCCAACACAAAATACTAGCTCATTATTTATTGGATATAAGTTTAAAACACATAGAATGATTCATAAATTTAAAAT +TAATTCACAAGGATTAACATCAGATACATGGAACTTAAAATATAAACAATTAAAAAATATAAATATAGATATACCTGTAT +TGGAGGAACAAGAAAAGATAGGTGATTTCTTTAAAAAAATGGATATATTGATAAGTAAACAGAAAATGAAAATTGAAATA +TTAGAAAAAGAGAAACAATCCTTTTTACAAAAAATGTTCTTATAACTTTGATAAATACATAGATTGCATAAGAATAAAAT +TTGTATAATTTAACATAAAAGTTGTAAAAGTAAAGTGAATTAAAAACGAACATTAAATTTAGGCACTGTGAAAGCGCAGT +GTCTTTTTTGTGTCGAAATTGTGTACAGAATAAGTAGTTAAATAAAGATTAAGTTGAGATAAAGTGTTATTCGTAAATAA +AAGAGAGTAGATCGATAGGAATTGAATGATATTAGTTAACTATTTATTAAATTACTTAATAATGATTAATTTTTAGTTAA +AGTAAGTTTAATGTGAAGCACGACCATTGCTCATTATAATGAATGAGGATTGTTCGTATTGCGTAATAGAATAAATCAAA +TAGACTAAAAATTGGGAGCATAGAATTATGAAATTAAAAAATATTGCTAAAGCAAGTTTAGCACTAGGGATTTTAACAAC +AGGGATGATTACAACTACTGCTCAGCCAGTAAAAGCAAGTACATTAGAGGTTAGATCACAAGCTACTCAAGACTTGAGTG +AATATTATAATAGACCGTTCTTTGAGTATACAAATCAGTCAGGATATAAAGAGGAAGGAAAAGTGACGTTTACTCCTAAT +TATCAACTTATAGATGTAACTTTAACTGGGAATGAAAAGCAAAATTTTGGTGAAGATATTTCTAATGTAGATATATTTGT +TGTAAGAGAAAATTCTGATAGATCTGGTAATACAGCTTCAATTGGTGGTATTACTAAAACAAACGGTTCAAATTATATTG +ATAAAGTAAAAGATGTAAATTTAATAATTACTAAAAACATCGATAGTGTTACATCAACGTCAACATCATCTACATATACA +ATTAATAAAGAAGAAATTTCATTAAAAGAACTTGATTTTAAATTAAGAAAGCATTTAATTGATAAACATAACCTTTATAA +GACAGAACCTAAAGACAGTAAAATTCGAATTACTATGAAAGATGGTGGGTTCTACACATTTGAATTGAATAAAAAGTTAC +AAACACACCGTATGGGTGATGTTATTGATGGCAGAAATATAGAAAAAATTGAAGTGAATTTATAAAATTATTCGAGGGAG +CATATCATGAGGGAAAATTTTAAGTTACGTAAAATGAAAGTCGGTTTAGTATCTGTTGCAATTACAATGTTATATATTAT +GACAAACGGACAAGCAGAAGCATCTGAAAATCAAAACGCTTTAATCTCTAATATAAATGTAGACAATCAGGAAAAACAGA +ATAATGTAAATCAAGCTGTTCAGCCTCAAAATAATACTAATGAAACATCAAAAGTACCGGCTAATTTTGTCAAATTGAAT +GATATTAAACCAGGTGATACTTCTATACAAGGAACAACTTTACCAAATCAATTTATACTATTAACTATTGATAAAAAAGA +TGTGAGCTCAGTTGAAGATTCTGACAGCAGCTTTGTTATGTCTGATAAAGATGGGAATTTTAAGTATGACTTAAATGGTC +GCAAAATTGTTCATAATCAAGAAATTGAAGTGTCTTCATCAGATCCCTATTTAGGTGACGATGAAGAAGATGAAGAAGTA +GAAGAAACTTCAACTGAAGAAGTTGGTGCTGAGGAAGAAAGTACAGAAGCTAAAGCTACATATACAACACCGCGATATGA +AAAAGCGTATGAAATACCGAAAGAACAGCTAAAAGAAAAAGATGGACATCACCAAGTTTTTATCGAACCTATTACTGAAG +GTTCAGGTATTATTAAAGGCCATACCTCTGTAAAAGGTAAAGTTGCTCTATCTATTAATAATAAATTTATTAACTTTGAG +ACAAATGCTAATGGTGGTCCAAATAAAGAAGAAGCGAAATCTGGATCAGAAGGAATCTGGATGCCTATTGATGACAAAGG +ATACTTTAATTTTGACTTCAAAACGAAACGTTTCGATGATTTAGAGTTAAAGAAAAATGATGAGATCTCATTAACATTTG +CACCTGATGACGAAGATGAGGCATTGAAGTCATTAATTTTCAAAACTAAGGTAACGAGTTTAGAAGATATTGATAAAGCA +GAAACTAAATATGACCATACTAAAGTGGAAAAAGTAAAAGTATTGAAAGATGTTAAAGAAGATTTACATGTAGATGAAAT +TTACGGAAGCTTATATCATACAGAAAAAGGTAAAGGTATTCTTGATAAAGAAGGTACTAAAGTAATTAAAGGTAAGACTA +AATTCGCAAATGCAGTTGTGAAGGTAGACTCTGAACTAGGTGAAGGTCAAGAATTCCCTGATTTGCAAGTCGATGAAAAA +GGTGAATTCAGCTTTGATGTAGATCATGCTGGATTTAGATTACAAAATGGAGAAACACTAAACTTCACAGTAGTTGATCC +TATTACAGGTGAATTATTAAGTGGAAATTTTGTTTCTAAAAACATAGATATTTATGAATCTCCTGAAGAAAAAGCAGATC +GTGAGTTTGATGAAAGAATGGAAAACACACCTGCATATCATAAATTACATGGTGATAAAATTGTCGGCTACGATACTAAC +GGATTCCCGATTACCTGGTTTTATCCATTAGGTGAAAAGAAAGTTGAACGTAAGGCACCAAAATTAGAAAAATAATTAAA +TAAAACAGCTTAATGATGTAATGAAATTAGTGAGTTAATCACTGACTTCTACGTCATTGAGCTGTTTTTTTGTGCTTTGT +TACAAAGCATTATTGAATTTATTTTACGTGTTCATATTTTGAAACATCAAAGCCGTCTTGTTTAGCTTTGTTGATAATGT +CTTTGATTGAATGTAGTCCTTTATCGGCGAAGTATGATCTTAAGTTGTCTTTTGTAGCTTGGTCAGCATTCTTATCTAAT +AACACATCAATATAACTTAATTCATGTTCTAAGAAGTTTGCATCATCATGTAGTACGAGTCCATTTTGAGAATAAACTTT +CGCATCTGCTTGATTACCATATCCAACAACGCCAGTTGCTAAAACACCTACCATTGCCGTAGCTACTAAAACCTTTTTAA +ATTTCATATCTATCACTCCTCTAAAAATTGTAACTCCATCATAACACTGAATATTAAGAAAATTACGTTTATTAAGTCGA +TTTAATAATTTTTAATAAATAGTTAAAGTGACAAATATTGTTTAAATGCAATTAATCTTTAATACGATGCTTGAGGATTT +TTCCTAATAAAACCTTGATTTCAAAAAGGGTTTAAATCAAATGAAACAATAATAAAAAAATGACGCAATATAATAATAAG +TACAAATTTAATTAAGAAATTAAATTGATTGTATATGTATATTTTGGTAACGTAAAAGAGAAATATACAAAATAATTAAT +TATTTATATGAAAAGAGAATATAAATGAAGTATAAAACAGAGAGACGTGAAGCGATGGGATATTTAAAAAGGTTTGCATT +GTACATAAGCGTTATGATTTTAATATTTGCGATAGCAGGTTGTGGCAAAGGTAATGAAACAAAAGAAGATTCAAAGGAAG +AACAAATCAAAAAGAGCTTTGCGAAAACATTAGATATGTATCCAATTAAGAATCTCGAGGACTTATACGACAAAGAAGGA +TACCGAGATGGCGAATTTAAAAAGGGTGATAAAGGGATGTGGACGATATATACAGATTTCGCCAAAAGTAATAAACAAGG +TGGATTGAGTAATGAAGGTATGGTCTTATACTTAGATAGAAATACACGGACTGCAAAGGGACATTATTTTGTTAAGACAT +TCTATAATAAGGGCAAATTCCCAGATAGAAAAAATTATAAAGTTGAAATGAAAAATAATAAAATTATCTTATTAGATAAA +GTAGAAGATACAAATCTAAAAAAGAGAATAGAAAACTTTAAATTTTTTGGACAATATGCAAACCTTAAAGAATTGAAAAA +CTACAACAATGGTGATGTCTCAATTAATGAGAATGTTCCAAGTTATGACGCAAAATTTAAAATGAGCAATAAAGATGAAA +ATGTTAAGCAATTAAGAAGTCGTTATAATATTCCTACTGATAAAGCACCGGTATTAAAAATGCATATTGATGGTAATTTG +AAAGGAAGTTCTGTGGGTTATAAAAAGTTGGAAATTGACTTTTCAAAAGGTGGAAAAAGCGATTTGTCAGTAATAGATTC +TTTGAATTTCCAGCCGGCGAAGGTAGATGAAGATGATGAATGATGAGGATGGTGTGTAACAATGAAGTCTATAAAAAGGA +TTGGATTGTGCATTAGTTTGTTGATTTTAATCATCTTTGTTACATCTTGTGATGGTGATAATAAGATCATTGGAGATTCA +AAAGAAGAACAAATCAAAAAGAGCTTTGCGAAAACGTTAGATATCTACCCTATTAAGAATCTCGAGGATTTATACGACAA +AGAAGGATATCGAGATGGCGAATTTAAAAAAGATGATAAAGGTACTTGGCTAATTAGATCTGAAATGAAAATCCAATTAA +AAGGAGAAAATCTGGAATCTAGAGGAGCAGTTTTAGAAATTAACAGAAATACTAGAACGGCTAAAGGGCATTATATTGTT +AGAGAAGTTGTTGAAGATAGCGATGGAATGACACACAATCATACAAAAAGATATCCTGTGAAAATGGAAAATAATAAAAT +GATTCCATTAAAACCAATCGATGACGAAAAAGTAAAAAAAGAAATCGAAGAATTTAACTTCTTTGTACAATACGGGAATT +TCAAAGAATTGGAAAACTATAAAGAGGACGAAGTGTCATATAACCCAGAAGTACCAATTTACTCTGCAAAATATCAATTG +AAAAACAGTGATTACAATGTTGAACAATTAAGGAAGCGATATAATATCCCGACGCAAAAAGCGCCCAAATTATTATTGAA +AGGCTCAGGTAATTTAAAAGGTTCATCAGTCGGATATAAAAATATTGAATTTACCTTTATTGAAAACAAGGAAGAAAATA +TTTACTTCACAGATAGTATCTACTTTAATCCAAGCGAGGATAAATAAATATAATTACTAATATAAAAAATAACGTTAGAA +CATTTAATAATAAAGTTAAAATGAATTACATGTATTAAATTGAATAAGTTAGTTTTGTTTGATAACATAAAAGTTGAATA +ATCACTATTTTAATAAGTTGTATGAAATATTCAAATGTTTATAAAATTAAAACAGAGAGATGTGAAATGATGGAGTATAT +AAAAAAAATTGCTTTGTACATGAGTGTATTACTTTTAATCATTTTTATTGGGGGATGTGGAAATATGAAAGATGAACAGA +AAAAAGAGGAACAAACGAATAAAACAGATTCAAAAGAAGAACAAATCAAAAAGAGTTTTGCGAAAACGTTAGATATGTAT +CCAATTAAGAATCTCGAGGATTTATACGACAAAGAAGGATATCGAGATGGTGAGTTTAAAAAAGGCGATAAAGGTACGTG +GACTATACTTACAGGTTTTTCAAAAAGTAACAAACCAGGAGTATTAGATGATGAAGGCATGGTGTTATATCTTAATAGAA +ATACCAAAAAGGCAACAGGTTATTATTTTGTAAATAAAGTTTATGATGATATTAGCAAAAATCATAATGAGAAAAAATAT +CGTGTTGAACTAAAAAATAATAAGATTGTTCTTTTGGATAATGTAGAAGACAAAAAACTTAAACAAAAAATTGAAAATTT +TAAATTTTTTAGTCAGTATGCTGATTTTAAAGATTTAAAAAATTATCAAGATGGAAATATAACAACTAATGAAAACGTAC +CGAGCTATGAAGCACAATATAAAATGAACAATAGTGATAAAAATGTAAAAAAACTTAGAGAAATTTATCCAATTACAACT +AATAACTCTCCAAATCTAAAATTATATATAGATGGTGATATAAAAGGAAGCTCAGTAGGATATAAAAAAATAGAATATAA +ATTTTCAAAAGATAAAGGTCAAGAGACAACATTAAGAGATTATTTGAATTTTGGACCGTCTGAAGGTGAGAATGTTGAGT +AGGAAGTATAAAATAGATTTAAAAGCAATTAATCAAAATACTGAGTCTACAAGTGCTATTTCGAAAGCGTCATACGAAGT +TGAGAATGCTAATAATAATGGTCTATCAAAAAGAGACGTGATTAATCAATTTAATGATTTGAAAAAAATGAAAAAATTCC +CTTCTAATCTTGAATATGTTGATAGTTACACTGATTCTCTTACTGGAGTAACAACTTCTGCTTTTTTAAATAAAGATACA +GGCAAAGTAACTCTCGGGATGACTGGGACTAATTTACAAGACGAAGCCTTTAAAAAGTTAAAAGAAGGTGAATTTTCAAG +ACAAAATGTTACCAATGCTTTGGAAACAGTTAAAGATGGATATGCAGATCTTAAAATATTATATTCTCCTGCATCTGATC +AAAACTATAGATATGCGAATACACAAGAATTTATAAATAAAATAAAAAGTAAGTATGACATTGATTTTATTACTGGACAT +TCACTAGGTGGAAGAGATGCGGTAGTTCTAGGAATGAGTAATGGTATTCCGAACATTGTGGTTTATAATCCAGCTCCTAT +TTCTATAACTAGTTTGAATCCTAATTCCCCAGATGGAAAACGTTTATTAGAATTATATAAAAATTATAAAGGTAATATTA +CTAGGTTTGTTGCAGAAAATGATGCATTGACAGAAAATCTGAAGAAATATAAGCATTATGTTTTTTTCGGTAATGATAAA +GTCTTTAAAAATGGTAAAGGTCATGAAATGGAAGGCTTTCTGACCGAAGAAGAACAAAAAGCTATAAAAAAAGAACTTAA +AAAACTACAAGGTTATGCAGAAGAAAATAATAAGTCATTTGTAAAGAATTCAAATAATGCTATCTCTAAATTAGCTAGTA +TAGAATTACTTAGAGCAAATATGATGACTACAAATGGAGGAGGATTATCTTCTTCGCAGCAAAAAGTTTTAGAAAGTTTA +ACAGCTTTAACAATTGCGCAGTCATTCAGTCAACTGATAGACGATGAAATTAATCAAATCAAAAAAATGTATAATGAAAA +GAAAAAGAAATTTGGAAAAAATTGGGAAGACGCTCAAAAAGCTGGAAAAGCTGTAGGTGAAGATTTGAGTGTAAATGGAG +TTCTCAATGCTTTAGATGAAGGTCAAGTGAATGAAAGTAGTATGGTAAGAGAACCTGAACAAATGATATCTGCAAAAGAA +AGACAACTTTCAACGATAGGGTCTTCTGTATCAAATTATATTATGAGAGTTAGACTCAGTATTAATGAAATCGTTGATAA +AGATCAAGTGCTTGCATCACAAATAGGTGGATTACTATGATGTTTAATCAAATTAATAATAAAAATGAATTAGAAGAATC +ATATGAATCTGAGAAAAAACGTATAGAGAATGAACTGCAAAATTTAAATGAACTTAGGCATAGAACTCGAAAAGAAAATG +AACGTAGTTATGATGTTTTTCAATATTTGAAGCACGAAATGAATTATAGTGAAGATGCCCAAAGGAAAATGACGAGAAAT +ATAGAAGCGTATGAGCAAGAAATCAATGAGATAATTAGAAAGCAAGAATGGAAATTAGAAGAATATAAAGAAGACTTAAA +AAAGTCTTATGAAAAGCAGTTAGATAAGCTAAGTGACTGATAATTTGGAGGAGATTAAATGACTCTAATAGAACCAGATA +TGACCTTAAGAATGCCAGATATAAGTACTACAGTAGAAACACTTAATCTCATATCTAAAATGGAAGCGCAAAAAGAAAAT +ATTCGCACAGTTATTGCACCTGAACATAAGCATAAATACAAAGATATTGAAAACGGATTAAAAGGTGAAGAAAAAGTATT +AATTGAACAAATGGCGCAACATTGCGAAGCTTTTAAAGCTAATTTTAAAGGTGCAGCTCAAGGAGATTGGGTTAAAAGTG +CCATGTCTGAGATAGACAGCATTAAGGATGACCTGAAAAAAATTAATAGCTAAACTACATATCATACTACTAAATAAAGC +GACCAATGTTCAGTATATTCACAACTGACACTAGGCCGCTTCTTTTTAATTTATATTATTCCCATGTAAAGTAATTCGAA +CCGAATCGAAATATCACACTCTCAATTGTAAAAAAACAACTTAAATTACTGTCCCGCATTAAACATGACCACATTCGTAA +CAGCAACAATAAAAATCCTCAAGTACTAAAAGCTATGATCACTTTATCAAAATCAACGCAAGTTAACCTCTCCCGAACTA +CCCGTTACTCATCACCTTCACCTTACAAACCATAAAGCGCTGCAGTTAAGTATTGAATGGCATAGCCAATTGAAATGAAA +ATAGCAATCGTTATAATAGCACTGCCAATGGTATATAAAGTATGTTTCGTAGATAATCCTTCAGTCTTTGAACCAAAAAC +ACTGAGTAGAATAAATGCAATACCAGTAACATATAAAACTAAAATGACAAATAACATGTTTATACACCTCGATTCTCTTT +CGTGAGATATATCTTTGTCTCCATTATAAAGTAAAGCGCTCAATAATTTAATTAAGTATACTATTTTTTTATTATCGAAG +ATTGCGAAAGAGGAAACGCTCCTATATAATGTAAAACAGAATTATTCCTATTTACAAATAAATTGAAGTGAGGGTATATA +TATGGCTAAAATTCCAGTTACGGTATTAAGTGGTTATTTAGGCTCGGGGAAGACAACGTTGTTAAATCATATTTTACAAA +ATCGAGAAGGTCGACGTATCGCGGTAATTGTAAATGATATGAGTGAAGTAAATATCGATAAAGATCTTGTCGCAGATGGT +GGGGGACTATCGCGTACAGATGAAAAATTAGTCGAACTTTCTAATGGTTGTATCTGTTGTACACTTAGAGACGATTTATT +AAAAGAAGTTGAGCGTTTAGTGAAAAAAGGTGGCATCGATCAAATTGTTATTGAGTCAACAGGGATTTCAGAGCCAGTAC +CTGTTGCACAAACTTTCTCATATATTGATGATGAACTTGGCATTGATCTTACAGCGATTTGCCGTTTAGATACAATGGTT +ACAGTTGTGGATGCTAACCGCTTCGTACATGACATCAACTCAGAAGATTTATTGATGGATCGTGATCAAAGCGTTGACGA +AACAGATGAGCGTTCGATTGCTGATTTATTAATTGACCAAGTTGAATTTTGTGATGTATTGATTATTAATAAAATTGATT +TAATTAGTGAAGAAGAACTAGCGAAGTTAGAAAAAATGTTAAGCGCATTGCAACCGACTGCTAAAATTATTAAGACAACA +AATTCTGAAGTAGATTTAAAAGAAGTCTTGAATACGCAGCGTTTTGATTTTGAAAAAGCGAGCGAGTCAGCAGGATGGAT +CAAAGAACTTGAGTCTGGTGGGCATGCATCGCATACACCTGAAACAGAAGAATATGGTATATCATCGTTTGTATATAAAC +GTCGTCTACCTTTCCATGCTAAAAGGTTCAATGATTGGTTAGAAAGCATGCCAAATAATGTCGTTCGATCAAAAGGTATC +GTATGGCTAGCACAATACAATCACGTAGCATGTTTATTATCTCAAGCAGGGTCATCTTGCAATATTCATCCAGTTACATA +TTGGGTGGCTAGTATGTCTGAAGCGCAACAAACACAAATATTAGCAGAACGTCAAGATGTCGCAGCTGAATGGGATCCAG +AATATGGCGATCGTCATACACAATTTGTCATTATTGGTACAGAATTAGATGAAGAAAAATTAACAAAAGAACTCGACGCA +TGCTTAGTCAATGCGCAAGAAATTGATGCAGATTGGCAACAATTTGAAGATCCATATCAATGGCAAATTAGACCAGCACG +ATAAGTTGAATGAAGTATAATCATTTTTGAATTGTGGCTTAATTGTTTGCAATTTATGACAATTAATAAAAAGTTAAGGT +GTTCTTAGGATCTTTTTGTTTTCAATCTATCTAAAGTGTGATAGTTTTGATAAAGCAGAAATTTGCATTTTATCCATAGG +GCTAGGACATGTATGTGTCTTAGTCCTTTTTATATTTACGTTGATGAAAAATGGCAAAACCATCGTACCAACTGTTAATT +GAAGGTACAATCATTGTTTAACAGTCATAAAAATGAGTGAAGTAAATGCAGCTATCAATGTACTATTAACACTTACGGTC +ACACGAGTGTTTCAGTCTAAACGTGCAACGCAATTCCGCACGTGGCCAATATATCTTGCGCTAGATCATGAAAATCATTA +AGATTTCGCATGCCAAATCATGAATGTCTCAGAACTCGAGTATCAAACATAAAACACGCCACAAAAATATAACGCGTCAG +ACTACAAAGATCACGAATATTCCGTATGTCCCACTAAGAACGTCCTATACAAAAAACACCAAGACCATTTATTTGCAACA +AACTAACAAGACTCACCTCACATCAATAAATCAACACAAAGCAAAGCCACCATCCCTATTGGTATAGTGGCCTGAGAATT +TTAAGTATTCAATTCGCTTAAATTATTTTGCGAAAATGTCGATAATTGCTTTGATGATTTTAATGATAGTACCTACAATA +GCCATCGTTTTGTCCTCCTGTATGTTGATTTAACATAGTGATGAGTTGTTGATCGTTAATGTTTGAGATTAGTTGTTACC +TAAAAATTTACCAAGTAAATCTTTAAAGAATTTGAATAATTTTGCTACGAATTCCATGTGAATGGCCCCCTTCAAATAAG +ATGTTCATATCTATGAGATTTTTATAACTTACTTACCAGTGAATTTCTCAATTAATCCTTTAATGAATTTAATGATTCCT +GCAATGATACCCATGTGAAAGACCTCCTTTGTTTGTTATGAAATCTTATTTACCAGTGAATTGTTCGATTAAGCTTTTGA +TAACTTTAATGATGCCAGCGATGATACCCATTAAGATTACCTCCTTTGCTTATGAGTTAACTTCATTGTACATAGTTATC +TTGTGCGTAATTGATTTTTTTCCTAACTTGAATTGTTATGATCATAACTGCGAATTTTTTATTTTAAAAGGACGGGAAAT +ACTGCCTAACTGTACAGGTGCATTTACAAAATCTTTACAATCATTGGTTTAATAGAGGTTAAGCTCATGAATTTGACATG +AAGCAGAAATTTAATTATATTTTATGTTATAAGTTTAAAACGAATAACACGTTAGGTCTCGTCTAGGCAAAGCATATTTA +TGAAGATGAATTGAACAGACAGAAGTAACATTTTATATACATATGTGCATGGGAGCACGCTTAAAGAGAACACTGCAACA +AGGTATATCAACGTACCGTGATGCAGTGTCTAGTTAAACTGAAACCTGCAATAGTAAATTTTCTGCCATTATGTACAGAA +TCTACTATTGTAGGTTTTTATTTTTATACATAAGCAATTATGCGAGAGATTAGAAATAAGGAGGTTATGCAGTGTTAAGT +TTTCAATTGCTATTTTCACTGTTTGTTATTGCGCTTATCATTGCATTGATAAGTGGCTTGTTGTTTTTAGCACCAGTTAT +GCCAATGAGATATATTAAATTACATTTATACATACTAGTCATGCCAGTATTATTTGCAGTCATTGGCTTTTTCGGTATTC +ATGGTCAACATGTCTTAGGTCCATTTAAAATAGATCGTTTATCTTGGTTATTAGCTGGCTTTGTAATGGCGCTTGGCTTT +ATTATTCAAAAGTTTTCAATGCGATATTTACTAGGTGATCATCATTATAGACATTACTTTCCATTGTTCACTGCGATTAC +GTCGTTTGCATCTTTAGCATGGATGTCTGAAGACTTAAGACTGATGGCACTCTGCTGGGGTATTACATTATTATGTTTAA +CATTGCTGATGAACGTTAATCGTTTTTGGAAAGTGCCACGTGAGTCTGCGAAATTATCAAGCATGACATTTTTATGTGGT +TGGCTTGCATTCGTTGGAGCAATTGTAACTATTTATATTGCGACTGGCGAGTGGCGGGTACCACAACATATAGTTCATCC +GACATGGTCATTGTTGACGAATGTACTACTTGTATTAGCTGTCATGATACCGGCAGCACAATTTCCTTTTCATCGATGGT +TGATTGAATCTGTAACGGCACCAACGCCAGTATCGGCAATTATGCATGCAGGAATTGTGAATGCAGGTGGTGTTATTCTA +ACTCGTTTTGCGCCGATATTTGATAATGGATTTGCGTTATCATTATTACTTATCCTTTCTAGTATTTCTGTATTGTTAGG +ATCGGGTATTAGCTTAGTTCAAGTTGACTATAAACGCCAATTAGTGGGCTCTACGATGAGTCAAATGGGCTTTATGTTAG +TTCAATGTGCATTGGGTGTATATTCAGCAGCGATTATTCATTTAATATTGCACGGTATTTTTAAAGCAACATTATTTTTA +CAATCAGGTTCTATCGTGAAGCGATTCAATATTCCAAAACAAGCATCTGCTAAAGACGCTTATGGCTGGATTGTCATGGG +ACGTGTATTAGCTATTATCGTGGCATTTCTATTTTGGATGAGTAGTGACAGAAGTGCATATGAAGTGTTAAGTGCACTCA +TTCTAGGATGGTCATTACTTGTATCTTGGAATCAAATGGTAGCCTTTAGTAAAGGACGCATGGCACGTCTTGTTGGTATG +ATTTTGATTGCAATCGTGACATTTATCTACGTCATCACACATAATTATTTTTACGCTGTATTACAAAATATAACAACACA +TGCGACAACGCCGCCTACAGTGAGTGTCATCATTAGTGTTGTTATTTTAATCTTTGGCAGTTTATTAAGTATTTGGGTGG +CGCGGCATCGATACTCTAAGGCTTTTGCGGTATTGTACGTGTGGTTAGTTAATCTAGGTGAAGCACGCTCGAAAGCGATA +GAAAGTCATCCGAATTATTTGAAGAAGTATTTATAGGAGGTGAAAGGTATGACAACACAGTTAAATATCAATTCAGTCAT +TGAAAATGCGAAACGTGTTATTACACCATTATCACCAATTTCGATTTTTGCAGCACGAAATCCATGGGAAGGATTAGAAG +CGGATACGTTTGAAGATGTCGCAAAATGGTTACGTGATGTTCGAGATGTGGATATTTTCCCAAATAAAGCATTAATAGAA +AGCGCTGTGGCACGTGGTGAATTAGATGAAAGTGTCTTTAATCAACTTGTTACTGATATGTTACTTGAACATCACTACAA +TATCCCGCAACACTACATCAATCTTTATATTGATAACATTAAAACATTAAAAGACGTACCTGCATCATATATGAATCATT +CAAATGTTGATGTTGTTGCTGATCTACTATTAGAAAAATCAAAACGTGATATGGCTGAATCATATCATCACTATGATGTA +CGTCCGATGAGTGATGCAATAATAGATGAACAAGGTGAGCCACTTAGCGAACAAGTGAATCGTCAAATGATTAAATGGAC +GAAACTTTATATCGATCAATTTCTATCGAGTTGGACAATGCCGAAGCGTGAGCAAAGTTTTTACCATGCATGGTTGCATT +TAGCGCAACATGACCATAGTTTTACTAAAGCACAGCGCCAAGTGATTAAAGGCTTACCCAATGATCCTGAAATGACGATA +GAGTCAGTATTAACTCATTTTTCAATAGATCAGGAAGACTACCAAGCTTATGTTGAAGGACATCTTTTGGCGTTACCGGG +TTGGGCAGGTATGTTGTATTACCGTTCACAACAGCATCACTTTGAACAACATTTGTTAACGGATTATTTGGCAATTCGGT +TAGTTGTCGAACAATTGCTAGTTGGTGATGAGTTTAAGTCAGTCGCTAAAGATTGTGAAAGTAGATCGGAAAATTGGTTT +AAGCAAACTGTTGCATCATGGTGTTACTACAGTGATATGCCTAGCGATGTATTACTACAACATGACGTCAATGAAATTCA +AACGTTTATTCATTTTGCAGCAACTATGAATAAAAATGTATTTAAAAATTTATGGCTAATTGCCTGGGAAATGACATACG +AATCTCAGTTAAAACAAAAAATTAAAGCAGGTCATGAAAGTGTGGCGGGCGCATTAGATGTAAACCAAGTAAATGTCTCA +GAAAATGATAACGCTAATCAGCCACATTCAGTATTGTTAAATGACACACAAGCAGTTGATGAAAATAATAGCGAGCTAAA +TCAGATGGGCACATCAACGAAAGCGCAAATTGCATTTTGTATAGATGTTCGTTCAGAACCATTTCGTAGACATATCGAAG +CAGCAGGGCCCTTTGAAACGATTGGTATTGCAGGCTTCTTTGGATTACCTATTCAAAAAGATGCCGTAGACGAACAATTC +AAACATGATTCATTACCTGTCATGGTACCGCCGGCATATCGCATTAAAGAATTTGCAGACCGCTACGATATGAATGTTTA +TCGACAACAGCAACAGACAATGTCATCGATGTTTTACACATTTAAATTGATGAAAAATAATGTCATGCCTAGTCTGTTAT +TGCCTGAATTAAGTGGGCCATTTTTAAGCTTAAGTACCATTGTCAATTCGATTATGCCTAGAAAAAGTCGCGCGTCTTTA +CAAAAAATAAAACAAAAGTGGTTGAAAAAGCCTGAAACAAAGTTGACGATTGATCGTGAGTTTGACCGAACATCAGACTT +ACCTGTTGGATTTACTGAGCAAGAGCAAATTGATTTCGCGTTACAAGCGTTGAAATTGATGGATTTAACCGAAGCATTTG +CGCCGTTCGTTGTGTTAGCAGGTCATGCTAGTCATTCTCACAATAATCCACATCATGCATCACTTGAATGTGGGGCTTGT +GGTGGCGCATCAAGCGGTTTTAATGCTAAGTTATTAGCGATGATATGTAATCGTCCAAATGTCAGACAAGGATTAAAACA +ATCAGGTGTGTATATTCCAGAGACAACTGTTTTTGCGGTAGCAGAACATCATACGTCTACTGATACGTTGGCATGGGTAT +ATGTGCCAGACACATTATCTTCTATTGCTTTAGATGCATATGAATCATTGAATGACGCGATGCCGATGATTTCTGAACAC +GCGAATCGCGAACGTTTGGACAAACTGCCAACGATTGGTCGTGTGAATCATCCAGTGGAAGAAGCGCAGCGGTTTGCGAG +TGATTGGAGTGAGGTACGTCCAGAATGGGGATTGGCTAAAAATGCATCATTTATAATTGGACGACGCCAATTAACAAAAG +GCATTGATTTAGAAGGGCGGACATTTTTACACAATTATGATTGGCGTAAAGATAAAGATGGCACATTATTAAATACCATC +ATTTCTGGTCCGGCACTTGTGGCACAATGGATTAATTTACAATATTATGCGTCGACAGTTGCGCCGCATTTTTACGGAAG +TGGGAATAAAGCGACACAAACCGTCACGTCAGGTGTTGGTGTCATGCAAGGTAATGCGAGTGATCTGATGTATGGCTTAT +CATGGCAATCTGTTATGGCTGCTGATCGGACGATGTATCATTCGCCAATTCGTTTACTTGTCGTTATTCAGGCACCCGAC +TATGTTGTAGCAAGACTACTCGCGAATAATGAGCATTTCGCTAGGAAGGTGTCTAATCATTGGCTGCGTTTAATGAGCGT +TAATGAGGAAGGGCGTTTTAAAAGTTGGATTTAACGATGATATGTTGCGATGCTAAGTAACTAATATATCATCATATCTA +CAATGTTCTAGCTTCTTAAAATATACTAAAAATGATAAACTTAATGTAATAGTTGATGTCGTGATAACGATACAAAGATG +AAGTAATCAATTTTAATATTATTGGAGTTAGTGATATGAAAAGAACGAAAGGTGAAATCGAAGCTGAAATCAGTAAAGCC +ATTACGCAATGGGAAAAAGATTTCCTTGGCAGAGGTTCCTTGTCAGTTAAATCAGACATTTTAAGAGATATGGTGATTAT +TAGTTTACAAGGTATCTTAACGCCAGCTGAATATCGTGTATGTAGTACGAATGAAGGATTATTAAATATTAAACGAACAC +GTTCTGAATTAGTTGAATCCGGTGAGCAAGATTTGAATGATATCATTTTTAAAATTACAGGTATCAAAGTGATGAGCTTC +CATAGTGATTTAAGTACAGTTACAGGTGAACGTATTATCGTATTCAAACTTGAGGATAATTTGGAAAAGCATATTTAAGA +AGGTTAGGTATGCTTCTGCTGGCTTATTAAAGCTATAGAAGTATGCTTGGTTAAACAGCGCAACGCCATATATTTGGGTG +TGCTGTTTTTCTATTACATGATGTGTTTATTACGTGGATGAATGCGTGGGTCTGAATATAGCCCGTGAAAGGCGTATGTC +ATAAAATTTGCGAATGGTGACAAGTCGACGCAAGCATATTTGTTGCGAGACATGCCCACTATCCTTTTACATAAAGAGTA +TATGTGTGACGTAGGCATATAATCGATAAAGTATTCCTAAAAAATTAGGTATAATGACCATTGTTGCAATAAGTTTTTCG +GCCTTGAAACCGATAACGCATAGTATAATAGGAATAAAATATAATACAAATATCCAGTAAATCAAATTTGAAAATGATGG +ATGAATAGGGGTAAAGAATCTAACTAATGTAATGATTAACGCTATATAGACAAAAATAACTCTATTGATACGCCTTACCC +CCTCTGTATAATAAATATAAATCGTACTAAATTGAAAATAGCAAAATATTGTTATTTCAATTATACAATGTTTTATTTGC +AATATACATAACAATATGAACTTTATAATTTTGATATTGTCGGTAAAGTGAAATTTGTGGCACATTGATATTATGGATAA +CATTTGTAAAATGAAAATGATGATACGAGGAGATTGTAAGATGATAGATAAAAAATTAACATCACCGAAAATGACAGTGC +CTTTATTTTTAATCGCGCTGATTGTATTTATAGGTATGTTTTACAGTGTAGTGACAAATCAAGAATGGCTTAAAAATATA +GATATGGGATCATTAACATGGTTTACAGATTATTTCGGTGAGCCACAACGTCAGTATGTTAACAATTTGTTTAATTACTA +TATGACGTTTAGTGCGGAAATTGGAGATGTCAAAGGTGTCGTGTTGATTTCCATTATCGTCACAATCATACTGTTTATTA +AACAGAGGCATTTAGCGGTTTGGTTTGTGACATATTTGGTTTCAGGTGTCATCATGAACAAATTAATTAAAGATACTGTA +TTACGTCCAAGACCATATAATCATTTAGCCGTTGATACAGGCTTTTCATTTCCGAGTGGACATTCCAACGCCAGCACATT +ATTATATTTCGCCTTAATGATCATTATTATTTCACTTGCTGCTAAGACAATAACAAAAGTGTTGAGTGCGTTGGTTATGG +GAATATTATGGCTTAGCATATTATTTTGTCGCCTTTATTTTCATGCACATTACTTTTCAGATGTCATTGGCGGCACGTCA +CTAGCAATCATTTGGGTAGCGTTATTCTTAATGGTATACCCATACTTTATTAATCATCGACGACAACGCGTTTAGCAAAC +ATCTTTAGACATAGTGGAGGTATATAGAAAAATGAGAATTAAAACACCGAGTCCATCGTATTTAAAAGGCACAAATGGAC +ATGCGATATTATTATTACATTCATTTACAGGTACAAATCGGGATGTGAAGCATCTTGCAGCTGAGTTAAATGACCAAGGA +TTTAGTTGTTATGCACCGAATTATCCAGGTCATGGTTTATTGTTGAAAGATTTCATGACATATAATGTAGATGATTGGTG +GGAAGAAGTTGAGAAAGCTTACCAATTTTTAGTCAATGAAGGTTATGAATCTATCAGTGCAACGGGTGTGTCTTTAGGTG +GATTAATGACATTAAAATTGGCGCAACACTATCCTTTGAAACGTATCGCTGTCATGTCAGCACCAAAGGAAAAGAGTGAC +GATGGCTTAATAGAACATTTAGTTTATTATAGTCAACGCATGTCGAATATTTTAAATTTAGATCAGCAAGCATCGAGTGC +GCAATTAGCAGCAATTGATGATTATGAAGGTGAAATTACGAAGTTTCAACATTTTATTGATGATATCATGACAAATTTAA +ATGTTATTAAAATGCCAGCTAATATATTATTTGGTGGTAAAGATGCGCCATCCTATGAAACAAGTGCACATTTTATTTAT +GAACATTTAGGATCAGTAGACAAAGAATTAAATGGTCTGAAGGATTCGCATCATTTAATGACGCATGGAGAAGGCAGAGA +TATTTTAGAAGAAAATGTTATTCGCTTTTTCAATGCTTTAACATAATTGAAGATTTTAAAAATTAACACTTGAGTGACGA +TGTTATTATATTTGTCATTTGAGTGTTTTTTGTGGCGTGATAGTATAGGGAACTTAGCTAAAGTTAATTTGTAGTGTGAT +GTCGTGGAAATATTATGGAGTTATACCAGAAGTTGATTTGTATTGTAATGTCGTGGAAATTTTATGGGCATTGCACCTGA +AGTTGTTATGTGGCGAGATGTTGGGGATATAGATGGTGTTGTATGTGGCGAGATATTGGCGCACGTATACAGCGTTGAAC +CAGAAAATTTAATGTAGCGTAATGACAACACATTACAAGAGTTGATTATATTATGATAAATCCAGGTGCTACCGTATTAA +TGAAACAATTTTGATGCAGGTGTATTGCATGTTATTTACATACATGTTAGAGTGTTACTTAAACACGTAGACGCTTTAAC +GCTTTAAAGGAGATGTGAATGATGAAAAGACAACAATCACAATGGAAGTCATCAACTGGATTTATTTTAGCTAGTGCGGG +TTCTGCAATCGGTCTTGGTGCCATGTGGAAATTCCCATATATGGCAGGGATTTATGGCGGCGGTGCCTTTCTAGCTATGT +TCTTAATATTCACCATTTTTGTTGGGTTGCCATTACTCATTATGGAATTCACTGTTGGGAAAATGGGACGGACATATACA +ACACAAATATATAGTAAATTAACTGGTAAAAAATGGCTCAATATCATTGGCTGGAACGGTAATTTGGCAGTGTTTATTTT +ATTTGGCTTCTATAGTGTTATCGGTGGTTGGATTGTCATTTACATCGGACAAGTATTATGGCAATTAGTTATATTTCAAC +GCATCAATCATCTCCAAGAAATGAATTTTGAAGCGGTAATATCAAATCCTTGGTTAACCGTTCTAGGGCAAGGTATATTC +ATATTCGCTACGATGATTATTGTCATGTTAGGTGTTGAAAAAGGATTAGAAAAGGCATCGAAAGTTATGATGCCATTGCT +GTTTGTCTTTTTAATCGTCATTGTGATTAAGTCTTTAACATTAGATGGCGTCTTAGAAGGTGTGAAATTTATTTTACAAC +CAAGAGTATCAGAGATTACTGCTGATGGCATCTTGTTTGCGCTAGGTCAATCATTCTTTACGTTATCATTAGGAACTACA +GGTATGATTACTTATGCGAGTTATGCCTCTAAAGACATGACGATTAAGTCATCAGCTATTTCTATCGTTGTTATGAATAT +CTTTGTATCTGTATTGGCAGGTCTAGCTATATTTCCGGCTTTACATAGTTTTGGCTATGAACCACAAGAAGGGCCTGGAT +TATTATTTAAAGTACTGCCAATGGTCTTTAGTCAAATGCATCTAGGCACATTATTCTATTTGGGATTCTTAGTGCTGTTC +TTATTTGCGGCTTTAACGTCATCTATTTCTTTATTAGAATTAAATGTTTCTAACTTCACGAAGAATGACAATACAAAACG +TAAAAAAGTCGCAGTGATCGGTAGTATTTTAGTATTTATCATTAGTATTCCAGCAACCTTATCTTTTGGTATCTTAAAAG +ATGTAAGATTCGGTGCGGGAACGATTTTTGATAATATGGATTTCATCGTTTCGAATGTATTGATGCCATTAGGCGCATTA +GGTACTACGCTTGTCGTAGGACAATTATTAGATAAAAAATTATTACAACAATATTTTGGTAAAGATCGATTTAGATTATT +CAGTGGTTGGTATTACTTAATTAAGTATGCGATGCCTGTCGTTATTATTTTAGTCTTTATCGTGCAATTATTTAGTTAAT +ATATAGAAGCATCTGACACACAAGTATTTGTGTTGCTCAGGTGTTTTTTTGTTTGGAATTGAAAATTTAGTACTGGTTCT +GAATTAATCGAAACTTAGATAATTGCAAAATAACAAAATGTCGTTTATAATTCTTATCTGAAAAGTAAGAATTAAAATTG +AGACATGAAGAAAGGTTTCATTCATTTGATCGGCATGTCAGATGAGGGTGCATCTATGATTACTTATGATTTAATTGGCA +ATACACCATTAGTACTGTTAGAACATTATAGTGATGATAAAGTTAAAATTTATGCCAAGCTTGAACAATGGAATCCTGGA +GGCAGTGTTAAAGACAGACTCGGGAAATATTTAGTAGAGAAGGCAATTCAAGAAGGGCGTGTGCGTGCAGGTCAAACTAT +TGTTGAAGCGACTGCTGGTAATACAGGCATAGGGTTAGCTATTGCAGCGAATAGACATCATTTGAAATGTAAGATCTTTG +CGCCGTATGGTTTTTCAGAAGAAAAGATTAATATTATGATAGCGCTTGGTGCAGAAGTTTCAAGGACGAGTCAGTCTGAA +GGTATGCATGGGGCACAATTAGCTGCACGTTCCTATGCTGAAAAATATGGTGCCGTTTATATGAATCAATTTGAATCCGA +ACATAATCCGGATACATATTTTCATACATTGGGACCCGAATTGACTTCAGCATTACAGCAAATTGATTATTTTGTGGCTG +GTATTGGCTCTGGCGGTACATTTACAGGTACCGCACGTTATTTAAAGCAACATCACGTGCAATGTTATGCCGTTGAGCCA +GAAGGGTCCGTGTTAAATGGAGGGCCAGCTCATGCACATGACACTGAAGGTATCGGTTCTGAGAAATGGCCGATATTTTT +AGAGAGACGTCTTGTAGATGGGATATTTACGATTAAAGATCAAGATGCCTTTCGAAATGTCAAAAGTTTGGCTATAAATG +AAGGGTTGTTAGTAGGCAGTTCTTCAGGTGCAGCATTACAAGGTGCATTGAATTTAAAAGCGCAATTATCTGAAGGTACG +ATTGTTGTCGTATTTCCAGATGGTAGTGATCGATATATGTCTAAGCAAATATTTAATTATGAGGAGAATAATAATGAACA +AGAAAACTAAATTAATTCATGGTGGGCACACAACAGACGATTATACAGGTGCCGTTACAACACCAATTTATCAAACAAGT +ACATATTTACAAGATGATATTGGTGATTTACGTCAAGGATATGAATATTCTCGTACTGCGAATCCAACAAGAAGTTCTGT +AGAAAGCGTTATTGCGACATTAGAAAATGGCAAACATGGCTTTGCATTTAGTTCAGGTGTTGCAGCAATCAGTGCAGTTG +TTATGCTGTTGGACAAAGGAGATCATATTATTTTAAATTCAGATGTATACGGCGGTACTTATCGCGCATTGACAAAAGTA +TTTACACGATTTGGCATTGAAGTGGATTTTGTAGATACAACGCATACAGATTCAATTGTACAAGCGATACGCCCAACAAC +AAAGATGTTGTTTATTGAAACACCTTCTAATCCATTATTACGTGTTACTGACATTAAAAAGTCTGCTGAAATTGCGAAAG +AACACGGTTTGATTTCAGTTGTTGATAACACATTTATGACACCTTATTATCAGAATCCATTAGATTTAGGTATCGATATT +GTCTTACATTCTGCAACGAAATATTTAGGTGGACATAGTGATGTCGTTGCTGGTTTAGTTGCAACATCGGATGACAAGCT +TGCAGAACGTTTAGCATTTATTTCAAATTCAACAGGTGGCATTTTAGGACCTCAAGATAGCTATTTACTTGTGAGGGGTA +TTAAAACATTAGGTTTACGTATGGAACAAATTAATCGCAGCGTTATTGAAATTATTAAAATGTTACAAGCACATCCAGCT +GTGCAACAAGTGTTCCATCCAAGTATTGAAAGTCATTTAAATCATGATGTCCATATGGCTCAAGCGGATGGCCATACAGG +TGTGATTGCATTTGAAGTGAAAAATACAGAAAGTGCCAAACAATTGATTAAAGCAACATCGTATTACACATTAGCTGAAA +GTTTAGGTGCAGTGGAAAGTTTAATTTCAGTACCTGCATTGATGACACATGCATCCATTCCAGCAGATATTCGAGCTAAA +GAAGGTATTACAGACGGACTTGTAAGAATTTCTGTAGGTATTGAAGATACTGAAGATTTAGTCGATGATTTAAAACAAGC +ACTAGATACTTTATAAATAATAGCAGCACTGGCATATATTTTGAGTTCAACGTTTGTTGTTCTTGAATATGCTAGTGCTT +TTTTGTGCGATGTTAATATTTAGTAATTGCGTTGTGGAGGATTGGTGGACATTTTTATGGATTTAATTTGATAAATGTCA +TAGTAGTCTCACAATTCGTCATTGTCACATGAATTACTTATTTTTTAATTTTTTAGAAAATTCGGCAAATTGTATTGACG +ATATATGCAATTTTACATATAATGCTCTTATTCCTAGTGGATTAATAAGAATTTGTAGGAGGGGCGATGATGATTGAGTT +TCGACAGGTAAGTAAGACCTTTAATAAAAAGAAGCAAAAAATAGATGCTTTGAAGGACGTATCATTTACGGTCAATCGCA +ATGATATTTTTGGTGTGATTGGATATAGTGGTGCAGGAAAAAGTACGTTGGTAAGACTCGTGAATCATCTTGAAGCTGCC +TCGAATGGACAAGTGATTGTAGATGGACATGATATTACGAATTATAGCGATAAAATGATGAGGGATATTAAGAAAGATAT +CGGTATGATATTTCAGCATTTCAATTTATTAAATTCAGCTACCGTATTTAAAAATGTAGCAATGCCACTCATTTTAAGTA +AGAAAAGCAAAACAGAAATTAAGCAACGAGTAACGGAAATGCTTGAATTTGTAGGATTGAGTGATAAAAAAGACCAATTT +CCTGATGAATTATCTGGTGGGCAGAAGCAAAGGGTGGCTATTGCAAGAGCGCTTGTTACTAATCCGAAAATACTCCTATG +CGATGAAGCAACAAGCGCATTGGATCCAGCAACGACTGCTTCGATATTGACGTTATTAAAGAATGTCAATCAAACCTTTG +GCATTACAATTATGATGATTACACATGAAATGCGCGTTATTAAAGACATTTGTAATCGTGTTGCTGTAATGGAAAAGGGG +AAAGTGGTTGAAACAGGAACTGTTAAAGAGGTGTTTAGTCATCCTAAAACGACGATTGCTCAAAATTTTGTGTCTACAGT +TATACAGACTGAGCCAAGTACATCATTGATTCGTCGATTGAATGACGAACAAGTTGGCGATTTTAAAGATTATAAAATCT +TCGTCGAGGAAACTCAGGTGACACAACCGATTATAAATGACTTGATTCAAATTTGTGGCAGAGAGGTTAAAATTTTATTT +TCATCTATGTCAGAAATACAAGGTAACACCGTATGTTATATGTGGCTTCGATTTAATATGGATCAACAATTTGAAGACAC +GGCAATAAATCAATATTTCAAAGAGAAAAATATTCAATTTGAGGAGGTGCATTAACATATGTTTGGTTCTGATTTAGACA +GTGCACAGTTATTACAAGCATTGTACGAAACGCTATATATGGTATCTATTGCTTTATTTTTAGGAGCAGTGATTGGTATT +CCATTAGGTGTCTTATTGGTAATTACTCGAAAACAAGGCATATGGCCCAATATAGTGATACATCAAGTTTTAAATCCTTT +AATCAATATTTTAAGGTCACTACCATTTATTATTTTGTTAATTGCGATTGTGCCATTCACAAAATTAGTAGTAGGTACTT +CAATTGGTACGACTGCTGCCATCGTGCCTTTAACAGTATATGTGGCACCTTACATTGCAAGACTTGTTGAAAACTCATTA +TTGGAAGTAGACGAGGGGATTATTGAAGCGGCGAAAGCGATGGGCGCTTCACCACTACAAATCATTAGATATTTTTTAAT +TCCTGAAGCTTTAGGTTCGTTAGTATTAGCAATTACCACTGCGATTATTGGACTTATTGGAAGTACGGCGATGGCAGGAG +CTGTTGGCGGTGGTGGTATAGGAGACTTAGCTTTAGTGTATGGTTATCAAAGATTTGATACGACGGTCATTATTATTACC +GTTATTGTATTAGTCATTATTGTCCAAGTGATTCAAACGCTAGGGAATGTTCTAGCTAGATTCATACGTAGACATTAATG +ATATATAGTGAAGATTTTGAAAGGAATTGATAGAATGAAAAGATTGATTGGGTTAGTTATCGTAGCACTTGTATTATTAG +CAGCGTGTGGTGGTAACAATGATAAAAAAGTAACAATTGGTGTCGCATCAAATGACACTAAGGCTTGGGAGAAGGTTAAA +GAATTAGCTAAAAAAGATGATATTGATGTGGAGATTAAGCACTTCTCAGATTACAATTTACCGAATAAAGCATTAAATGA +TGGTGATATTGATATGAATGCATTCCAACATTTTGCATTTTTAGATCAATATAAAAAGGCGCATAAAGGAACAAAGATTT +CAGCATTAAGTACAACAGTTTTAGCACCGTTGGGCATTTACTCAGATAAAATTAAAGATGTCAAAAAGGTTAAAGATGGT +GCTAAAGTTGTCATTCCAAATGATGTGTCAAACCAAGCACGTGCACTTAAACTATTAGAAGCAGCTGGTTTAATAAAACT +GAAAAAAGATTTCGGATTAGCAGGCACGGTGAAAGATATAACGTCAAATCCAAAACATTTAAAAATTACTGCAGTAGATG +CACAACAAACTGCACGTGCTTTATCTGATGTCGATATTGCAGTTATTAATAACGGTGTAGCAACTAAAGCGGGTAAAGAT +CCTAAAAATGATCCGATATTTTTAGAAAAATCAAATTCAGATGCAGTAAAGCCATATATTAATATTGTTGCAGTTAATGA +CAAAGACTTGGATAACAAAACATATGCTAAAATCGTAGAATTGTATCATTCAAAAGAAGCTCAAAAAGCGTTGCAGGAAG +ATGTCAAAGATGGAGAGAAACCTGTTAATTTATCTAAAGATGAGATTAAGGCAATAGAAACGTCATTAGCAAAATAAATT +ATATTGCGTCCTACAAGCAAAGTTCATGCTTATGTTTGTAGGGCGTTATTGTTGGAGAATAAAATTATTTCCAATAGAGA +AAGGGATTGTAATCATTTTATAGTGAAATATTATGAAATTGTAATAATTTAGATATTGTAAAATCTAATAAGTTGTAATA +ATTTTAAGGGATAATTATAAAATTTGATGATACAGTATATGATTTTTTTGTAATCATAATGTCATCAAACATCAACCTAT +TATACATAATAAAATCGTATAATGATGTAGTATTCATAAATTCGGATAAAAGAATGTTAGGAAAGTTAAGCAAGAGGAGG +ATTTTAAAGTGCAAAAAAAAGTAATTGCAGCTATTATTGGGACAAGCGCGATTAGCGCTGTTGCGGCAACTCAAGCAAAT +GCGGCTACAACTCACACAGTAAAACCGGGTGAATCAGTGTGGGCAATTTCAAATAAGTATGGGATTTCGATTGCTAAATT +AAAGTCATTAAACAATTTAACATCTAATCTAATTTTCCCAAACCAAGTACTAAAAGTATCTGGCTCAAGTAATTCTACGA +GTAATAGTAGCCGTCCATCAACGAACTCAGGTGGCGGATCATACTACACAGTACAAGCAGGCGACTCATTATCATTAATC +GCATCAAAATATGGTACAACTTACCAAAACATTATGCGACTTAATGGTTTAAATAATTTCTTTATTTATCCAGGTCAAAA +ATTAAAAGTATCAGGTACTGCTAGCTCAAGTAACGCTGCGAGCAATAGTAGCCGTCCATCAACGAACTCAGGTGGCGGAT +CATACTATACAGTACAAGCAGGTGACTCATTGTCATTAATTGCATCAAAATATGGTACAACTTATCAAAAAATTATGAGC +TTAAATGGCTTAAATAATTTCTTTATATATCCGGGTCAAAAATTGAAAGTAACTGGTAATGCATCTACGAACTCAGGATC +TGCAACAACGACAAATAGAGGTTACAATACACCAGTATTCAGTCACCAAAACTTATATACATGGGGTCAATGTACATATC +ATGTATTTAATCGTCGTGCTGAAATTGGTAAAGGTATTAGTACTTATTGGTGGAATGCTAATAACTGGGATAACGCAGCG +GCAGCAGATGGTTACACTATCGACAATAGACCTACTGTAGGTTCTATCGCTCAAACAGATGTAGGTTACTATGGTCATGT +TATGTTTGTAGAACGTGTAAATAACGATGGTAGTATTTTAGTTTCAGAAATGAACTATTCAGCTGCACCAGGTATTTTAA +CTTACAGAACGGTACCAGCTTACCAAGTAAATAATTATAGATATATTCACTAAAGTCTTACGTATATAAATATATAATGA +ATTCCTATTACATTTCACAAGCTGAAAAGTTGGATTGAATCCTAAATTTTATGGCGGGGCTATCAAAGTCGTGATTGTTG +TAATAGGAATTTTTGTATATGAAAAAGGATTGGTCGAATGAACTTCATGTTCTATGTTCGACCAATCCTTTTCTGAATTA +GTATTCATGTTTTACTTTGCGAATGCTTGTAAATAATCTAGCACCGTTTGTTATTAAAGTAACAACTGCCACTGCTTTTT +TGAACTTACGTGGTGACTTAATTGAAATGTAAAAGTCTAACACGGTTCCTACAGTGCTTAAACCTAATGAAAGATATACT +AATTTTTTATTTTTAGCATGATATTTATAGCCATTGTAGCCGTCGACTATGAAACCTGCGACATTTAGTAAACTTGATAA +ACGTTGTGATTTGGAACGTTTTGCCATAATAATAATTCCCCCTAGTATCTTCTGTTTATATGAAAAGTTGGGTGCGCTGA +ATTGCTAACGTTTTGCGCTATAACTACTCATATATGATAACATAATTGTACAGTATAATTTGAAAAATTGATTTCACAAA +GTTGGGGTGTCAAAGATGATTAAATGTGTCTGTTTAGTTGAAGAAACAGCTGATAAAATATTGCTTGTTCAAGTAAGGAA +TCGCGAAAAGTATTATTTCCCGGGTGGTAAAATAGAAGAAGGGGAATCACAAGTACACGCGCTGTTAAGAGAAGTAAAAG +AAGAATTAAATTTAACATTAACAATGGATGAAATTGAATATGTCGGGACAATTGTAGGTCCTGCATATCCACAACAGGAT +ATGTTAACTGAGTTAAATGGATTTCGCGCATTAACCAAAATCGATTGGGAAAACGTAACTATCAATAATGAAATTACGGA +TATACGCTGGATTGATAAAGATAATGATGCGTTGATTGCGCCTGCTGTCAAAGTTTGGATTGAAACTTATGGTGGTAAAC +ATGACAAATAATGACACCATCATGTTACGACATTATGTCCCACAAGATTATTCGATGTTAGAAGCTTTTCAATTAAGTGA +AAGTGATTTGAAGTTTGTTAAAACGCCAGAGGAAAATATTACAGCTGCAATGTCTGATAATGAAAGGTATCCCATCGTTG +TAATGGATGGCAGGCAATGTGTGGCCTTTTTTACATTACATCGTGGAAAAGGGGTCGCACCATTTAGCGATAACCAAGAT +GCAGTATTTTTCAGGTCATTTAGTGTTGATCAACGTTATCGTAATAGAGGAATAGGTAAAGTGGTAATGGAAAAATTGGC +GTCATTTATCACTTCAACATTTCAGGATATTAATGAGATTGTGTTAACGGTTAATACTGACAATCCACATGCCATGGCAC +TTTATCGCCAACAAGGATATCAATATATGGGAGATAGTATGTTCGTCGGAAGACCTGTTCATATTATGGCGTTAACTATA +AAATAAATTAAATTTAAAAGCATCTTTACTCATCGTCGACCACAACAATTAATGATGAATAAAGGTGCTTTTTGTTATAG +ATCATCGGACAATTTACTATAGTAAAAAGCGACCTAGTGAACAATTGACATATATCCACAGGTCGCTTAACTTAAGTTAT +ATTGCTAGTTGCGATTAATTGATAGACTCATCATTTTTGCGCTGTCGAGATGGTCTTTTTATTAAAAATGCCGTAATCCA +AGCCGTAATCGGAATACTGATTGCAACGGCAATACCGCCTAAAATAATAGAAATAAATTCTTGGGCAAATATTTTCGAGT +TTATAATATGACCAAATGAATATTTAAGTTTGAAAAACCAAATAAATAAAGCAAGTTGGCCACCAAAAAAGGCAAGGTAA +ATCGTGTTCGCAGATGTCGCTAAAATTTCTCTACCAACACGCATGCCAGATTGGAATAATTCGTATTGCGTAAGCGTTGG +ATTCACTTGATGCAATTCATAAATGGGTGAACTAATGGTAATTGTTAAATCTATCACAGCTGCAATAACAGCAAGAATAA +TAGTGAACACCATAAATTGAACCATATCAATGCCAATATTCATTGAATACACATATGTTTCATCTTGTTGTTCGGTTGAA +AAGCCTTGTAGATGACCGAAGTAGACCGATAAATAAATGAGTGTAATCAACAATATTGTTGTAACGATAGTGCTGATAAA +TGCAGCTTGTGTTTTAACATTGTAACTATTGAGTACGAATAAATTACAAGCGCCAATAATAATGCAGAAAAAGAATGTGA +CGACATAAATCGGTACGCCAAAAATAATCAATACAATACTAATAATTAAAATAGCGAAATTTAAAAATAGGGTTAAATAA +GAGATGAATCCCTTTTTACCTCCGAAAATTATCATCAGAAAGAGGAGCAATAACGCCAATATAAATACAGCATTCATTGT +TTCGCCCTCCTTAATGTTTCAAATATTTCCATAAACAATATTGTGATAGGAATTGTAAGTACGATACCTATACCACCTGT +TAGTGCGCGCGCAATTTCTAACGACCAATTCATCGAAATAGTATAAGTCACTGTATTGGCATTTTTTAAAAAGATTAAAA +ACATAGGTAGTGCACCGGATAAATATGAGAATAATAAGATGTTAGTCATTGTTCCCATAATATCTTGGCCGATGTTTCGC +CCAGCAAGCGCCCATCTCCTCATTGAAATGTGTGGCGTACGCTGTAAAATTTCATGCATACCACTAGCAATTGTAATTGC +AACATCCATAATAGCGCCAAGTGAACCTATTAACACTGAGGCTAGGAAGATATCTTTCGGTGGTAATGATAAAAAGTTCA +TCGTTTCATATTTAATGCCTTTACCATCTGTCATATATATGATTAATTCTGTTAAACCTATACTCAAAAAAGTTCCGATA +ATTGTACTGGCTATGGTAATGAGTGTACGCATATGCCAGCCTGTAACGAGCAATAAAGTGAGTATTGTTGAACAGATCAT +GGCAATGGTCATGAGTAAGAATAAATTAATATTGCTATGTTGAATATGAATGTAAATTGCGATTAATATGGCAATAGAAT +TCAAGATTAACGATAAAATCGATTGCAGTCCGACTTTGCGACCAACCAATAATACAGTTAATAAGAACAAACCAGTGATG +ATAACCGTTAAGGTATCACGCTTCTTTTCTATAATATAAGCATCACTCGGCTTGTTAGAAATATGTAATAATACTTTTTC +GTGTGTGCGAAATGCCTCAGAATCTGCTTGCGATTTGACGTACTGATGATTAATCGTCGTCGTTTCTCCAGCAAATTGAC +CATTTAATATTTTGACTTTTAATTGATTTTTATATTTAATATCACGATTATTTTGTGCATCTTTTGTAGGTGTCGAAGAA +ACATGTTTGACATCTATAATTTGACCAATTGGTTTGTTGTAAAAGTTCTCATTATTGAATGTAAATAAAATAGCACCAAT +GAATGCGATGCAGAACAAACCTAAAATTATATTAAATGGCTTTGTAAATAAATTTCTATATTTCAAAAACAAAACCCCAA +TTCTATGAATGAATTAATATGGTGATTATACGCCCTTAATTTTTTATTTTCAAAGATATTACTGCTAAGTGTAAAACGAA +AATCATCATTGATAGCATCGAATTACTTAATGGAATGTAGACGTTTTAGTCATTAATTGCTGAATAAGTGTTAATAATAT +GCCAATATCACTCTTTGTATAAGGCTCCTTTGTAATAGCACATATCGTTCTTTTTAATTCAGTATGATCTAATTTTATAT +CTATCCATGATTTAGATTCTGGTAAATGTATATTTTGTGATGAAATGATGTAACCTTCTTTTTGACGAAGGAGATACTGC +GCAAGTGGTTGGCTACTGATTGTGTATACATCTGATTTAGTAATCTTGCGCAATTGTTTTTTTACAGTTTCGGCAAATGG +TGCCAAGCAATAAATATGACTATGCTCAAACTGAATTAATGGTGGGTGTGTCGCCATCGTAATTGGATCGTCTGAAGGCG +CATATAAATGATAGTGCTCTTCGAATAAAGGTAGCATATGTAATTGTTTGTGTTTACGTATTTCTGGTGTAAGTTCCGTG +AAACCAATGTCTATATTCCCATTTAATACGCTATTTATAATTGTGTCATGTTCTAATAAGCTCGGTATGACATGTGTATC +ATTTTGTAAATGAAACGTTTGGATAAGTGGTAGTAACATGTGGGATACGTCACTCTCATCATAGCCAATGTAGATACTTT +TATTTTTAGTTAATCCATGGCTTTGAAATTGTTCAATCGTGCTATCTAAATGTTCAATAATACGCAGAGCTTCATTAAAT +AATAATTTCCCTTCAGATGTGAGCGTAATATTGCGTCCTTGCTTTTTAAATAAAGACACATTAAGTTCTTGTTCTAATAA +TGTAATTTGACGGCTTATCGCTGATTGAGCAATGTTTAGTTCAAGTGCTGTTTCGGAGATATGTTCTCTTTTAGCGACCT +CGATAAAATATCTTAATTGTTTAATTTCCATAGCGATATAGGCACCTCCAAAAATGAGTGTTTTGTAACTATTATAGCAA +TATTATTGATAAATGTTCTATTTTTTAGATGAATATCTTCTATTTTATATATTGAACAGATAAATTTTTTAGATTATAGT +AATTATCATTAATAACTAATATCAGAATATTCTAAAAAAGGGGTGTGCATCATGCACAATGAGAAATTAATTAAAGGCTT +ATATGACTATCGTGAGGAACATGATGCGTGTGGTATTGGTTTTTATGCGAATATGGATAATAAAAGGTCTCACGACATCA +TTGATAAATCGCTTGAAATGTTGCGACGCTTAGATCACAGGGGCGGGGTCGGCGCAGATGGCATCACTGGTGATGGCGCA +GGTATTATGACTGAAATACCTTTTGCATTTTTCAAACAACATGTAACGGACTTTGATATCCCAGGTGAAGGTGAATATGC +CGTGGGGTTATTTTTTTCCAAAGAACGCATTTTAGGTTCTGAACATGAAGTAGTTTTTAAAAAATATTTTGAAGGCGAAG +GGTTATCAATTCTTGGTTATCGTAATGTACCAGTTAATAAAGATGCCATTGCTAAACATGTAGCAGATACGATGCCAGTC +ATTCAACAAGTGTTTATTGATATTAGGGACATTGAAGATGTTGAAAAGCGTTTGTTTTTAGCGAGAAAACAATTAGAGTT +CTATTCGACTCAGTGCGATTTAGAATTGTATTTTACGAGCTTATCACGCAAAACAATTGTATATAAAGGTTGGTTACGAT +CAGACCAAATTAAAAAACTATATACAGATTTATCGGATGATTTATATCAATCAAAGCTAGGGTTAGTGCATTCGAGATTT +AGTACGAATACATTCCCGAGTTGGAAAAGGGCACATCCTAACCGTATGTTAATGCATAATGGTGAGATTAACACGATTAA +AGGTAATGTAAACTGGATGCGAGCACGCCAACATAAATTAATCGAAACATTATTTGGCGAGGATCAACATAAAGTGTTTC +AAATTGTCGATGAGGATGGTAGTGACTCTGCCATTGTAGATAATGCGCTAGAGTTCTTATCGTTAGCCATGGAGCCAGAA +AAGGCAGCGATGTTACTCATACCTGAACCTTGGTTATATAATGAAGCGAATGATGCAAATGTACGTGCGTTTTATGAATT +TTATAGTTATTTAATGGAACCGTGGGATGGTCCTACAATGATTTCGTTCTGTAACGGTGACAAACTTGGCGCGCTTACAG +ATAGAAATGGATTACGTCCAGGTCGTTATACGATTACTAAAGATAACTTTATTGTCTTTTCATCTGAAGTGGGTGTTGTG +GACGTACCTGAAAGTAATGTTGCTTTTAAAGGTCAATTGAATCCTGGAAAGTTATTGCTTGTTGATTTTAAACAGAATAA +AGTCATTGAAAATAATGATTTAAAAGGTGCGATTGCTGGAGAATTACCATATAAAGCGTGGATTGATAACCATAAAGTTG +ACTTTGATTTTGAAAATATACAATATCAAGATTCGCAATGGAAAGATGAGACGTTATTTAAATTACAACGTCAGTTTGCA +TACACGAAAGAAGAGATTCATAAGTATATTCAGGAACTTGTAGAAGGTAAGAAGGATCCTATCGGTGCAATGGGATATGA +TGCGCCAATTGCAGTGTTGAACGAGCGACCAGAATCACTATTTAATTACTTTAAACAGCTGTTTGCACAAGTTACGAATC +CACCAATTGATGCGTATCGTGAAAAAATCGTAACGAGTGAACTTTCTTATTTAGGTGGCGAAGGTAACTTACTAGCACCT +GACGAAACGGTTTTAGATCGTATTCAATTGAAAAGGCCGGTATTGAATGAATCACACTTAGCAGCGATTGATCAGGAACA +TTTTAAATTAACTTATTTATCAACGGTATATGAAGGGGATTTGGAAGATGCGTTAGAAGCATTAGGCCGAGAAGCAGTGA +ATGCTGTAAAGCAAGGCGCTCAAATTCTAGTGTTAGATGATAGTGGATTAGTTGATAGCAATGGCTTTGCAATGCCGATG +TTACTCGCAATAAGTCATGTGCATCAATTACTTATTAAAGCAGATTTACGTATGTCTACAAGTTTAGTCGCTAAATCTGG +TGAGACACGAGAAGTGCATCATGTTGCTTGTTTACTCGCATATGGCGCGAATGCAATTGTGCCATACCTAGCGCAACGTA +CAGTTGAACAACTGACATTGACAGAAGGGTTACAAGGCACCGTTGTCGATAATGTTAAGACATATACGGATGTATTGTCA +GAAGGTGTCATTAAAGTAATGGCTAAGATGGGAATTTCGACAGTGCAAAGTTATCAAGGGGCACAAATATTTGAAGCGAT +TGGCTTGTCTCATGATGTGATTGATCGTTATTTTACTGGGACACAGTCTAAGTTATCTGGTATTTCGATTGATCAAATTG +ATGCTGAAAATAAAGCACGTCAACAAAGTGATGATAATTATCTTGCATCAGGTAGTACATTCCAATGGAGACAACAAGGT +CAACATCATGCTTTTAATCCGGAATCTATTTTCTTATTGCAGCACGCATGTAAAGAAAATGACTATGCGCAATTTAAAGC +ATACTCTGAAGCGGTGAACAAAAATAGAACAGATCACATTAGACATTTACTTGAATTTAAAGCATGTACACCGATTGACA +TCGACCAAGTTGAACCGGTAAGTGACATTGTCAAACGCTTTAATACAGGGGCGATGAGTTATGGATCGATTTCAGCGGAA +GCACATGAAACGTTAGCACAAGCCATGAACCAATTAGGTGGAAAGAGTAATAGTGGTGAAGGTGGCGAAGATGCAAAACG +TTATGAAGTACAAGTTGATGGAAGCAACAAAGTAAGTGCGATTAAACAAGTTGCTTCTGGGCGTTTTGGTGTAACTAGTG +ATTATTTACAACATGCCAAAGAAATTCAAATTAAAGTTGCGCAAGGTGCAAAGCCTGGTGAAGGTGGTCAATTACCTGGT +ACTAAGGTATATCCGTGGATTGCGAAGACAAGAGGGTCAACGCCAGGTATCGGTCTGATTTCACCACCGCCACATCATGA +TATTTATTCAATAGAAGATTTAGCGCAACTGATACATGATTTGAAAAATGCGAATAAAGATGCAGATATCGCGGTAAAAT +TAGTTTCGAAAACAGGTGTTGGTACCATTGCATCTGGGGTGGCAAAAGCATTTGCAGATAAAATTGTCATCAGTGGTTAC +GATGGTGGTACAGGGGCTTCACCCAAAACGAGTATTCAGCATGCCGGTGTTCCTTGGGAGATTGGTTTAGCAGAAACACA +TCAAACATTAAAACTAAATGACTTAAGAAGTCGTGTTAAGTTAGAAACAGACGGTAAGTTATTAACTGGTAAAGATGTAG +CGTACGCATGTGCGCTTGGAGCGGAAGAATTTGGATTTGCAACTGCACCATTAGTGGTGTTGGGCTGTATTATGATGCGT +GTATGCCATAAAGATACATGTCCAGTAGGAGTTGCAACTCAAAACAAAGATTTACGTGCTTTATATAGAGGTAAAGCACA +TCATGTTGTTAATTTTATGCATTTTATTGCACAAGAATTAAGAGAAATTTTAGCATCTTTAGGTTTGAAACGTGTAGAAG +ACTTAGTTGGAAGAACTGATTTATTACAACGATCATCAACATTAAAAGCGAATAGCAAAGCGGCTAGTATTGATGTTGAA +AAACTGTTATGTCCTTTCGATGGGCCAAACACAAAAGAAATTCAACAAAATCATAATCTTGAGCATGGATTTGATTTAAC +AAATTTATATGAAGTAACGAAGCCATATATTGCTGAAGGGCGTCGCTATACAGGTAGCTTTACAGTAAATAATGAACAAC +GTGATGTAGGGGTTATTACAGGTAGTGAGATTTCGAAACAATATGGAGAAGCAGGACTTCCTGAAAATACAATTAATGTT +TATACGAATGGTCATGCTGGTCAAAGTCTTGCAGCATATGCACCGAAAGGCTTAATGATTCATCATACTGGAGATGCGAA +TGACTATGTTGGTAAAGGATTATCTGGTGGTACGGTCATTGTCAAAGCACCTTTTGAAGAACGACAAAATGAAATTATTG +CTGGTAACGTCTCATTCTATGGTGCGACAAGTGGTAAGGCATTTATTAACGGTAGTGCAGGAGAAAGATTCTGTATTAGA +AATAGTGGTGTAGATGTTGTCGTTGAAGGTATCGGCGACCATGGATTAGAGTATATGACTGGTGGACATGTCATTAATTT +AGGTGATGTAGGTAAGAACTTCGGTCAAGGTATGAGTGGTGGTATTGCTTACGTTATCCCGTCTGATGTAGAAGCTTTTG +TTGAAAATAATCAACTAGATACGCTTTCGTTTACAAAGATTAAACACCAAGAAGAAAAAGCATTCATTAAGCAAATGCTG +GAAGAACATGTGTCACACACGAATAGTACGAGAGCGATTCATGTGTTAAAACATTTTGATCGCATTGAAGATGTCGTCGT +TAAAGTTATTCCTAAAGATTATCAATTAATGATGCAAAAAATTCATTTGCACAAATCATTACATGACAATGAAGATGAAG +CGATGTTAGCTGCATTTTACGATGACAGTAAAACAATCGATGCTAAACATAAACCAGCCGTTGTGTATTAAGGAAAGGGG +GAGATACGATGGGTGAATTTAAAGGATTTATGAAGTATGACAAACAGTACTTAGGTGAATTATCACTGGTAGACCGTTTG +AAGCATCATAAAGCATATCAACAACGATTTACTAAAGAAGATGCCTCTATCCAAGGTGCACGATGTATGGATTGTGGAAC +GCCGTTTTGTCAAACCGGACAACAGTATGGTAGGGAAACAATAGGTTGTCCAATTGGAAACTACATTCCTGAATGGAACG +ACTTAGTGTATCATCAAGATTTTAAAACTGCTTATGAACGCTTAAGCGAAACAAATAACTTTCCTGACTTTACAGGGCGT +GTATGTCCTGCACCATGCGAAAGTGCTTGTGTGATGAAGATTAATAGAGAATCGATTGCGATTAAAGGTATTGAACGCAC +AATTATTGATGAAGCTTTTGAAAATGGTTGGGTAGCGCCGAAAGTTCCGAGTCGCCGTAGAGATGAAAAAGTGGCAATCG +TTGGAAGCGGTCCAGCAGGATTAGCTGCTGCTGAAGAACTTAATCTACTAGGATATCAAGTAACTATTTATGAACGTGCT +AGAGAATCAGGCGGTTTATTAATGTATGGTATTCCGAATATGAAACTTGATAAAGATGTGGTTCGACGTCGTATTAAGTT +AATGGAAGAAGCGGGCATTACTTTCATTAATGGTGTTGAAGTCGGTGTTGATATTGATAAAGCAACGTTAGAATCTGAGT +ATGATGCCATTATATTATGTACTGGTGCACAAAAAGGTAGAGATTTACCTTTAGAAGGACGCATGGGTGATGGTATACAT +TTCGCTATGGATTATTTAACTGAACAAACGCAGTTGTTAAATGGAGAAATTGATGATATAACAATAACTGCAAAAGATAA +GAATGTCATTATCATTGGTGCTGGTGATACAGGGGCAGACTGTGTAGCGACAGCATTAAGAGAAAATTGTAAATCGATTG +TTCAATTTAATAAATATACGAAATTGCCAGAAGCAATTACATTTACAGAAAATGCATCATGGCCTTTAGCAATGCCGGTG +TTTAAAATGGACTATGCGCACCAAGAGTACGAAGCTAAGTTTGGTAAGGAACCACGTGCATATGGTGTTCAAACAATGCG +TTACGATGTTGACGATAAAGGACACATACGTGGTTTGTATACTCAAATTTTAGAGCAAGGCGAAAATGGTATGGTCATGA +AAGAAGGACCTGAAAGATTTTGGCCTGCTGACCTTGTATTATTATCAATCGGCTTCGAAGGTACAGAACCAACAGTACCG +AATGCTTTTAACATTAAAACGGATAGAAATCGAATCGTGGCGGATGATACAAACTATCAAACTAATAATGAAAAGGTATT +TGCTGCTGGAGATGCTAGACGTGGTCAAAGTTTAGTTGTATGGGCAATTAAAGAAGGTAGAGGCGTAGCGAAAGCAGTAG +ATCAGTATTTAGCTAGTAAAGTTTGTGTATAATCTTTGTATGGAAATGGTGGTTACGTTGACGTTGTGACATGCTGAATC +GAGTTTGAAAAAATCTAGTATCTATCAACGTCACATGCCATCTTTGTAACCTAAAAACAAAGGTTTGTAAGACAACAAAT +AGATTAATTATAAGTAGTGATTTTTTACATTCGTTTATAGGTCAACTGTAGTGGAAGACAATGATTTGTGGTAATCATGT +AATGCTTAAAAACAATATTGACTTTTACAGAACGTTCATATATGATAAATATTGTGTTTAGGAGGAATACCCAAGTCCGG +CTGAAGGGATCGGTCTTGAAAACCGACAGGGGCTTAACGGCTCGCGGGGGTTCGAATCCCTCTTCCTCCGCCATCAATAT +TTATATTAAATTCTATATATAATGAAGGTAAGTGCTCAAATTTTGAGTATTTACCTTTTTTATTTGTCTTTGAATGGCTC +GTAATTTTTGATAATAGAAATGATAAGGCATTGAGATTGGAAGGGCATTTGGCTTGTGCAATATACATAGCTAAATGTCT +TTTTTGTTTTGTGAAATATGATGGATGGCTTGTGTGGACAAGTTTGCTATTTATAGATATGCATTTTTCAATTTAGGAGT +TGGCCATGCATCTACACTTTATAATGGTGAGAGCGTGGTGAGGTATTGTTAATAACGCAATTGTAGCGAGGAGTTATTGC +TACATATGTCGTTATGGCTCATTGATTTTCTGAAATGGCTACCCCAGATAATTGTGACAAAATAAAAATATTTTGTTGAA +AGCCTTTACATAACTTGTCTAGACAAGTTATACTCGTTTTAAGACATTAAGGGAGTGAAATATATGGCTGTAAAAAGAGA +AGATGTAAAAGCCATCGTAACCGCTATTGGGGGAAAAGAAAATCTTGAAGCTGCAACGCATTGTGTAACACGATTACGTT +TAGTGCTGAAGGATGAAAGTAAAGTTGATAAAGACGCATTAAGTAATAACGCGTTGGTCAAGGGGCAGTTTAAAGCAGAC +CATCAATATCAAATTGTCATTGGTCCAGGAACAGTCGATGAAGTGTATAAGCAGTTTATTGATGAAACAGGTGCTCAAGA +AGCTTCGAAAGATGAAGCGAAACAAGCAGCTGCACAAAAAGGGAATCCAGTACAACGTTTGATCAAATTGTTGGGGGATA +TTTTTATACCAATATTACCTGCGATTGTGACAGCTGGTTTGTTAATGGGAATCAATAATTTACTTACAATGAAAGGTTTA +TTTGGTCCAAAAGCACTTATTGAGATGTATCCACAAATTGCTGATATTTCAAACATCATTAATGTGATTGCGAGTACGGC +ATTTATTTTCTTACCAGCATTAATTGGTTGGAGTAGTATGCGTGTATTTGGTGGTAGTCCGATTCTAGGCATAGTCTTAG +GTTTGATTTTAATGCATCCGCAATTAGTATCTCAGTATGATTTGGCAAAAGGGAATATTCCGACGTGGAACTTATTTGGC +TTAGAGATTAAGCAGTTGAATTACCAAGGTCAAGTGTTGCCAGTTTTAATTGCAGCTTACGTTCTAGCTAAAATTGAAAA +AGGATTAAATAAAGTCGTTCACGATTCGATAAAAATGTTGGTCGTTGGACCCGTAGCGCTTTTAGTTACTGGATTTTTAG +CATTTATTATCATTGGACCAGTTGCGTTATTGATTGGTACAGGTATTACATCTGGTGTTACATTTATATTCCAACATGCA +GGATGGCTTGGCGGAGCAATATATGGATTGTTATATGCACCACTTGTAATTACAGGACTACACCATATGTTTTTAGCAGT +AGATTTCCAATTGATGGGTAGCAGCTTAGGCGGTACGTATTTATGGCCAATTGTTGCGATTTCCAATATTTGTCAGGGCT +CTGCAGCATTTGGAGCATGGTTTGTCTATAAACGTCGTAAAATGGTTAAAGAAGAAGGCTTGGCATTAACATCTTGTATT +TCTGGTATGTTAGGTGTTACTGAACCAGCCATGTTCGGTGTGAACTTACCTCTGAAATATCCATTTATCGCTGCGATATC +AACGTCTTGTGTATTGGGGGCAATCGTTGGTATGAATAACGTACTTGGAAAAGTTGGTGTTGGTGGCGTGCCAGCATTCA +TTTCAATTCAAAAAGAATTTTGGCCAGTATATCTTATTGTGACAGCTATTGCTATTGTTGTACCATGTATACTAACAATT +GTGATGTCTCATTTTAGTAAACAAAAAGCGAAAGAAATTGTTGAAGATTAATAAAATAAAAAAGGGGCGTTCGTTATTTG +GACGTCCTTTATTACGTTATAAGGTGGTAATTGTGTGTCGAAAGAAATAGATTGGAGAAAATCCGTTGTATATCAAATTT +ATCCTAAGTCGTTTAATGATACGACGGGGAATGGTATAGGAGATATCAATGGAATTATAGAAAAATTGGATTATATCAAG +TTATTGGGTGTTGATTATATTTGGTTAACACCAGTGTATGAATCACCGATGAATGATAATGGCTATGATATCAGCAATTA +TTTAGAAATCAATGAAGACTTTGGAACGATGGATGATTTTGAAAAGTTAATCAAAGTTGCGCATCAAAAAGACTTGAAAG +TGATGTTAGATATTGTCATTAATCATACGTCGACGGAGCATGAATGGTTTAAAGAAGCCCGTAAATCTAAAGATAACCCT +TATAGAGATTATTACTTTTTCAGATCATCTGAAGACGGGCCGCCAACAAATTGGCATTCTAAATTCGGTGGTAATGCATG +GAAGTATGATTCTGAGACAGATGAATATTATTTACATTTATTTGATGTCAGTCAAGCTGATTTAAATTGGGATAATCCGG +AAGTACGTCAATCGTTATATCGCATAGTCAATCATTGGATAGACTTCGGCGTTGATGGTTTTCGATTTGATGTCATTAAC +TTAATTTCTAAAGGTGAATTTAAGGACTCTGACAAAATAGGTAAAGAATTTTATACGGATGGTCCTAGAGTGCATGAGTT +TCTGCATGAATTAAATCGTCAAACGTTTGGTAACACTGACATGATGACTATAGGAGAAATGTCTTCGACGACGATTGAAA +ATTGTATTAAGTATACACAACCAGAACGCCAAGAATTGAATAGTGTTTTTAATTTTCATCATCTAAAGGTTGATTATGTT +GATGGTGAAAAGTGGACAAATGCGAAGCTTGATTTTCATAAGTTAAAGGAAATTCTGATGCAATGGCAACGAGGTATTTA +TGACGGTGGCGGATGGAACGCGATTTTCTGGTGTAATCATGATCAGCCACGGGTAGTGTCTAGATTTGGTGATGATACGT +CGGAAGAGATGAGGATACAAAGTGCTAAAATGTTAGCTATCGCACTGCATATGTTGCAAGGGACGCCATATATTTACCAA +GGTGAAGAAATTGGTATGACGGACCCACATTTTACATCAATAGCACAATATCGTGATGTTGAATCGATTAATGCCTACCA +TCAGTTGTTAAGTGAAGGGCATGCTGAAGCGGATGTGTTAGCGATTTTAGGACAGAAGTCACGAGACAATTCGAGAACGC +CTATGCAATGGAGTGATGATGTTAATGCTGGATTTACAGCTGGTAAGCCTTGGATTGATATTTCGGAAAATTATCATCAG +GTCAACGTTAGACAAGCACTTCAGAATAAAGAGTCTATTTTCTATACGTATCAAAAATTAATACAATTAAGACATACGCA +TGATATTATTACGTATGGAGACATTGTGCCACGTTTTATGGATCATGATCATTTATTTGTTTATGAACGTCATTATAAGA +ATCAACAATGGCTAGTAATTGCGAATTTCTCAGCATCGGCTGTTGATTTGCCAGAAGGATTGGCTAGAGAAGGTTGTGTT +GTGATTCAAACAGGCACAGTGGAAAATAATACGATAAGCGGGTTTGGTGCAATTGTAATCGAAACAAACGCGTAAAATAA +ATTGAGTGGATGCGTTTATATGGCGAAACAAAAAAAGTTTATGAAGATTTATGAGGCGTTGAAAGAAGATATATTAAACG +GGCAGATTCAATATGGTGAACAAATTCCGTCTGAACATGATTTGGTGCAATTGTACCAGTCATCTCGAGAGACCGTGCGT +AAGGCATTAGATTTGTTGGCATTAGACGGCATGATTCAAAAGATTCATGGTAAAGGGTCACTTGTCATTTATCAGGAGGT +TACAGAGTTTCCATTTTCTGAACTTGTTAGTTTTAAAGAAATGCAAGAAGAAATGGGCGTCGCATATTTAACTGAAGTTG +TTGTGAATGAGGTTGTTGAAGCGCATGAAGTTCCAGAAGTTCAACATGCTTTAAACATCAATTCTAGTGAATCACTCATT +CATATTGTTAGAACTCGTCGGCTTAACCAACATGTGAAGATTGTTGATGAAGATTATTTTCTAAAGTCGATTGTTTCAGA +TATAGGTAATGATGTTGCGAGTGATTCTATTTATGATTATTTGGAAAAGGTATTAAATCTTAATATTAGTTATTCAAGTA +AGTCTATTACTTTTGAACCGTTTGATGAACAAGCATATCAATTGTTTGGTGATGTATCGGTGGCTTATTCAGCAACAGTT +CGAAGTATTGTGTATTTAGAAAATACAATGCCGTTTCAATATAATATTTCAAAACATCTTGCAAATGAATTTAAATTTAA +TGACTTCTCAAGACGTCGTATAAAGTAAACAATGATATAAATGATTTATACTTGCAATTAACTATTAAAATATAGTAATA +TATATCTTGCCGTGCTAGGTGGGGAGGTAGCGGTTCCCTGTACTCGAAATCCGCTTTATGCGAGGCTTAATTCCTTTGTT +GAGGCCGTATTTTTGCGAAGTCTGCCCAAAGCACGTAGTGTTTGAAGATTTCGGTCCTATGCAATATGAACCCATGAACC +ATGTCAGGTCCTGACGGAAGCAGCATTAAGTGGATCATCATATGTGCCGTAGGGTAGCCGAGATTTAGCTAACGACTTTG +GTTACGTTCGTGAATTACGTTCGATGCTTAGGTGCACGGTTTTTTATTTTTTAAATATTAAACCGATTATTAAGAGTTGA +AAATATATAATTATAGAAGCTACTTTCTTGAAGACAATTCAGCGTATTATACGTGGAACATGTTTGTGGGAAGTAGCTTT +TTTATATGTGAAGTTTGATTCAAGTGAACTCGATGTGCAGTTTGAATGATTTTTGTGTCAATGAAAAGTAAGAAGTTATA +ATTTGATGATAAAGAAATGATGGTGAAATGAGGGGGAGTATCTTACAATAGAATTATTAATGAGATACGTTATGATTATT +GACAATCAAATGCCTACGGAGGACATATGCAAATATATTTAAGTACTTTAACAGAGTTAGATTATGATAAATCTTTAAAT +AGTATTGAAGAAAGTTTTGATGATAATCCTGAAACGAGTTGGCAAGCACGTGCGAAAGTAAAACATTTAAGAAAATCTCC +TTGCTATAATTTTGAATTAGAAGTAATAGCGAAAAATGAAAATAACGATGTCGTTGGACACGTTTTATTAATTGAAGTAG +AAATTAATAGTGATGATAAGACGTATTATGGTTTGGCGATTGCCTCTTTATCAGTTCATCCTGAATTACGTGGACAAAAA +TTAGGTCGTGGCTTGGTTCAAGCAGTAGAAGAGCGTGCCAAAGCACAAGAGTATAGTACGGTTGTTGTAGACCATTGTTT +TGACTACTTTGAAAAGTTGGGTTATCAAAATGCTGCTGAGCATGACATTAAATTAGAATCTGGTGATGCACCGTTACTTG +TAAAATATTTATGGGATAATTTGACGGATGCACCACACGGAATCGTAAAATTTCCAGAACATTTTTATTAATTGTTCAAT +TAAGAAGTAAAGGTATTATCATGCTATAATGAGAGGTAATTGTTTATGGAGGTGCTAACTTGAATTATCAAGCCTTATAT +CGTATGTACAGACCCCAAAGTTTCGAGGATGTCGTCGGACAAGAACATGTCACGAAGACATTGCGCAATGCGATTTCGAA +AGAAAAACAGTCGCATGCTTATATTTTTAGTGGTCCGAGAGGTACGGGGAAAACGAGTATTGCCAAAGTGTTTGCTAAAG +CAATCAACTGTCTAAATAGCACTGATGGAGAACCTTGTAATGAATGTCATATTTGTAAAGGCATTACGCAGGGGACTAAT +TCAGATGTGATAGAAATTGATGCTGCTAGTAATAATGGCGTTGATGAAATAAGAAATATTAGAGACAAAGTTAAATATGC +ACCAAGTGAATCGAAATATAAAGTTTATATTATAGATGAGGTGCACATGCTAACAACAGGTGCTTTTAATGCCCTTTTAA +AGACGTTAGAAGAACCTCCAGCACACGCTATTTTTATATTGGCAACGACAGAACCACATAAAATCCCTCCAACAATCATT +TCTAGGGCACAACGTTTTGATTTTAAAGCAATTAGCCTAGATCAAATTGTTGAACGTTTAAAATTTGTAGCAGATGCACA +ACAAATTGAATGTGAAGATGAAGCCTTGGCATTTATCGCTAAAGCGTCTGAAGGGGGTATGCGTGATGCATTAAGTATTA +TGGATCAGGCTATTGCATTTGGTGATGGTACGTTAACATTGCAAGATGCGTTGAATGTCACAGGTAGCGTACATGATGAA +GCGTTGGATCACTTGTTTGATGATATTGTACAAGGTGACGTACAAGCATCTTTTAAAAAATACCATCAGTTTATAACAGA +AGGTAAAGAAGTGAATCGCCTAATAAATGATATGATTTATTTTGTCAGAGATACGATTATGAATAAAACATCTGAGAAAG +ATACTGAGTATCGAGCACTGATGAACTTAGAATTAGATATGTTATATCAAATGATTGATCTTATTAATGATACATTAGTG +TCGATTCGTTTTAGTGTGAATCAAAACGTTCATTTTGAAGTGTTGTTAGTAAAATTAGCTGAGCAGATTAAGGGTCAACC +ACAAGTGATTGCGAATGTAGCTGAACCAGCACAAATTGCTTCATCGCCAAACACAGATGTATTGTTGCAACGTATGGAAC +AGTTAGAGCAAGAACTAAAAACACTAAAAGCACAAGGAGTGAGTGTCGCTCCTGTTCAAAAATCTTCGAAAAAGCCTGCG +AGAGGCATACAAAAATCTAAAAATGCATTTTCAATGCAACAAATTGCAAAAGTGCTAGATAAAGCGAATAAGGCAGATAT +CAAATTGTTGAAAGATCATTGGCAAGAAGTGATTGATCATGCCAAAAATAATGATAAAAAATCACTCGTTAGTTTATTGC +AAAATTCGGAACCTGTGGCGGCAAGTGAAGATCACGTACTTGTGAAATTTGAGGAAGAGATCCATTGTGAAATCGTCAAT +AAAGACGACGAGAAACGTAGTAGTATAGAAAGTGTTGTATGTAATATCGTTAATAAAAACGTTAAAGTTGTTGGTGTACC +ATCAGATCAATGGCAAAGAGTTCGAACGGAATATTTACAAAATCGTAAAAACGAAGGCGATGATATGCCAAAGCAACAAG +CACAACAAACAGATATTGCTCAAAAAGCAAAAGATCTTTTCGGTGAAGAAACTGTACATGTGATAGATGAAGAGTGATAC +ATGACAAGCGATATAATCGTATGTATAATGAAAGAAACATCATTTTATTGATAAATATTTATTGATTTTCAAGGAGGAAA +TGGAATATGCGCGGTGGCGGAAACATGCAACAAATGATGAAACAAATGCAAAAAATGCAAAAGAAAATGGCTCAAGAACA +AGAAAAACTTAAAGAAGAGCGTATTGTAGGAACAGCTGGCGGTGGCATGGTTGCAGTTACTGTAACTGGTCATAAAGAAG +TTGTCGACGTTGAAATCAAAGAAGAAGCTGTAGACCCAGACGATATTGAAATGCTACAAGACTTAGTGTTAGCAGCTACT +AATGAAGCGATGAATAAAGCTGATGAGCTTACTCAAGAACGTTTAGGTAAACATACTCAAGGCTTAAACATCCCTGGAAT +GTGATCATAGATGCATTATCCAGAACCTATATCAAAACTTATTGATAGCTTTATGAAATTGCCAGGCATTGGTCCAAAGA +CAGCCCAACGTCTGGCTTTTCATACCTTAGATATGAAAGAAGACGATGTTGTTCAGTTTGCCAAAGCATTAGTAGATGTT +AAGAGAGAATTAACATATTGTAGCGTATGTGGTCACATTACTGAAAATGATCCATGTTATATTTGTGAAGATAAGCAAAG +AGATCGTTCAGTTATTTGTGTTGTGGAAGATGACAAAGATGTCATAGCTATGGAAAAAATGAGAGAATACAAAGGTTTAT +ATCACGTTTTACATGGGTCTATTTCGCCTATGGATGGCATTGGACCAGAAGATATTAATATTCCTTCATTGATTGAACGC +TTGAAAAACGATGAAGTTAGCGAATTAATCTTAGCTATGAACCCGAACTTAGAGGGGGAATCTACAGCCATGTATATTTC +TAGATTAGTTAAGCCTATAGGTATCAAAGTGACGAGATTAGCACAAGGGTTATCGGTAGGTGGCGATTTAGAGTATGCTG +ACGAAGTAACATTATCTAAAGCAATCGCAGGTAGAACAGAAATGTAATGTCTTCTATTAAACATTTTTGATTTTAATACT +ATAGTAAGAAAAGTCACAGTGTAATCATTGTGGCTTTTTTTATGGTGTGGTGTGATGTACTACTTTATTTGCGGTGTGGC +GGTGGTATGGTTTACCTAGTTTTACTGAGGGATGGGTAATCTTTAGGAAGCAAGCCGTTGGTTGTGATTTGTTACTTCTA +ATAGTAATGATGTGAATTGGATTATCGAATTAGATCTATGGTTATGGTGTGTTGGTGCTATTAATTTGATAAATGCGGTT +AATGACTATGCAAATGAAATTCTTTTGTAATTGAAATGATAGATGCTGGCTTAGTAAGTTGTACTTCTTTGGTCTAAAGC +TTATTAAATCAGCCTGTATAGCGGTGTTTTGAGAGATTATTTAAAACTTGTAAATTTATTTTTAATTTCTGGTAAAAAAA +TAACGTTCTGTTTTGCGTTTTTTTTGATTGATATGGTTAGAGAAAAATCTGTTTCTTGTTCTAAAAAACGTACTATTTAT +AAGTGGGGATTTTTTAAGTTCGATTTTTAGGATAAGGGCGTTCAGTACAGATGACAAAGGTGTAATTTTTACTGTTGTTA +AGCAGTTTGAAAGCCTGTATAGTATTTATTTGTTGAGGCAAACAAAACAACTCAACTTAAGAAATAACTTGAATTACTAA +CGAAAATTAATTTTAAAAAGTTATTGACTTAAATGTTAATAAAATGTATAATTAATTCTTGTCGGTAAGAAAAATGAACA +TTGAAAACTGAATGACAATATGTCAACGTTAATTCCAAAAACGTAACTATTAGTTACAAACATTATTTAGTATTTATGAG +CTAATCAAACATCATAATTTTTATGGAGAGTTTGATCCTGGCTCAGGATGAACGCTGGCGGCGTGCCTAATACATGCAAG +TCGAGCGAACGGACGAGAAGCTTGCTTCTCTGATGTTAGCGGCGGACGGGTGAGTAACACGTGGATAACCTACCTATAAG +ACTGGGATAACTTCGGGAAACCGTAGCTAATACCGGATAATATTTTGAACCGCATGGTTCAAAAGTGAAAGACGGTCTTG +CTGTCACTTATAGATGGATCCGCGCTGCATTAGCTAGTTGGTAAGGTAACGGCTTACCAAGGCAACGATGCATAGCCGAC +CTGAGAGGGTGATCGGCCACACTGGAACTGAGACACGGTCCAGACTCCTACGGGAGGCAGCAGTAGGGAATCTTCCGCAA +TGGGCGAAAGCCTGACGGAGCAACGCCGCGTGAGTGATGAAGGTCTTCGGATCGTAAAACTCTGTTATTAGGGAAGAACA +TATGTGTAAGTAACTGTGCACATCTTGACGGTACCTAATCAGAAAGCCACGGCTAACTACGTGCCAGCAGCCGCGGTAAT +ACGTAGGTGGCAAGCGTTATCCGGAATTATTGGGCGTAAAGCGCGCGTAGGCGGTTTTTTAAGTCTGATGTGAAAGCCCA +CGGCTCAACCGTGGAGGGTCATTGGAAACTGGAAAACTTGAGTGCAGAAGAGGAAAGTGGAATTCCATGTGTAGCGGTGA +AATGCGCAGAGATATGGAGGAACACCAGTGGCGAAGGCGACTTTCTGGTCTGTAACTGACGCTGATGTGCGAAAGCGTGG +GGATCAAACAGGATTAGATACCCTGGTAGTCCACGCCGTAAACGATGAGTGCTAAGTGTTAGGGGGTTTCCCGCCCCTTA +GTGCTGCAGCTAACGCATTAAGCACTCCGCCTGGGGAGTACGACCGCAAGGTTGAAACTCAAAGGAATTGACGGGGACCC +GCACAAGCGGTGGAGCATGTGGTTTAATTCGAAGCAACGCGAAGAACCTTACCAAATCTTGACATCCTTTGACAACTCTA +GAGATAGAGCCTTCCCCTTCGGGGGACAAAGTGACAGGTGGTGCATGGTTGTCGTCAGCTCGTGTCGTGAGATGTTGGGT +TAAGTCCCGCAACGAGCGCAACCCTTAAGCTTAGTTGCCATCATTAAGTTGGGCACTCTAAGTTGACTGCCGGTGACAAA +CCGGAGGAAGGTGGGGATGACGTCAAATCATCATGCCCCTTATGATTTGGGCTACACACGTGCTACAATGGACAATACAA +AGGGCAGCGAAACCGCGAGGTCAAGCAAATCCCATAAAGTTGTTCTCAGTTCGGATTGTAGTCTGCAACTCGACTACATG +AAGCTGGAATCGCTAGTAATCGTAGATCAGCATGCTACGGTGAATACGTTCCCGGGTCTTGTACACACCGCCCGTCACAC +CACGAGAGTTTGTAACACCCGAAGCCGGTGGAGTAACCTTTTAGGAGCTAGCCGTCGAAGGTGGGACAAATGATTGGGGT +GAAGTCGTAACAAGGTAGCCGTATCGGAAGGTGCGGCTGGATCACCTCCTTTCTAAGGATATATTCGGAACATCTTCTTC +AGAAGATGCGGAATAACGTGACATATTGTATTCAGTTTTGAATGTTTATTTAACATTCAAATATTTTTTGGTTAAAGTGA +TATTGCTTATGAAAATAAAGCAGTATGCGAGCGCTTGACTAAAAAGAAATTGTACATTGAAAACTAGATAAGTAAGTAAA +ATATAGATTTTACCAAGCAAAACCGAGTGAATAAAGAGTTTTAAATAAGCTTGAATTCATAAGAAATAATCGCTAGTGTT +CGAAAGAACACTCACAAGATTAATAACGCGTTTAAATCTTTTTATAAAAGAACGTAACTTCATGTTAACGTTTGACTTAT +AAAAATGGTGGAAACATAGATTAAGTTATTAAGGGCGCACGGTGGATGCCTTGGCACTAGAAGCCAATGAAGGACGTTAC +TAACGACGATATGCTTTGGGGAGCTGTAAGTAAGCTTTGATCCAGAGATTTCCGAATGGGGAAACCCAGCATGAGTTATG +TCATGTTATCGATATGTGAATACATAGCATATCAGAAGGCACACCCGGAGAACTGAAACATCTTAGTACCCGGAGGAAGA +GAAAGAAAATTCGATTCCCTTAGTAGCGGCGAGCGAAACGGGAAGAGCCCAAACCAACAAGCTTGCTTGTTGGGGTTGTA +GGACACTCTATACGGAGTTACAAAGGACGACATTAGACGAATCATCTGGAAAGATGAATCAAAGAAGGTAATAATCCTGT +AGTCGAAAATGTTGTCTCTCTTGAGTGGATCCTGAGTACGACGGAGCACGTGAAATTCCGTCGGAATCTGGGAGGACCAT +CTCCTAAGGCTAAATACTCTCTAGTGACCGATAGTGAACCAGTACCGTGAGGGAAAGGTGAAAAGCACCCCGGAAGGGGA +GTGAAATAGAACCTGAAACCGTGTGCTTACAAGTAGTCAGAGCCCGTTAATGGGTGATGGCGTGCCTTTTGTAGAATGAA +CCGGCGAGTTACGATTTGATGCAAGGTTAAGCAGTAAATGTGGAGCCGTAGCGAAAGCGAGTCTGAATAGGGCGTTTAGT +ATTTGGTCGTAGACCCGAAACCAGGTGATCTACCCTTGGTCAGGTTGAAGTTCAGGTAACACTGAATGGAGGACCGAACC +GACTTACGTTGAAAAGTGAGCGGATGAACTGAGGGTAGCGGAGAAATTCCAATCGAACCTGGAGATAGCTGGTTCTCTCC +GAAATAGCTTTAGGGCTAGCCTCAAGTGATGATTATTGGAGGTAGAGCACTGTTTGGACGAGGGGCCCCTCTCGGGTTAC +CGAATTCAGACAAACTCCGAATGCCAATTAATTTAACTTGGGAGTCAGAACATGGGTGATAAGGTCCGTGTTCGAAAGGG +AAACAGCCCAGACCACCAGCTAAGGTCCCAAAATATATGTTAAGTGGAAAAGGATGTGGCGTTGCCCAGACAACTAGGAT +GTTGGCTTAGAAGCAGCCATCATTTAAAGAGTGCGTAATAGCTCACTAGTCGAGTGACACTGCGCCGAAAATGTACCGGG +GCTAAACATATTACCGAAGCTGTGGATTGTCCTTTGGACAATGGTAGGAGAGCGTTCTAAGGGCGTTGAAGCATGATCGT +AAGGACATGTGGAGCGCTTAGAAGTGAGAATGCCGGTGTGAGTAGCGAAAGACGGGTGAGAATCCCGTCCACCGATTGAC +TAAGGTTTCCAGAGGAAGGCTCGTCCGCTCTGGGTTAGTCGGGTCCTAAGCTGAGGCCGACAGGCGTAGGCGATGGATAA +CAGGTTGATATTCCTGTACCACCTATAATCGTTTTAATCGATGGGGGGACGCAGTAGGATAGGCGAAGCGTGCGATTGGA +TTGCACGTCTAAGCAGTAAGGCTGAGTATTAGGCAAATCCGGTACTCGTTAAGGCTGAGCTGTGATGGGGAGAAGACATT +GAGTCTTCGAGTCGTTGATTTCACACTGCCGAGAAAAGCCTCTAGATAGAAAATAGGTGCCCGTACCGCAAACCGACACA +GGTAGTCAAGATGAGAATTCTAAGGTGAGCGAGCGAACTCTCGTTAAGGAACTCGGCAAAATGACCCCGTAACTTCGGGA +GAAGGGGTGCTCTTTAGGGTTAACGCCCAGAAGAGCCGCAGTGAATAGGCCCAAGCGACTGTTTATCAAAAACACAGGTC +TCTGCTAAACCGTAAGGTGATGTATAGGGGCTGACGCCTGCCCGGTGCTGGAAGGTTAAGAGGAGTGGTTAGCTTCTGCG +AAGCTACGAATCGAAGCCCCAGTAAACGGCGGCCGTAACTATAACGGTCCTAAGGTAGCGAAATTCCTTGTCGGGTAAGT +TCCGACCCGCACGAAAGGCGTAACGATTTGGGCACTGTCTCAACGAGAGACTCGGTGAAATCATAGTACCTGTGAAGATG +CAGGTTACCCGCGACAGGACGGAAAGACCCCGTGGAGCTTTACTGTAGCCTGATATTGAAATTCGGCACAGCTTGTACAG +GATAGGTAGGAGCCTTTGAAACGTGAGCGCTAGCTTACGTGGAGGCGCTGGTGGGATACTACCCTAGCTGTGTTGGCTTT +CTAACCCGCACCACTTATCGTGGTGGGAGACAGTGTCAAGCGGGCAGTTTGACTGGGGCGGTCGCCTCCTAAAAGGTAAC +GGAGGCGCTCAAAGGTTCCCTCAGAATGGTTGGAAATCATTCATAGAGTGTAAAGGCATAAGGGAGCTTGACTGCGAGAC +CTACAAGTCGAGCAGGGTCGAAAGACGGACTTAGTGATCCGGTGGTTCCGCATGGAAGGGCCATCGCTCAACGGATAAAA +GCTACCCCGGGGATAACAGGCTTATCTCCCCCAAGAGTTCACATCGACGGGGAGGTTTGGCACCTCGATGTCGGCTCATC +GCATCCTGGGGCTGTAGTCGGTCCCAAGGGTTGGGCTGTTCGCCCATTAAAGCGGTACGCGAGCTGGGTTCAGAACGTCG +TGAGACAGTTCGGTCCCTATCCGTCGTGGGCGTAGGAAATTTGAGAGGAGCTGTCCTTAGTACGAGAGGACCGGGATGGA +CATACCTCTGGTGTACCAGTTGTCGTGCCAACGGCATAGCTGGGTAGCTATGTGTGGACGGGATAAGTGCTGAAAGCATC +TAAGCATGAAGCCCCCCTCAAGATGAGATTTCCCAACTTCGGTTATAAGATCCCTCAAAGATGATGAGGTTAATAGGTTC +GAGGTGGAAGCATGGTGACATGTGGAGCTGACGAATACTAATCGATCGAAGACTTAATCAAAATAAATGTTTTGCGAAGC +AAAATCACTTTTACTTACTATCTAGTTTTGAATGTATAATCTACATTCATATGTCTGGTGACTATAGCAAGGAGGTCACA +CCTGTTCCCATGCCGAACACAGAAGTTAAGGTCTTTAGCGACGATGGTAGCCAACTTACGTTCCGCTAGAGTAGAACGTT +GCCAGGCAAAAAATGGATGCGATGAGCCGCATTGAGACCGCAAGGTCTCTTTTTTTTATGCCTAAAACGTCAAAATAAAA +AGTAAACACAAAGAAAAATGGCTTGGCGAAGTGAAAACGTTTGAATCTGACGAAACGAGAAAAGAGCGCAACGAGTTTAG +TAGAGCTAAATGAGTAAGCGAGAGCCGAAGGAGAGGAAAGAAGCAAGCGATTGTCACAAGTCAAGAAAGGTCTTTAGCGA +CGATGGTAGCCAACTTACGTTCCGCTAGAGTAGAACGTTGCCAGGCAAGTTGAGACCGTGAGGTCTCTTTTTTTATGTCT +AAAACGTCAAAATAAAAAGTAAACACAAAGAAAAATGGCTTGGCGAAGTGAAAACGTTTGAATCTGACGAAACGAGAAAA +GAGCGCAACGAGTTTAGTAGAGCTAAATGAGTAAGCGAGAGCCGAAGGAGAGGAAAGAAGCAAGCGATTGTCACAAGTCA +AGAAAGGTTCTTAGCGAGGATGGTAGCTAACTTACGTTCCGCTAGAGTAGAACGTTGCTAGACAAGAAATGAATGCGATG +AGCCGCATTGAGTGTGAAATTGATATTTTAATAATGTGCACTTTTGATTATTTAAATCTGTGACTAGAAGTATAAGTGAA +GACATTCAGAAGTATTATAAAAAGTGAACAGCAGTAAGATAGTTTTTAATCATAAATCATCTTACTGCTGTTTTTAGATT +TTATGTCTAATATCTTTTAAATCGAAGTACAAAAAGGAAATTAATTATTATACAATAGACAAGCTATTGCATAAGTAACA +CTAACTTTTGTCAAAGAAGTGTTACTATATAATTAATACTTTTGAAAGTAACTAATTCCAAAACAGTGTAAATAAAGGAA +GCGTATATCATGAAGCAACCTATTTTAAATAAATTAGAAAGTTTAAATCAAGAAGAAGCGATTTCTTTGCATGTTCCGGG +TCATAAAAATATGACTATCGGTCATTTATCTCAATTATCAATGACAATGGATAAAACTGAAATACCTGGATTAGATGATT +TACATCATCCTGAAGAAGTCATTTTGGAAAGTATGAAGCAGGTGGAGAAACATTCAGATTATGATGCTTATTTCTTAGTG +AATGGCACCACTTCAGGAATATTATCTGTCATCCAGTCTTTTTCACAGAAAAAAGGCGATATCTTAATGGCAAGAAATGT +ACATAAATCTGTGTTACATGCGCTCGATATTAGCCAACAAGAAGGGCATTTTATTGAAACGCATCAAAGTCCGTTAACGA +ATCATTATAATAAAGTTAATTTAAGCCGTTTGAATAATGACGGTCACAAACTTGCTGTGTTGACTTATCCTAACTATTAC +GGTGAAACATTTAATGTAGAAGAGGTTATCAAATCTTTGCACCAATTAAATATTCCTGTACTCATTGACGAAGCACACGG +CGCGCACTTTGGATTGCAAGGATTTCCAGATTCTACATTAAATTATCAAGCTGACTATGTTGTTCAATCTTTTCATAAAA +CGTTACCAGCTTTAACGATGGGCTCGGTACTTTATATTCATAAAAATGCACCTTATAGAGAAACTATTATAGAATATCTA +AGCTACTTCCAAACATCTAGTCCTTCGTATTTGATTATGGCTAGTTTAGAGTCAGCTGCCGAGTTCTATAAAACATATGA +TAGTACCGTGTTTTTTGATAAGAGAGCGCAATTAATCGAATGTTTGGAGAAGAAGGGTTTTGAAATGCTTCAAGTTGATG +ATCCGTTGAAGTTGCTGATAAAATATGAAGGTTTTACAGGTCATGATATTCAAAATTGGTTTATGAATGCACATATCTAT +TTAGAATTAGCGGACGACTATCAAGCATTAGCGATATTGCCGTTATGGCATCATGATGATACGTATTTATTTGATTCGCT +TTTACGTAAAATTGAAGATATGATTTTACCGAAAAAATCAGTTTCTAAAGTTAAACAAACACAACTTTTAACAACTGAAG +GTAACTATAAACCAAAACGCTTTGAATATGTTACTTGGTGTGATTTGAAAAAGGCAAAAGGTAAAGTTCTGGCGCGACAT +ATTGTCCCGTATCCGCCAGGGATTCCTATTATTTTCAAAGGAGAAACAATAACTGAAAATATGATAGAATTGGTAAATGA +ATATCTGGAAACTGGAATGATAGTTGAAGGAATTAAAAATAATAAAATTTTAGTTGAGGATGAATAAAATGTCAGCTTTT +ATAACTTTTGAGGGCCCAGAAGGCTCTGGAAAAACAACTGTAATTAATGAAGTTTACCATAGATTAGTAAAAGATTATGA +TGTCATTATGACTAGAGAACCAGGTGGTGTTCCTACTGGTGAAGAAATACGTAAAATTGTATTAGAAGGCAATGATATGG +ACATTAGAACTGAAGCAATGTTATTTGCTGCATCTAGAAGAGAACATCTTGTATTAAAGGTCATACCAGCTTTAAAAGAA +GGTAAGGTTGTGTTGTGTGATCGCTATATCGATAGTTCATTAGCTTATCAAGGTTATGCTAGAGGGATTGGCGTTGAAGA +AGTAAGAGCATTAAACGAATTTGCAATAAATGGATTATATCCAGACTTGACGATTTATTTAAATGTTAGTGCTGAAGTAG +GTCGCGAACGTATTATTAAAAATTCAAGAGATCAAAATAGATTAGATCAAGAAGATTTAAAGTTTCACGAAAAAGTAATT +GAAGGTTACCAAGAAATCATTCATAATGAATCACAACGGTTCAAAAGCGTTAATGCAGATCAACCTCTTGAAAATGTTGT +TGAAGACACGTATCAAACTATCATCAAATATTTAGAAAAGATATGATATAATTGTTAGAAGAGGTGTTATAAAATGAAAA +TGATTATAGCGATCGTACAAGATCAAGATAGTCAGGAACTTGCAGATCAACTTGTTAAAAATAACTTTAGAGCAACAAAA +TTGGCAACAACAGGTGGGTTTTTAAGAGCGGGTAATACAACATTCTTATGTGGTGTCAATGATGACCGTGTAGATGAAAT +ATTGTCTGTGATTAATCAAACGTGTGGTAATAGAGAACAGTTGGTTTCACCTATTACACCTATGGGAGGCAGTGCGGATT +CGTACATTCCATATCCAGTTGAAGTTGAAGTTGGCGGTGCTACTGTATTTGTTATGCCAGTTGATGCATTCCATCAATTT +TAATTCTATAATACAATCATCAATTAGAGATACTTAAAATAGTGTATTAATAAGTTATTAACAATTTTGGGTTGCTTGAC +TGCGACTAGTTCAGATGCCAATAGATTTGATTTTTGTGGTTCTAAAAATAATCACAAATCATGGTCGCTATTGTTGCAGT +AATTAGTTGCTCTTTGGCAACCTTTTTATATAAAAGCAAAAGGGAGTTTGTAATGAATGGATGAACAGCAACAATTGACG +AATGCATATCATTCAAATAAATTATCGCATGCCTATTTATTTGAAGGTGATGATGCACAAACGATGAAACAAGTTGCGAT +TAATTTTGCAAAGCTTATTTTATGTCAAACAGATAGTCAATGTGAAACAAAGGTTAGTACATATAATCATCCAGACTTTA +TGTATATATCAACAACTGAGAATGCAATTAAGAAAGAACAAGTTGAACAACTTGTGCGTCATATGAATCAACTTCCTATA +GAAAGCACAAATAAAGTGTACATCATTGAAGACTTTGAAAAGTTAACTGTTCAAGGGGAAAACAGTATCTTGAAATTTCT +TGAAGAACCACCGGACAATACGATTGCTATTTTATTGTCTACAAAACCTGAGCAAATTTTAGACACAATCCATTCAAGGT +GTCAGCATGTATATTTCAAGCCTATTGATAAAGAAAAGTTTATAAATAGATTAGTTGAACAAAACATGTCTAAGCCAGTA +GCTGAAATGATTAGTACTTATACTACGCAAATAGATAATGCAATGGCTTTAAATGAAGAATTTGATTTATTAGCATTAAG +GAAATCAGTTATACGTTGGTGTGAATTGTTGCTTACTAATAAGCCAATGGCACTTATAGGTATTATTGATTTATTGAAAC +AGGCTAAAAATAAAAAACTGCAATCTTTAACTATTGCAGCTGTGAATGGTTTCTTCGAAGATATCATACATACAAAGGTA +AATGTAGAGGATAAACAAATATATAGTGATTTAAAAAATGATATTGATCAATATGCGCAAAAGTTGTCGTTTAATCAATT +AATTTTGATGTTTGATCAACTGACGGAAGCACATAAGAAATTGAATCAAAATGTAAATCCAACGCTTGTATTTGAACAAA +TCGTAATTAAGGGTGTGAGTTAGATGCCAAATGTAATAGGTGTTCAGTTTCAAAAAGCGGGAAAATTAGAATATTATACA +CCTAATGATATACAAGTAGATATAGAAGACTGGGTAGTTGTCGAATCTAAAAGAGGCATAGAGATAGGTATTGTTAAAAA +TCCATTAATGGATATTGCTGAAGAGGATGTTGTGTTACCTCTTAAAAATATTATTCGCATTGCTGATGACAAAGATATTG +ATAAATTTAATTGTAATGAACGAGATGCTGAAAATGCATTAATACTATGTAAAGACATTGTAAGAGAACAAGGTTTGGAC +ATGCGTTTAGTCAATTGCGAATATACATTAGATAAATCGAAAGTTATTTTTAATTTTACGGCGGATGATCGTATTGATTT +TAGAAAATTAGTAAAAATATTAGCGCAACATTTAAAAACACGTATCGAGTTGAGACAAATTGGTGTAAGGGATGAAGCCA +AATTGCTTGGCGGTATCGGACCTTGTGGTAGGTCGTTATGTTGTTCTACATTTTTAGGGGATTTTGAACCAGTATCGATT +AAGATGGCTAAGGATCAAAATTTATCATTAAATCCAACTAAAATTTCTGGTGCATGTGGTCGTTTGATGTGTTGTTTAAA +ATATGAAAATGACTATTATGAGGAAGTACGTGCACAATTACCTGATATTGGTGAAGCAATTGAAACGCCTGATGGTAACG +GGAAAGTAGTTGCTTTAAATATATTAGACATTTCTATGCAGGTGAAGCTTGAGGGACATGAACAGCCACTTGAATATAAA +TTAGAAGAAATAGAAACTATGCATTAAGGAGGCATTATTACATTTGGATCGCAATGAAATATTTGAAAAAATAATGCGTT +TAGAAATGAATGTCAATCAACTTTCAAAGGAAACTTCAGAATTAAAGGCACTTGCAGTTGAATTAGTAGAAGAAAATGTA +GCGCTTCAACTTGAAAATGATAATTTGAAAAAGGTGTTGGGCAATGATGAACCAACTACTATTGATACTGCGAATTCAAA +ACCAGCAAAAGCTGTGAAAAAGCCATTACCAAGTAAAGATAATTTGGCTATATTGTATGGAGAAGGATTTCATATTTGTA +AAGGCGAATTATTTGGAAAACATCGACATGGTGAAGATTGTCTGTTCTGTTTAGAAGTTTTAAGTGATTAATCAAGCACA +CTCAAATAGTGTTATAATTATAAATGAATATGGTTTGGATAAGTCTGAGACAATGCATGTTTCAGGCTTTAATTGTGTAT +AAAGTTTTGGTGATTGCATAAGAGATGGCGGTACTAAATGTTATTATTAAGTGTGCACGCAGTATCATTAGTTATAAAAT +GTAGCTGTTAAAAGTCAAAAATACATCGAATGTAGTTAGGCATATAATATAAAAAGAGTTTTCAATTACTCAATAGAAAA +AGGTTGTCTTCATAGGAGTTAAAAATGTTAAAAGAGAATGAACGATTTGATCAACTAATCAAAGAAGATTTTAGTATTAT +TCAAAATGATGATGTTTTTTCATTTTCAACGGATGCTTTGTTGTTAGGGCATTTTACAAAACCTAGAACAAAAGATATTG +TGTTGGACTTATGTTCAGGCAATGGGGTGATACCCTTGTTATTGTTTGCGAAACATCCACGACATATAGAAGGTGTTGAG +ATTCAAAAAACACTTGTCGATATGGCGCGACGCACATTTCAATTCAATGATGTTGATGAATATTTAACAATGCATCACAT +GGATTTGAAAAACGTTACTAAAGTATTTAAACCTTCACAATATACTTTAGTAACGTGTAATCCGCCTTATTTTAAAGAGA +ATCAGCAACACCAACATCAAAAAGAAGCACATAAGATAGCGAGACATGAGATTATGTGTACACTTGAAGATTGCATGATT +GCAGCCCGTCATTTATTAAAAGAAGGTGGCAGGCTAAACATGGTACATCGTGCAGAGAGACTAATGGATGTCTTGTTTGA +AATGAGAAAAGTGAATATTGAACCTAAGAAAGTCGTTTTTATATATAGTAAAGTAGGGAAATCAGCACAAACGATAGTAG +TAGAAGGTCGAAAAGGTGGAAATCAAGGTTTAGAAATCATGCCCCCATTTTATATTTATAATGAAGATGGTAATTATAGC +GAAGAAATGAAGGAAGTATATTATGGATAGTCATTTTGTATATATTGTAAAATGTAGTGATGGAAGTTTATATACAGGAT +ACGCTAAAGACGTTAATGCACGTGTTGAAAAACATAACCGAGGTCAAGGAGCCAAATATACGAAAGTAAGACGTCCGGTG +CATTTAGTTTATCAAGAAATGTATGAGACAAAGTCTGAAGCATTGAAGCGTGAATATGAAATTAAAACTTATACCAGACA +AAAGAAATTGCGATTAATTAAGGAGCGATAGTATGGCTGTATTATATTTAGTGGGCACACCAATTGGTAATTTAGCAGAT +ATTACTTATAGAGCAGTTGATGTATTGAAACGTGTTGATATGATTGCTTGTGAAGACACTAGAGTAACTAGTAAACTGTG +TAATCATTATGATATTCCAACTCCATTAAAGTCATATCACGAACATAACAAGGATAAGCAGACTGCTTTTATCATTGAAC +AGTTAGAATTAGGTCTTGACGTTGCGCTCGTATCTGATGCTGGATTGCCCTTAATTAGTGATCCTGGATACGAATTAGTA +GTGGCAGCCAGAGAAGCTAATATTAAAGTAGAGACTGTGCCTGGACCTAATGCTGGGCTGACGGCTTTGATGGCTAGTGG +ATTACCTTCATATGTATATACATTTTTAGGATTTTTGCCACGAAAAGAGAAAGAAAAAAGTGCTGTATTAGAGCAACGTA +TGCATGAAAATAGCACATTAATTATATACGAATCACCGCATCGTGTGACAGATACATTAAAAACAATTGCAAAGATAGAT +GCAACACGACAAGTATCACTAGGGCGTGAATTAACTAAGAAGTTCGAACAAATTGTAACTGATGATGTAACACAATTACA +AGCATTGATTCAGCAAGGCGATGTACCATTGAAAGGCGAATTCGTTATCTTAATTGAAGGTGCTAAAGCGAACAATGAGA +TATCGTGGTTTGATGATTTATCTATCAATGAGCATGTTGATCATTATATTCAAACTTCACAGATGAAACCAAAACAAGCT +ATTAAAAAAGTTGCTGAAGAACGACAACTTAAAACGAATGAAGTATATAATATTTATCATCAAATAAGTTAATCACTTTA +TCGATTATATGAAATTTTAAACGATTTTATAAACGCAAGCTGTAATTTTAAATGGTAAGTTATCATTTTGCATTGATACT +GATAAAATGATGTTGACTATGATAAAAAAATGATGACATCGACGTTTTTTAATGTAAAATAAATACATTGAAAGTAATAA +ATACCTTAACATTGAATAAGATGAAAATGAGATGACGAGATAAATGTTCGCGTCCGTTGAAATGCATAGAAATCTTAGAT +ATTATTTGAAGTGAGACATTACGAGGAGGAACAGTTATGGCTAAAGAAACATTTTATATAACAACCCCAATATACTATCC +TAGTGGGAATTTACATATAGGACATGCATATTCTACAGTGGCTGGAGATGTTATTGCAAGATATAAGAGAATGCAAGGAT +ATGATGTTCGCTATTTGACTGGAACGGATGAACACGGTCAAAAAATTCAAGAAAAAGCTCAAAAAGCTGGTAAGACAGAA +ATTGAATATTTGGATGAGATGATTGCTGGAATTAAACAATTGTGGGCTAAGCTTGAAATTTCAAATGATGATTTTATCAG +AACAACTGAAGAACGTCATAAACATGTCGTTGAGCAAGTGTTTGAACGTTTATTAAAGCAAGGTGATATCTATTTAGGTG +AATATGAAGGTTGGTATTCTGTTCCGGATGAAACATACTATACAGAGTCACAATTAGTAGACCCACAATACGAAAACGGT +AAAATTATTGGTGGCAAAAGTCCAGATTCTGGACACGAAGTTGAACTAGTTAAAGAAGAAAGTTATTTCTTTAATATTAG +TAAATATACAGACCGTTTATTAGAGTTCTATGACCAAAATCCAGATTTTATACAACCACCATCAAGAAAAAATGAAATGA +TTAACAACTTCATTAAACCAGGACTTGCTGATTTAGCTGTTTCTCGTACATCATTTAACTGGGGTGTCCATGTTCCGTCT +AATCCAAAACATGTTGTTTATGTTTGGATTGATGCGTTAGTTAACTATATTTCAGCATTAGGCTATTTATCAGATGATGA +GTCACTATTTAACAAATACTGGCCAGCAGATATTCATTTAATGGCTAAGGAAATTGTGCGATTCCACTCAATTATTTGGC +CTATTTTATTGATGGCATTAGACTTACCGTTACCTAAAAAAGTCTTTGCACATGGTTGGATTTTGATGAAAGATGGAAAA +ATGAGTAAATCTAAAGGTAATGTCGTAGACCCTAATATTTTAATTGATCGCTATGGTTTAGATGCTACACGTTATTATCT +AATGCGTGAATTACCATTTGGTTCAGATGGCGTATTTACACCTGAAGCATTTGTTGAGCGTACAAATTTCGATCTAGCAA +ATGACTTAGGTAACTTAGTAAACCGTACGATTTCTATGGTTAATAAGTACTTTGATGGCGAATTACCAGCGTATCAAGGT +CCACTTCATGAATTAGATGAAGAAATGGAAGCTATGGCTTTAGAAACAGTGAAAAGCTACACTGAAAGCATGGAAAGTTT +GCAATTTTCTGTGGCATTATCTACGGTATGGAAGTTTATTAGTAGAACGAATAAGTATATTGACGAAACAACGCCTTGGG +TATTAGCTAAGGACGATAGCCAAAAAGATATGTTAGGCAATGTAATGGCTCACTTAGTTGAAAATATTCGTTATGCAGCT +GTATTATTACGTCCATTCTTAACACATGCGCCGAAAGAGATTTTTGAACAATTGAACATTAACAATCCTCAATTTATGGA +ATTTAGTAGTTTAGAGCAATATGGTGTGCTTAATGAGTCAATTATGGTTACTGGGCAACCTAAACCTATTTTCCCAAGAT +TGGATAGCGAAGCGGAAATTGCATATATCAAAGAATCAATGCAACCGCCTGCTACTAAAGAGGAAAAAGAAGAGATTCCT +AGCAAACCTCAAATTGATATTAAAGACTTTGATAAAGTTGAAATTAAGGCAGCAACGATTATTGATGCTGAACATGTTAA +GAAGTCAGATAAGCTTTTAAAAATTCAAGTAGACTTAGATTCTGAACAAAGACAAATTGTATCAGGAATTGCCAAATTCT +ATACACCAGATGATATTATTGGTAAAAAAGTAGCAGTTGTTACTAACCTGAAACCAGCTAAATTAATGGGACAAAAATCT +GAAGGTATGATATTATCTGCTGAAAAAGATGGTGTATTAACCTTAGTAAGTTTACCAAGTGCAATTCCAAATGGTGCAGT +GATTAAATAACTGTATTTTTAAAAATTAGGAGAGATAATTATGTTAATCGATACACATGTCCATTTAAATGATGAGCAAT +ACGATGATGATTTGAGTGAAGTGATTACACGTGCTAGAGAAGCAGGTGTTGATCGTATGTTTGTAGTTGGTTTTAACAAA +TCGACAATTGAACGCGCGATGAAATTAATCGATGAGTATGATTTTTTATATGGCATTATCGGTTGGCATCCAGTTGACGC +AATTGATTTTACAGAAGAACACTTGGAATGGATTGAATCTTTAGCTCAGCATCCAAAAGTGATTGGTATTGGTGAAATGG +GATTAGATTATCACTGGGATAAATCTCCTGCAGATGTTCAAAAGGAAGTTTTTAGAAAGCAAATTGCTTTAGCTAAGCGT +TTGAAGTTACCAATTATCATTCATAACCGTGAAGCAACTCAAGACTGTATCGATATCTTATTGGAGGAGCATGCTGAAGA +GGTAGGCGGGATTATGCATAGCTTTAGTGGTTCTCCAGAAATTGCAGATATTGTAACTAATAAGCTGAATTTTTATATTT +CATTAGGTGGACCTGTGACATTTAAAAATGCTAAACAGCCTAAAGAAGTTGCTAAGCATGTGTCAATGGAGCGTTTGCTA +GTTGAAACCGATGCACCGTATCTTTCGCCACATCCGTATAGAGGGAAGCGAAATGAACCGGCGAGAGTAACTTTAGTAGC +TGAACAAATTGCTGAATTAAAAGGCTTATCTTATGAAGAAGTGTGCGAACAAACAACTAAAAATGCAGAGAAATTGTTTA +ATTTAAATTCATAAAGTTAAAAGTGAGAAAGATCACCGCCATAAATGTAAACGATGCTATATTCGTTTAATATGCTATGG +TTCTTTCTCACTTTTTTAAATTAAAATATCGTGCATGTGGAATACGTGCGATAGAGATGGTTAGAGCTTTGAAATTAAGA +ATTGTAGGAAGGCGTTTTAAATGAAAATCAATGAGTTTATAGTTGTAGAAGGACGAGATGATACTGAGCGTGTTAAACGA +GCTGTTGAATGTGATACGATTGAAACGAATGGTAGTGCCATCAACGAACAAACTTTAGAAGTAATTAGAAATGCTCAACA +AAGTCGAGGCGTTATTGTATTAACAGATCCAGATTTCCCAGGAGATAAAATTAGAAGTACAATTACTGAACATGTCAAAG +GTGTTAAACATGCGTATATTGATAGAGAAAAAGCTAAAAATAAAAAAGGGAAAATTGGTGTTGAACATGCCGACTTAATT +GATATTAAAGAAGCGTTAATGCATGTTAGTTCACCCTTTGATGAAGCTTATGAATCAATTGATAAATCTGTGCTAATAGA +GTTGGGGTTAATTGTTGGGAAAGATGCAAGGCGCCGTAGAGAAATTTTAAGTAGAAAATTGCGAATCGGCCATTCCAATG +GTAAGCAGTTATTGAAAAAGTTAAATGCATTTGGTTATACCGAAGCGGATGTAAGGCAAGCTTTAGAAGATGAATGAGGA +AGTGAAAATGTTGGATAATAAAGATATTGCAACACCATCAAGAACGCGAGCGTTGTTAGATAAATATGGCTTTAATTTTA +AAAAAAGTTTAGGACAGAACTTTTTGATAGATGTGAATATCATTAATAATATCATTGATGCAAGTGATATTGATGCACAA +ACTGGGGTGATTGAAATTGGTCCAGGCATGGGGTCATTGACAGAACAATTGGCCAGACATGCTAAAAGAGTATTGGCATT +TGAAATTGATCAACGTTTAATACCTGTATTAAATGATACACTATCACCTTATGATAATGTGACGGTGATTAATGAAGATA +TTTTAAAAGCGAATATTAAAGAAGCTGTTGAAAATCATTTACAAGATTGTGAAAAAATAATGGTTGTTGCAAACCTGCCG +TACTATATTACGACGCCAATTTTATTAAATTTGATGCAACAAGATATACCAATTGATGGCTACGTGGTGATGATGCAAAA +AGAAGTGGGCGAACGCTTAAATGCTGAAGTAGGTTCAAAAGCATATGGTTCGTTATCAATTGTCGTACAATACTATACAG +AGACTAGTAAAGTATTAACGGTACCTAAATCTGTATTTATGCCACCACCTAATGTTGATTCAATAGTTGTAAAACTGATG +CAGAGAACTGAACCGTTAGTAACAGTAGATAACGAGGAAGCATTCTTTAAGTTAGCAAAAGCAGCATTTGCACAAAGAAG +AAAGACAATTAACAATAACTATCAAAATTATTTTAAAGATGGTAAACAACACAAAGAAGTGATTTTACAATGGTTGGAAC +AAGCAGGTATTGATCCAAGACGTCGCGGTGAAACGCTATCTATTCAAGATTTTGCTAAATTGTATGAAGAAAAGAAAAAA +TTCCCTCAATTAGAAAATTAAATGATTGACAAAGCAAAGCACTATTGTTAAAATTTAAATTTTGTTTGACGAAAACGTTG +CAAATATGGTATTATGTAACTTGTAGCGAGGTGGAGCAATATGCCAAAATCAATTTTGGACATCAAAAATTCTATTGATT +GTCATGTAGGAAATCGTATTGTACTGAAAGCCAATGGAGGCCGTAAGAAAACAATAAAACGTTCTGGAATTTTAAAAGAA +ACATATCCGTCAGTTTTCATTGTTGAGTTAGATCAAGACAAACACAACTTTGAGAGAGTATCTTATACATACACTGATGT +GTTAACTGAAAATGTTCAAGTTTCATTTGAAGAGGATAATCATCACGAATCAATTGCACACTAAATAAGACATATAGAGA +TGTTAGACGTTTCTTAGTATAAGAAGTAAATATTATGATAATTATTTGAGTGTTGGGCATTATGTTCAATACTCTTTTTA +TTTACAAAATGTTTAACACTGATGTTTCGCTTATAGATTTTTCAGTAAATGGATAATTGTATTTATAAACACAAATACAA +GTAAATACTAAGTAATTAGATGGAGAAAATTACTTTTTTATTAAAAAAACACTAAAAAACAAATTAAAATGTCAAATATT +AATTCTCTTTATGTTAAAATCATCATATTAAGATAACGAAAAGAGGGCGGAAAATGATATATGAAACGGCACCAGCCAAA +ATTAATTTTACGCTCGATACACTTTTTAAAAGAAATGATGGCTATCATGAGATTGAAATGATAATGACAACAGTTGATTT +AAATGATCGTTTAACTTTTCATAAAAGAAAAGATCGAAAGATAGTTGTTGAGATTGAACATAATTATGTGCCTTCTAATC +ATAAAAATCTCGCATATCGTGCAGCGCAACTATTTATTGAGCAATATCAACTAAAGCAAGGTGTAACAATTTCTATCGAT +AAAGAAATACCTGTTTCTGCTGGCTTAGCTGGAGGTTCGGCTGATGCAGCAGCAACGTTAAGAGGATTGAATCGACTTTT +TGATATAGGGGCGAGTTTGGAAGAATTGGCTCTACTAGGCAGTAAAATCGGGACAGATATTCCGTTTTGTATTTATAATA +AAACTGCACTATGTACTGGAAGAGGAGAGAAAATCGAGTTTTTAAATAAACCACCTTCAGCTTGGGTGATTCTTGCTAAA +CCAAACTTAGGCATATCATCACCAGATATATTTAAGTTGATTAATTTAGATAAGCGTTACGACGTACATACGAAAATGTG +TTATGAGGCCTTAGAAAATCGAGATTATCAACAATTATGTCAAAGTTTGTCTAATCGATTAGAGCCAATTTCTGTTTCAA +AACACCCACAAATCGATAAATTAAAAAATAATATGTTGAAAAGTGGTGCAGATGGTGCGTTAATGAGTGGAAGCGGACCT +ACTGTGTATGGGCTAGCACGAAAAGAAAGCCAAGCAAAAAATATTTATAATGCAGTTAACGGTTGTTGTAATGAAGTGTA +CTTAGTTAGACTATTAGGATAGAAGGGTTGAAAAGATGAGATATAAACGAAGCGAGAGAATTGTTTTTATGACGCAATAT +TTGATGAACCATCCGAATAAATTGATTCCATTAACTTTTTTTGTGAAAAAATTTAAACAGGCGAAGTCTTCAATAAGTGA +AGATGTCCAAATTATAAAAAATACATTCCAAAAAGAAAAGTTAGGTACAGTAATTACTACTGCTGGCGCAAGTGGTGGTG +TTACGTATAAACCAATGATGAGTAAAGAAGAGGCGACTGAAGTTGTTAATGAGGTCATTACTCTATTAGAAGAGAAAGAA +CGTTTGTTACCTGGCGGATATTTATTTTTATCAGATTTGGTAGGTAATCCATCGCTACTAAACAAAGTTGGTAAGTTAAT +TGCCAGTATTTACATGGAAGAAAAATTAGATGCTGTTGTTACCATTGCGACAAAAGGTATTTCATTGGCAAATGCGGTTG +CTAATATTTTAAATTTACCAGTAGTAGTGATTAGAAAAGACAACAAGGTGACTGAAGGTTCTACAGTTTCAATTAATTAC +GTTTCAGGATCTTCAAGAAAAATAGAAACAATGGTACTTTCGAAGAGAACTTTAGCAGAAAATTCAAATGTTTTAGTTGT +CGATGATTTTATGAGGGCTGGTGGCTCTATTAATGGTGTTATGAATTTAATGAATGAGTTTAAAGCCCATGTAAAAGGGG +TATCAGTACTTGTAGAATCAAAAGAAGTTAAACAAAGATTGATTGAAGATTATACTTCCTTAGTGAAATTATCTGATGTA +GATGAATATAATCAAGAGTTTAACGTAGAACCTGGCAACAGTTTATCTAAGTTTTCATAAAAGGAGTTTTAGTATTATGA +AAATCATTAACACAACAAGATTACCGGAAGCACTTGGACCATATTCGCATGCAACAGTTGTGAATGGTATGGTTTATACT +TCTGGTCAGATTCCATTGAATATTGATGGACATATCGTAAGCGCTGATGTTCAAGCACAGACAAAACAAGTTTTAGAAAA +TTTAAAGGTTGTTTTGGAAGAAGCAGGATCTGATTTGAATTCTGTTGCGAAAGCGACCATTTTCATTAAAGATATGAATG +ATTTCCAAAAAATAAATGAAGTGTATGGTCAATATTTTAATGAACACAAGCCAGCGCGTAGTTGTGTAGAGGTTGCGCGT +TTGCCAAAAGATGTGAAAGTAGAAATTGAATTAGTAAGTAAAATTAAGGAATTATAATTTTCGATTAATATGTTTAATCA +AGCTTCTAAATAAAACAGAGAGATATATACTATAGGGGGGCTCACTACATGAAAGTGACAGATGTAAGACTTAGAAAAAT +ACAAACAGATGGACGAATGAAAGCACTCGTTTCCATTACATTAGATGAAGCTTTCGTAATTCATGATTTACGTGTAATTG +AAGGAAACTCTGGCTTGTTCGTTGCAATGCCAAGTAAACGTACACCAGATGGTGAATTCCGCGACATCGCGCATCCTATT +AATTCAGATATGAGACAAGAAATTCAAGATGCAGTGATGAAAGTATATGATGAAACAGATGAAGTAGTACCAGATAAAAA +CGCTACATCAGAAGATTCAGAAGAAGCTTAATCAATTTTATATTTAGCGATGTAATACATTTGCAATAAGTTGATTTGAT +ACTGTCGATAAAGCATAAAGCTTTGTCGGCAGTTTTTTTAGTTTGTATTAATGTTTTTTTATTTTTAATGAAAGGCTAAT +AAATATATACGTTAACAGATTATGATGATATGAAAATTATTGATTCATTGCTGATAAAAATTGCGTTTGAATGATGCTCG +TATTTTTGAAGTAAGAAAAAAGTTGTTTTTAAAATTACAACGAATTAAAAACAATGCCTTTTATATGTTGAAAGAGTATT +GCAGATTAAATTATAATAATGACGAAGTGTAAAATTTAATGGGGGTTAATGTTCATGCGAAGACACGCGATAATTTTGGC +AGCAGGTAAAGGCACAAGAATGAAATCTAAAAAGTATAAAGTGCTACACGAGGTTGCTGGGAAACCTATGGTCGAACATG +TATTGGAAAGTGTGAAAGGCTCTGGTGTCGATCAAGTTGTAACCATCGTAGGACATGGTGCTGAAAGTGTAAAAGGACAT +TTAGGCGAGCGTTCTTTATACAGTTTTCAAGAGGAACAACTCGGTACTGCGCATGCAGTGCAAATGGCGAAATCACACTT +AGAAGACAAGGAAGGTACGACAATCGTTGTATGTGGTGACACACCGCTCATCACAAAGGAAACATTAGTAACATTGATTG +CGCATCACGAGGATGCTAATGCTCAAGCAACTGTATTATCTGCATCGATTCAACAACCATATGGATACGGAAGAATCGTT +CGAAATGCGTCAGGTCGTTTAGAACGCATAGTTGAAGAGAAAGATGCAACGCAAGCTGAAAAGGATATTAATGAAATTAG +TTCAGGTATTTTTGCGTTTAATAATAAAACGTTGTTTGAAAAATTAACACAAGTGAAAAATGATAATGCGCAAGGTGAAT +ATTACCTCCCTGATGTATTGTCGTTAATTTTAAATGATGGCGGCATCGTAGAAGTCTATCGTACCAATGATGTTGAAGAA +ATCATGGGTGTAAATGATCGTGTAATGCTTAGTCAGGCTGAGAAGGCGATGCAACGTCGTACGAATCATTATCACATGCT +AAATGGTGTGACAATCATCGATCCTGACAGCACTTATATTGGTCCAGACGTTACAATTGGTAGTGATACAGTCATTGAAC +CAGGCGTACGAATTAATGGTCGTACAGAAATTGGCGAAGATGTTGTTATTGGTCAGTACTCTGAAATTAACAATAGTACG +ATTGAAAATGGTGCATGTATTCAACAGTCTGTTGTTAATGATGCTAGCGTAGGAGCGAATACTAAGGTCGGACCGTTTGC +GCAATTGAGACCAGGCGCGCAATTAGGTGCAGATGTTAAGGTTGGAAATTTTGTAGAAATTAAAAAAGCAGATCTTAAAG +ATGGTGCCAAGGTTTCACATTTAAGTTATATTGGCGATGCTGTAATTGGCGAACGTACTAATATTGGTTGCGGAACGATT +ACAGTTAACTATGATGGTGAAAATAAATTTAAAACTATCGTCGGCAAAGATTCATTTGTAGGTTGCAATGTTAATTTAGT +AGCACCTGTAACAATTGGTGATGATGTATTGGTGGCAGCTGGTTCCACAATCACAGATGACGTACCAAATGACAGTTTAG +CTGTGGCAAGAGCAAGACAAACAACAAAAGAAGGATATAGGAAATAATCATTTACGTATTTAAAATGGCTAGGATAAAAG +GATAATCCTATGTAATATTAATGTAATCTTTATGATTTAATGATTCGCATAGTAATGGAGTTACATTTTATATATAATAG +TAATTGCGTAAGTAAATAATTGGAGGACTATAAATGTTAAATAATGAATATAAGAATTCGTCATTAAAGATTTTTTCATT +GAAAGGAAACGAAGCATTAGCGCAAGAAGTTGCTGACCAAGTAGGAATTGAACTAGGTAAATGTTCAGTTAAACGTTTTA +GTGATGGAGAAATTCAAATTAATATCGAAGAGAGTATTCGTGGTTGTGACGTATTTATTATTCAACCAACATCATATCCT +GTGAATCTACATTTAATGGAATTATTAATTATGATTGATGCTTGTAAACGTGCTTCTGCAGCAACAATCAATATTGTAGT +GCCATATTATGGATATGCAAGACAAGATAGAAAAGCCCGTAGCCGTGAGCCAATCACTGCTAAATTAGTTGCAAACTTAA +TCGAAACAGCTGGCGCAACTCGTATGATTGCGTTAGACTTACATGCACCACAAATTCAAGGATTCTTTGATATTCCAATT +GACCACTTAATGGGTGTGCCAATTCTTGCTAAACATTTCAAAGATGATCCGAATATTAACCCAGAAGAATGTGTCGTTGT +TTCACCAGACCATGGCGGCGTTACACGTGCACGTAAATTAGCTGACATTTTAAAAACTCCAATTGCAATTATAGATAAAC +GTCGTCCTAGACCAAATGTTGCTGAAGTGATGAACATTGTTGGTGAGATTGAAGGACGTACGGCAATTATTATTGACGAT +ATTATTGATACAGCAGGTACAATCACTTTAGCTGCACAAGCATTAAAAGATAAAGGTGCTAAAGAAGTATATGCTTGTTG +TACACACCCTGTTTTATCAGGACCGGCTAAAGAACGTATCGAAAATTCTGCTATAAAAGAATTAATCGTAACAAACTCAA +TTCATTTAGATGAAGATCGCAAACCATCTAACACTAAAGAATTATCTGTTGCTGGTTTAATCGCACAAGCTATCATTCGT +GTATACGAAAGAGAATCAGTTAGCGTATTATTTGACTAATATTTAAAAGGCGTTTGACGAACATATTCCAAACGTGTATA +ATAGTTTCGTTCGTGATTATACGAATAAATAAACACTTGCAAGCAACGATGATGTTGATGGGTAAGTGAGGTGCTCGTTT +TGAGCAAAAATGAAAGGTGGAAATGAGAATGGCTTCATTAAAGTCAATCATCCGTCAAGGTAAACAAACACGTTCAGATC +TTAAACAATTAAGAAAATCTGGTAAAGTACCAGCAGTAGTATACGGTTACGGTACTAAAAACGTGTCAGTTAAAGTTGAT +GAAGTAGAATTCATCAAAGTTATCCGTGAAGTAGGTCGTAACGGTGTTATCGAATTAGGCGTTGGTTCTAAAACTATCAA +AGTTATGGTTGCAGACTACCAATTCGATCCACTTAAAAACCAAATTACTCACATTGACTTCTTAGCAATCAATATGAGTG +AAGAACGTACTGTTGAAGTACCAGTTCAATTAGTTGGTGAAGCAGTAGGCGCTAAAGAAGGCGGCGTAGTTGAACAACCA +TTATTCAACTTAGAAGTAACTGCTACTCCAGACAATATTCCAGAAGCAATCGAAGTAGACATTACTGAATTAAACATTAA +CGACAGCTTAACTGTTGCTGATGTTAAAGTAACTGGCGACTTCAAAATCGAAAACGATTCAGCTGAATCAGTAGTAACAG +TAGTTGCTCCAACTGAAGAACCAACTGAAGAAGAAATCGAAGCTATGGAAGGCGAACAACAAACTGAAGAACCAGAAGTT +GTTGGCGAAAGCAAAGAAGACGAAGAAAAAACTGAAGAGTAATTTTAATCTGTTACATTAAAGTTTTTATACTTTGTTTA +ACAAGCACTGTGCTTATTTTAATATAAGCATGGTGCTTTTTGTGTTATTATAAAGCTTAATTAAACTTTATTACTTTGTA +CTAAAGTTTAATTAATTTTAGTGAGTAAAAGACATTAAACTCAACAATGATACATCATAAAAATTTTAATGTACTCGATT +TTAAAATACATACTTACTAAGCTAAAGAATAATGATAATTGATGGCAATGGCGGAAAATGGATGTTGTCATTATAATAAT +AAATGAAACAATTATGTTGGAGGTAAACACGCATGAAATGTATTGTAGGTCTAGGTAATATAGGTAAACGTTTTGAACTT +ACAAGACATAATATCGGCTTTGAAGTCGTTGATTATATTTTAGAGAAAAATAATTTTTCATTAGATAAACAAAAGTTTAA +AGGTGCATATACAATTGAACGAATGAACGGCGATAAAGTGTTATTTATCGAACCAATGACAATGATGAATTTGTCAGGTG +AAGCAGTTGCACCGATTATGGATTATTACAATGTTAATCCAGAAGATTTAATTGTCTTATATGATGATTTAGATTTAGAA +CAAGGACAAGTTCGCTTAAGACAAAAAGGAAGTGCGGGCGGTCACAATGGTATGAAATCAATTATTAAAATGCTTGGTAC +AGACCAATTTAAACGTATTCGTATTGGTGTGGGAAGACCAACGAATGGTATGACGGTACCTGATTATGTTTTACAACGCT +TTTCAAATGATGAAATGGTAACGATGGAAAAAGTTATCGAACACGCAGCACGCGCAATTGAAAAGTTTGTTGAAACATCA +CGATTTGACCATGTTATGAATGAATTTAATGGTGAAGTGAAATAATGACAATATTGACAACGCTTATAAAAGAAGATAAT +CATTTTCAAGACCTTAATCAGGTATTTGGACAAGCAAACACACTAGTAACTGGTCTTTCCCCGTCAGCTAAAGTGACGAT +GATTGCTGAAAAATATGCACAAAGTAATCAACAGTTATTATTAATTACCAATAATTTATACCAAGCAGATAAATTAGAAA +CAGATTTACTTCAATTTATAGATGCTGAAGAATTGTATAAGTATCCTGTGCAAGATATTATGACCGAAGAGTTTTCAACA +CAAAGCCCTCAACTGATGAGTGAACGTATTAGAACTTTAACTGCGTTAGCTCAAGGTAAGAAAGGGTTATTTATCGTTCC +TTTAAATGGTTTGAAAAAGTGGTTAACTCCTGTTGAAATGTGGCAAAATCACCAAATGACATTGCGTGTTGGTGAGGATA +TCGATGTGGACCAATTTCTTAACAAATTAGTTAATATGGGGTACAAACGGGAATCCGTGGTATCGCATATTGGTGAATTC +TCATTGCGAGGAGGTATTATCGATATCTTTCCGCTAATTGGGGAACCAATCAGAATTGAGCTATTTGATACCGAAATTGA +TTCTATTCGGGATTTTGATGTTGAAACGCAGCGTTCCAAAGATAATGTTGAAGAAGTCGATATCACAACTGCAAGTGATT +ATATCATTACTGAAGAAGTGATCAGCCATCTTAAAGAAGAGTTAAAAACTGCATATGAAAATACAAGACCCAAAATAGAT +AAATCAGTGCGCAATGATTTGAAAGAAACGTATGAAAGCTTTAAATTATTCGAAAGTACATACTTTGATCATCAAATACT +ACGTCGCTTAGTAGCGTTTATGTATGAAACACCTTCGACAATTATTGAGTATTTCCAAAAAGATGCAATCATTGCAGTTG +ATGAATTTAATCGTATTAAAGAAACTGAAGAAAGTTTAACAGTAGAGTCTGATTCGTTTATTAGCAATATTATTGAAAGT +GGTAATGGATTTATAGGACAAAGTTTTATAAAATATGATGATTTTGAAACATTGATTGAAGGCTATCCTGTCACTTATTT +TTCATTATTCGCTACAACAATGCCGATAAAACTAAATCATATTATTAAATTTTCATGTAAACCTGTCCAACAATTTTATG +GGCAATATGACATTATGCGTTCTGAATTTCAACGATATGTTAATCAAAACTATCATATCGTGGTTTTGGTCGAAACCGAA +ACTAAAGTTGAACGTATGCAAGCGATGTTAAGTGAAATGCATATTCCATCAATAACAAAATTGCATCGCTCAATGTCATC +GGGGCAAGCAGTGATTATTGAAGGCAGTTTATCTGAAGGATTTGAACTACCTGATATGGGATTAGTTGTCATTACTGAGC +GTGAGCTTTTTAAATCAAAACAGAAAAAGCAACGAAAACGTACGAAAGCTATCTCAAATGCTGAAAAAATTAAGTCTTAC +CAAGATTTAAATGTGGGAGATTATATTGTTCATGTGCATCATGGTGTTGGTAGATATTTAGGTGTTGAGACGCTCGAAGT +GGGGCAAACGCATCGTGATTATATTAAATTGCAATATAAAGGTACGGATCAACTATTTGTTCCAGTAGATCAAATGGATC +AAGTTCAAAAATATGTAGCTTCGGAAGATAAGACGCCAAAATTAAATAAACTCGGTGGCAGTGAATGGAAAAAAACAAAA +GCTAAAGTTCAACAAAGTGTTGAAGATATTGCTGAAGAGTTGATTGATTTATATAAAGAAAGAGAAATGGCAGAAGGTTA +TCAATATGGGGAAGACACAGCTGAGCAAACAACATTTGAATTAGATTTTCCATATGAACTTACGCCTGACCAAGCTAAAT +CTATCGATGAAATTAAAGATGACATGCAAAAATCGCGTCCAATGGATCGCTTGCTATGTGGTGATGTTGGTTATGGTAAA +ACTGAAGTTGCAGTGAGAGCAGCATTCAAAGCTGTAATGGAAGGAAAGCAGGTTGCATTTTTAGTTCCTACAACTATTTT +AGCTCAGCAACATTATGAGACGTTAATTGAGCGTATGCAAGATTTTCCTGTTGAAATTCAATTAATGAGTCGTTTTAGAA +CGCCTAAAGAGATAAAACAAACTAAGGAAGGACTTAAAACTGGATTTGTTGACATAGTTGTTGGTACACACAAATTACTT +AGTAAAGATATACAGTATAAAGATTTAGGGCTGTTGATTGTAGATGAAGAACAACGATTTGGTGTACGCCATAAAGAGCG +TATTAAAACATTAAAACATAATGTAGATGTACTAACATTGACTGCAACCCCAATACCTAGAACATTGCATATGAGTATGC +TAGGTGTGCGGGATTTGTCAGTGATTGAAACGCCGCCAGAAAATCGTTTCCCAGTTCAAACATATGTATTAGAACAGAAC +ATGAGTTTTATCAAAGAAGCTTTAGAAAGAGAACTATCCCGTGATGGCCAAGTGTTTTATCTTTATAATAAAGTGCAATC +CATTTATGAAAAACGAGAACAACTCCAGATGTTAATGCCAGATGCTAACATTGCAGTTGCTCATGGACAAATGACAGAGC +GCGATTTAGAAGAAACGATGTTAAGTTTTATCAATAATGAATATGATATTTTAGTAACGACGACGATTATTGAAACAGGT +GTCGATGTCCCAAATGCAAATACTTTGATCATTGAAGATGCAGATCGCTTTGGATTGAGTCAGTTGTATCAATTAAGAGG +TCGTGTTGGTCGTTCAAGTCGTATTGGTTATGCATACTTCTTACATCCAGCAAATAAGGTACTAACTGAGACTGCAGAAG +ATCGATTACAAGCGATTAAAGAATTTACGGAGTTAGGCTCAGGATTTAAGATTGCGATGCGTGATTTGAACATTCGTGGT +GCTGGTAATTTGTTAGGTAAACAACAGCACGGCTTTATTGATACAGTTGGATTTGATTTGTACAGTCAAATGTTAGAAGA +AGCTGTAAATGAAAAACGTGGTATTAAGGAACCAGAATCTGAGGTGCCAGAAGTCGAAGTTGATTTAAACTTGGATGCAT +ATTTGCCAACAGAATATATTGCAAATGAACAAGCTAAAATTGAAATTTATAAAAAGCTACGAAAAACTGAAACATTTGAT +CAAATTATCGACATTAAAGATGAATTAATTGATCGTTTCAATGATTATCCTGTTGAAGTAGCACGTTTGCTTGATATAGT +GGAAATAAAAGTACACGCATTACATTCAGGTATCACGTTGATTAAAGATAAAGGGAAAATAATTGATATTCATTTATCTG +TAAAAGCCACTGAAAATATTGATGGCGAAGTGCTGTTCAAAGCAACACAACCTTTAGGTAGAACAATGAAGGTTGGTGTT +CAAAATAATGCAATGACAATTACTTTAACGAAACAAAATCAATGGCTTGATAGTTTGAAGTTTTTAGTTAAGTGCATTGA +AGAAAGTATGAGAATCAGTGATGAAGCATAAAGAAGCATTTAATGGCGTTGTCGTGTTAACTGCTGCATTAATTGTCATT +AAAATTCTGAGTGCTGTATATCGAATTCCATATCAAAATATATTAGGCGATACAGGTTTGTATGCATATCAACAAGTGTA +TCCAATTGTAGCATTAGGAATGATATTATCGATGAATGCCATTCCTAGTGCAATTACACAAAATATAGGGAAGTATCATA +GTGACGAAGCATATGCAAAAGCAGTCGCTTATATACAATTAGTTGGTATATTATTATTTATTGCTATTTTTGTGTTTGCG +AACAATATTGCACATATGATGGGTGATGGCCATTTAACACCAATGATTCAAGCTGCAAGTTTAAGCTTTATATTTATAGG +TATGCTTGGCGTGTTAAGAGGTTATTATCAATCTGCAAATAATATGACAGTTCCGGCTATTTCCCAGGTTATAGAACAAG +TTATACGAGTAGGTATTATCATTGTTACTATTGTTATTTTTGTAGACAGAGGTTGGACGATATATGAAGCGGGAACAATT +GCTATTTTAGCATCAACGATAGGTTTTTTAGGTTCTTCAATTTATTTAGTAGCGCACCGACCTTTTAAGTTTAAAATGGT +AAATAACACTGCAAAGATCGTTTGGAAACAGTTCGCACTTTCGGTTTTGATTTTCGCTATCAGTCAATTAATCGTAATTT +TATGGCAAGTGATTGATAGTGTTACTATTATTAAGTCACTTCAAGCGATACGCGTGCCATTCGATGTTGCCATAACTGAA +AAAGGAGTCTATGACCGTGGTGCATCATTTATTCAGATGGGATTGATTGTAACTACAACATTTAGTTTTGCGCTCATTCC +TCTGTTAAGTGACGCAATCAAAATGAATAATCAGGTACTTATGAATCGTTATGCAAATGCGTCATTAAAGATTACGATTT +TAATAAGTACAGCAGCGGGAATAGGATTAATTAATTTATTGCCTTTAATGAACGGTGTGTTTTTTAAGACGAATGATTTA +ACCTTAACGTTAAGTGTTTATATGATTACGGTCATTTGTGTATCGTTAATTATGATGGATATGGCATTATTACAAGCGCA +ACATGCTGTGAGACCTATTTTTGTTGGTATGACGGCAGGATTGGTTATTAAATTTATACTTAATATCATTTTGATTCGTT +TAAGTGGCATTATTGGTGCGAGCATTAGTACTGTTGTATCATTAATTATATTCGGTACGATTATCCATATTGCTGTCACG +AGAAAATACCACTTATATGCGATGAGACGATTTTTTATCAATGTTGTTTTAGGTATGGTATTTATGTCGATTGTTGTTCA +ATGCGTGTTAAACATAGTGACAACACACGGTAGAATCACTGGACTCATTGAATTATTATGTGCAGCAGTATTAGGTATCA +TTGCATTGTTTTTCTATATTTTTAGATTTAATGTTTTGACATATAAAGAGTTAACTTATTTACCATTTGGTTCAAAGTTG +TATCAAATTAAGAAAGGAAGACGTTGATGGCACATACCATTACGATTGTTGGCTTAGGAAACTATGGCATTGATGATTTG +CCGCTAGGGATATATAAATTTTTAAAGACACAAGATAAAGTTTATGCAAGAACGTTAGATCATCCAGTTATAGAATCATT +GCAAGATGAATTAACATTTCAGAGTTTTGACCATGTTTATGAAGCACATAACCAATTTGAAGATGTCTATATTGATATTG +TGGCGCAATTGGTTGAAGCTGCTAATGAAAAAGATATTGTCTATGCGGTTCCGGGTCATCCTAGAGTTGCTGAGACAACT +ACAGTGAAATTACTGGCTTTAGCAAAGGACAATACTGATATAGATGTGAAAGTTTTAGGTGGGAAAAGCTTTATTGATGA +TGTGTTTGAAGCAGTTAATGTAGATCCAAATGATGGCTTCACACTGTTAGATGCGACATCATTACAAGAAGTAACACTTA +ATGTTAGAACGCATACATTGATTACGCAAGTTTATAGTGCAATGGTTGCTGCTAATTTGAAAATCACTTTAATGGAACGA +TATCCTGATGATTACCCTGTTCAAATTGTCACTGGTGCACGAAGCGATGGTGCGGATAACGTTGTGACATGCCCATTATA +TGAATTGGATCATGATGAAAATGCATTCAATAATTTGACGAGTGTATTCGTACCAAAAATCATAACATCGACATATTTGT +ATCATGACTTTGATTTTGCAACGGAAGTGATTGATACTTTAGTTGATGAAGATAAAGGTTGTCCATGGGATAAAGTGCAA +ACGCATGAAACGCTAAAGCGTTATTTACTTGAAGAAACATTTGAATTGTTCGAAGCTATTGACAATGAAGATGATTGGCA +TATGATTGAAGAACTAGGAGATATTTTATTACAAGTGTTATTGCATACTAGTATTGGTAAAAAAGAAGGGTATATCGACA +TTAAAGAAGTGATTACAAGTCTTAATGCTAAAATGATTCGTAGACACCCACACATATTTGGTGATGCCAATGCTGAAACT +ATCGATGACTTAAAAGAAATTTGGTCTAAGGCGAAAGATGCTGAAGGTAAACAGCCAAGAGTTAAATTTGAAAAAGTATT +TGCAGAGCATTTTTTAAATTTATATGAGAAGACGAAGGATAAGTCATTTGATGAGGCCGCGTTAAAGCAGTGGCTAGAAA +AAGGGGAGAGTAATACATGAGATTAGATAAATATTTAAAAGTATCACGGTTAATAAAGCGACGTACGCTAGCAAAAGAAG +TAAGTGATCAAGGTAGAATTACAATAAATGGTAATGTTGCTAAAGCTGGATCGGATGTTAAAGTTGAAGATGTGCTGACG +ATTCGCTTTGGTCAAAAATTAGTAACAGTTAAAGTAACTGCATTAAATGAACATGCATCTAAAGATAACGCGAAGGGTAT +GTATGAAATCATTGAAGAGCGTCGACTTGAAGAAGCGTAAATTGGAGGTGACAAGCAATGAAAAATAAAGTAGAACATAT +AGAAAATCAGTACACGTCGCAAGAGAACAAGAAAAAACAACGTCAAAAAATGAAAATGCGTGTTGTTCGTAGGCGTATTA +CAGTATTTGCGGGCGTATTACTTGCGATAATTGTTGTTTTATCAATCTTGCTTGTTGTCCAAAAACATCGCAATGATATT +GATGCACAGGAGCGAAAAGCGAAAGAAGCACAGTTTCAAAAGCAACAAAATGAAGAAATTGCGTTAAAAGAAAAGTTGAA +TAATCTGAATGACAAAGATTACATTGAAAAAATTGCGCGTGATGATTATTACTTAAGCAACAAAGGTGAAGTGATTTTTA +GGTTGCCAGAAGACAAAGATTCGTCTAGCTCAAAATCTTCGAAAAAATAAATCCAAATTGATTCAAAATTATCCGAGTAT +AGACATTGTGAAAAAATCCAAACAAGGATATAATAAGGGAAAATCGAATCAAATCGGGAGGATTTATTTAACATATGTCA +ATCGAAGTTGGAAATAAGCTTAAAGGTAAAGTCACTGGTATTAAAAAGTTTGGTGCATTCGTAGAATTACCTGAAGGAAA +AAGTGGTTTAGTTCACATTAGTGAAGTCGCAGATAATTATGTTGAAAACGTAGAAGAGCACCTTTCTGTTGGTGATGAAG +TAGACGTAAAAGTATTATCTATTGCTGATGATGGAAAAATTAGTCTTTCAATTAAGAAAGCTAAAGACCGTCCACGTAGA +CAACATACGAGTAAACCAAGTCATCAAAAACCAGTGCAAAAAGCCGAAGATTTTGAAAAGAAATTAAGCAATTTCTTAAA +AGATAGTGAAGATAAATTAACTTCAATCAAACGTCAAACAGAATCTAGACGCGGTGGCAAAGGTTCAAGACGTTAATTAA +AATAAATAAAGACTGTTTCGATAAGGAATATATTTAGAATGATGCGTATCGAATAATCGATTGCAGCGTTAGACAATCTA +AGACTGTTTCTTAAATAAGGAGCAGTCTCTTTTATTTGTAATGATATAACTAAGACTTATACCATTTTTGAAAATTGTAA +AAGTGAGGTGATGTTATGCAGTTAAATAGTAATGGTTGGCATGTTGATGACCATATTGTTGTCGCTGTTTCTACAGGTAT +TGATAGTATGTGTTTATTGTATCAACTACTAAATGATTATAAAGATAGTTATAGAAAACTAACATGCTTACATGTCAATC +ATGGCGTTAGGTCAGCTTCAATTGAGGAAGCCAGATTTTTAGAAGCATACTGCGAACGTCATCACATCGATTTACATATC +AAAAAGTTAGATTTGTCGCATAGTCTCGACCGAAATAACAGCATTCAGAATGAAGCTCGAATTAAACGTTACGAATGGTT +TGATGAAATGATGAATGTATTAGAAGCGGATGTATTGCTAACGGCGCATCATTTGGACGATCAATTAGAAACTATTATGT +ATCGTATTTTTAATGGGAAATCAACACGTAATAAACTAGGATTTGATGAGTTATCGAAGCGAAAAGGTTATCAGATTTAT +CGACCACTTTTAGCTGTCTCTAAAAAAGAAATAAAACAATTCCAAGAGAGATATCATATTCCATATTTTGAAGATGAATC +TAATAAAGATAACAAATATGTTAGAAATGATATTCGTAATAGAATTATTCCAGCTATTGATGAAAATAATCAACTTAAAG +TATCGCATTTATTAAAATTAAAACAATGGCATGATGAACAATATGATATTTTGCAATATTCAGCTAAACAATTTATTCAA +GAATTTGTGAAGTTTGATGAACAGTCAAAATATTTAGAGGTTTCTAGACAAGCTTTTAATAACTTACCAAACTCATTAAA +GATGGTTGTGTTGGACTGCCTATTATCAAAGTATTATGAGTTGTTTAATATTAGTGCTAAAACATACGAAGAGTGGTTTA +AACAATTTAGTAGTAAGAAAGCACAATTCAGTATTAATCTCACGGATAAATGGATAATTCAAATCGCATATGGTAAATTA +ATAATAATGGCTAAAAATAATGGCGATACATATTTTAGAGTTCAAACAATTAAAAAGCCAGGTAATTATATTTTTAACAA +ATATCGATTAGAGATACATTCTAATTTACCAAAATGTTTATTTCCGCTTACAGTGAGAACACGACAAAGTGGCGATACAT +TTAAACTGAATGGGCGCGATGGTTATAAGAAAGTGAATCGCCTGTTTATAGATTGTAAAGTGCCACAGTGGGTTCGGGAT +CAAATGCCAATCGTATTGGATAAACAACAGCGCATTATTGCGGTAGGAGATTTATATCAACAACAAACAATAAAAAAATG +GATTATAATTAGTAAAAATGGAGATGAATAGCGTTATGCATAATGATTTGAAAGAAGTATTGTTAACTGAAGAAGATATT +CAAAATATCTGTAAGGAATTGGGAGCACAATTAACAAAGGATTATCAAGGTAAACCATTAGTATGCGTGGGTATCTTAAA +AGGCTCAGCAATGTTTATGTCAGATTTAATTAAACGAATTGATACCCATTTATCAATTGATTTCATGGATGTTTCTAGTT +ATCACGGAGGCACTGAGTCAACTGGTGAAGTTCAAATCATTAAAGATTTAGGTTCTTCTATTGAAAATAAAGACGTATTA +ATTATTGAAGATATCTTAGAGACTGGTACTACACTTAAGTCAATTACTGAATTATTACAATCTAGAAAAGTTAATTCATT +AGAAATAGTTACTTTATTAGATAAACCAAACCGTCGTAAAGCGGACATTGAAGCTAAGTATGTAGGTAAAAAAATACCAG +ATGAATTTGTTGTTGGTTACGGTTTAGATTATCGTGAATTATACCGAAACTTACCATATATCGGTACGTTAAAACCTGAA +GTGTATTCAAATTAATTTTTTAATCAATTTCAGTTATTATTACTATGCGTTTGAGAAATAATAGTGTAGACTCAAAAATA +TGAAAAATGTATTTCATATATATTTAATTTTAGACAAGACATATGTCTTGAAAAGTTGAAAAATATAGAGATTGATAAAA +CTAATACGGGTGTGAATGACATTGATGTTAAGCTCAATTACTAGCTTATAAAACATGTCATATGTTACAATTTTTGTTAG +TTTTATTATGGGAAGTAGGAGGAAATGACGCATGCAGAAAGCTTTTCGCAATGTGCTAGTTATCGTAATAATAGGCGTTA +TTATTTTTGGTCTATTTTCATATTTAAACGGTAATGGAAATATGCCGAAACAGCTTACATATAATCAATTTACTGAGAAG +TTGGAAAAAGGTGACCTTAAAACTTTAGAAATCCAACCACAACAAAATGTCTATATGGTAAGTGGTAAAACGAAAAATGA +TGAAGACTATTCATCAACTATTTTATATAACAACGAAAAAGAATTACAAAAAATTACTGATGCTGCTAAAAAGCAAAACG +GTGTAAAATTAACGATTAAAGAAGAAGAAAAACAAAGTGTCTTTGTGAGTATACTTTCAACATTAATTCCAGTTGTAGTC +ATAGCGTTATTATTTATTTTCTTCCTAAGCCAAGCACAAGGTGGCGGTAGTGGCGGTCGTATGATGAACTTTGGTAAATC +TAAAGCAAAAATGTACGATAATAATAAACGTCGTGTTCGTTTCTCTGATGTAGCAGGGGCAGATGAAGAAAAACAAGAAT +TAATTGAAATTGTTGATTTCTTGAAAGATAATAAAAAATTCAAAGAAATGGGATCTAGGATTCCTAAAGGTGTCTTACTT +GTTGGACCTCCAGGTACTGGTAAAACATTACTTGCTAGAGCGGTTGCAGGTGAAGCTGGCGCACCATTCTTCTCTATTAG +TGGTTCAGACTTTGTAGAGATGTTTGTTGGTGTTGGTGCGAGCCGTGTTCGTGACTTATTCGATAATGCTAAGAAAAACG +CGCCTTGTATCATCTTTATCGATGAGATTGATGCTGTTGGTCGTCAACGTGGTGCAGGTGTTGGTGGCGGTCATGATGAA +CGTGAACAAACCCTAAACCAATTATTAGTTGAAATGGATGGTTTCGGTGAAAATGAAGGTATCATTATGATAGCTGCTAC +AAACCGTCCTGATATCCTTGACCCAGCCTTATTACGTCCAGGTCGTTTTGATAGACAAATTCAAGTTGGTCGTCCAGATG +TGAAAGGCCGTGAAGCAATTCTTCATGTTCATGCTAAAAACAAACCACTTGATGAAACGGTTGATTTAAAAGCAATTTCA +CAACGTACACCTGGTTTCTCAGGTGCTGATTTAGAGAACTTATTAAATGAAGCATCTTTAATTGCTGTACGTGAAGGTAA +AAAGAAAATTGACATGAGAGATATCGAAGAGGCAACGGATAGAGTTATAGCCGGACCTGCTAAGAAATCTCGAGTTATTT +CTAAGAAAGAACGTAATATTGTTGCTCATCACGAAGCTGGTCATACAATTATCGGTATGGTACTTGATGAGGCAGAAGTA +GTGCATAAAGTTACTATTGTTCCACGTGGACAAGCAGGTGGTTATGCAATGATGCTACCTAAACAAGATCGTTTCTTAAT +GACTGAACAAGAGTTATTAGATAAAATCTGTGGTTTACTTGGTGGACGTGTATCAGAAGATATTAACTTTAACGAAGTAT +CAACAGGTGCTTCAAATGACTTCGAACGTGCAACACAAATCGCACGCTCAATGGTTACGCAATATGGTATGAGTAAAAAA +TTAGGACCATTACAGTTCGGTCATAGCAATGGTCAAGTATTCTTAGGTAAAGATATGCAAGGTGAGCCTAATTATTCAAG +CCAAATCGCATATGAAATTGATAAAGAAGTTCAACGAATCGTTAAAGAACAATACGAACGTTGTAAACAAATTTTATTAG +AGCACAAAGAACAATTAATTTTAATTGCTGAAACATTATTAACAGAAGAAACATTAGTTGCTGAACAAATTCAATCATTA +TTCTACGAAGGTAAATTACCTGAAATTGATTATGATGCAGCTAAAGTTGTTAAAGATGAAGATTCTGAATTTAATGATGG +TAAATTCGGTAAATCTTATGAAGAGATTCGTAAAGAGCAATTAGAAGATGGACAACGTGACGAAAGTGAAGATCGTAAAG +AAGAAAAAGATATTGCTGAGGATAAAAAAGAAGCTGATAAATCTGATGAAAAAGATGAACCAGCACATCGACAAGCCCCA +AATATCGAAAAACCTTACGATCCAAATCACCCAGACAATAAATAATCGATTATATTCAGTACCTCTTTCTATGATAAAGT +TATAGAAAGAGGTACTTTTATCGTTTTTGAAAATACGTATTAGATTTTAAGTCGTTGAATTGTTATAGCAGAAAATAATT +GTAAAACAAGTTACTTCATTATTTAGAATGATGGGTGTAGAATAAGTACAATTGTTGCATTTTATGAAGTAAAGTAATTT +TTTAAATATAGAGTAATAGAGGAGATTGAAATAATGACACACGATTATATTGTTAAAGCATTAGCATTTGATGGAGAGAT +TAGGGCTTATGCTGCTTTGACAACTGAAACTGTTCAAGAAGCACAAACGAGACATTATACATGGCCGACAGCATCTGCTG +CAATGGGAAGAACAATGACAGCAACAGCTATGATGGGCGCAATGTTGAAAGGTGATCAAAAATTAACTGTCACTGTAGAT +GGCCAAGGACCTATTGGACGAATTATTGCCGATGCAAATGCTAAAGGCGAGGTGCGTGCTTATGTAGACCATCCACAAAC +TCATTTTCCATTAAATGAGCAAGGTAAACTTGATGTAAGACGAGCGGTAGGGACAAATGGATCTATTATGGTTGTTAAAG +ACGTTGGAATGAAAGACTATTTCTCTGGAGCAAGTCCAATTGTTTCAGGAGAACTTGGTGAAGATTTTACTTATTATTAT +GCTACAAGTGAACAAACACCTTCATCGGTAGGTCTTGGTGTATTGGTAAATCCTGATAATACGATTAAAGCAGCAGGAGG +ATTTATCATTCAAGTTATGCCAGGTGCCAAAGATGAAACAATTTCAAAATTAGAAAAAGCAATTAGTGAAATGACACCAG +TTTCTAAATTAATTGAACAAGGATTAACGCCAGAAGGATTACTAAACGAAATCTTAGGTGAAGACCATGTGCAAATTTTA +GAGAAAATGCCTGTTCAATTTGAATGTAATTGTAGTCATGAGAAATTTTTAAATGCTATTAAAGGATTGGGCGAGGCTGA +GATTCAAAATATGATTAAAGAAGATCATGGTGCTGAAGCAGTATGTCATTTCTGTGGAAATAAATATAAATATACTGAAG +AAGAATTAAACGTGTTGCTAGAAAGTTTAGCGTAATTTAATTTAAATCAATACGCTAAAATGTTTATTTTTAGCGGTTTA +GTGAAATGTAGAACTAAATAGTTGTATAATCCTTAGTGATTTTGTTTGCTTTCTAGAATTTATTTGATAAAATAATTCTA +TATCCGATAAATAAACTAAGATTTCAACAACTAACTAAAAAGGAGTGTTCTTAATGGCACAAAAACCAGTAGATAATATT +ACTCAAATTATTGGCGGTACACCGGTAGTCAAATTGAGAAATGTAGTAGATGACAATGCAGCAGATGTTTATGTAAAATT +GGAATATCAAAATCCAGGTGGTTCTGTAAAGGATAGAATTGCTTTAGCAATGATTGAAAAAGCAGAGCGAGAAGGCAAAA +TTAAACCTGGCGATACAATTGTAGAACCAACAAGTGGTAATACAGGTATCGGTTTAGCATTTGTATGTGCTGCTAAAGGA +TATAAAGCAGTATTTACTATGCCCGAAACAATGAGCCAAGAGCGTCGTAATTTATTAAAAGCATACGGTGCGGAATTAGT +TTTAACGCCTGGATCAGAAGCGATGAAAGGTGCAATTAAAAAAGCTAAAGAATTGAAAGAAGAACATGGTTACTTCGAGC +CACAACAATTTGAAAACCCTGCGAACCCTGAAGTTCATGAGTTAACTACAGGTCCTGAGTTATTACAACAATTTGAAGGG +AAAACTATCGATGCGTTCCTAGCTGGTGTTGGTACTGGTGGTACGTTATCTGGTGTAGGTAAAGTTCTGAAAAAAGAATA +TCCTAACATCGAAATTGTTGCTATAGAGCCTGAGGCTTCTCCAGTATTGAGCGGTGGTGAGCCAGGTCCACATAAATTAC +AAGGTTTAGGTGCTGGATTTATTCCAGGCACTTTGAATACAGAAATCTATGACAGTATTATTAAAGTAGGAAATGATACA +GCGATGGAAATGTCTCGTCGAGTTGCTAAAGAGGAAGGTATTTTAGCAGGTATTTCATCAGGTGCTGCGATTTATGCTGC +CATTCAAAAAGCAAAAGAATTAGGAAAAGGTAAAACAGTAGTAACAGTATTGCCGAGTAATGGTGAACGCTACTTATCAA +CACCTTTATATTCATTCGATGACTAATTAATGTCATTTAAAAGAGTGAGTTATCTTTTTGAGATAACTTGCTCTTTTTTT +CTACCATGTATATTTTTAAAAATATGAGCGTTAAATTAAACATTTTTCTGATAAAAATATCCAGTGAATGATAAGATAAT +AAACGTACATACTAATAACTAGTAAATAGCAGGAGTAAATTTTATTAGAGTTAAACAATACATAATTAAAGGGTGGTTAA +CATGACTAAAACAAAAATTATGGGCATATTAAACGTCACACCTGATTCATTCTCAGATGGTGGAAAATTTAATAATGTTG +AATCAGCTATAAATAGAGTGAAAGCCATGATAGATGAAGGTGCTGACATTATAGATGTTGGAGGTGTTTCAACGAGACCC +GGTCATGAAATGGTTTCATTAGAAGAAGAGATGAACAGAGTATTACCTGTTGTTGAAGCTATTGTCGGTTTTGATGTAAA +AATTTCAGTCGATACATTTCGAAGTGAGGTTGCTGAAGCATGTTTAAAATTAGGCGTTGATATGATTAATGATCAATGGG +CGGGTCTGTATGATCATCGTATGTTCCAAATTGTAGCTAAATATGACGCGGAAATTATTTTAATGCATAATGGAAATGGT +AATCGTGATGAACCGGTTGTCGAAGAAATGTTAACATCTTTGTTAGCACAAGCACATCAAGCTAAAATAGCTGGTATACC +TTCAAATAAAATTTGGCTAGATCCAGGTATAGGTTTCGCTAAAACTAGAAATGAAGAAGCCGAAGTTATGGCAAGACTGG +ATGAACTTGTTGCAACAGAATATCCAGTTTTATTAGCGACAAGCCGGAAACGTTTCACTAAAGAGATGATGGGTTATGAT +ACAACACCGGTTGAAAGAGATGAAGTAACTGCAGCTACGACTGCATATGGTATTATGAAAGGCGTTAGAGCAGTACGCGT +TCATAATGTCGAGTTGAATGCTAAATTAGCTAAAGGTATAGATTTTTTAAAGGAGAATGAAAATGCAAGACACAATCTTT +CTTAAAGGTATGCGCTTTTATGGATATCATGGTGCTTTATCAGCTGAAAATGAAATAGGGCAAATTTTCAAAGTGGATGT +AACTTTGAAAGTAGACTTAGCTGAAGCTGGGCGTACTGATAATGTTATTGATACAGTTCATTATGGTGAAGTGTTCGAAG +AGGTTAAATCAATTATGGAAGGTAAGGCCGTTAATTTACTTGAGCATCTAGCTGAACGTATTGCAAATCGTATAAATTCA +CAATATAATCGTGTAATGGAAACGAAAGTGAGAATCACTAAAGAAAACCCACCGATTCCGGGTCATTATGATGGAGTAGG +TATCGAAATAGTGAGGGAGAATAAATGATTCAAGCATACTTAGGATTAGGTAGTAATATTGGTGATAGAGAAAGCCAGTT +AAACGATGCTATAAAGATTTTGAATGAATATGATGGTATTAACGTATCTAATATTTCTCCGATTTATGAAACAGCACCAG +TTGGGTATACTGAGCAACCTAACTTTTTAAATTTGTGTGTTGAAATTCAAACAACACTCACAGTATTACAACTGTTGGAA +TGTTGTTTGAAGACAGAAGAATGTTTACACCGTATTAGAAAGGAACGATGGGGTCCTAGAACTTTAGATGTGGATATTTT +GTTGTATGGAGAAGAAATGATAGATTTACCAAAACTGTCGGTGCCACATCCGAGAATGAATGAACGTGCATTTGTTTTAA +TCCCATTAAATGATATAGCAGCAAATGTCGTAGAACCACGTTCGAAATTGAAAGTGAAAGATTTAGTTTTTGTCGATGAC +AGTGTAAAGAGATATAAATAATGTATTGTTGAGAACATTCATATTTATTGGGAATAGATTATTGATTATTGGAGATGTTT +CCGCATTTTGATTATCTCTAAATGCATTTGATTTCGAATTAGTATATGATGATTTAGTATGTGTTAGAATTAAGTTATTT +CATAAAATCAAAGTAAGCAATATTTGATAAATTGCTGTAATTTATTTAGGCTTTTGTTGAAGTTTGTGGAAGATAATTTT +TATACATTTACTTAGGGTTGTATTAATGATAATCGTATTTTAAATGAATAAGTAATTCATAAACGAAAACAGAAATTGCT +TTATTACATTTACAAGCTCTTGTAAAATTAAAAAAGTGATAAAATTGATGTTATGAATATAAGAAAACATGCCACATCAT +AACTTTAAGGTGTAATGGTTAATGATAAAGTATTAGAAACATCGAAAGAGATGTATGTTGAGCAAAAATGTCTGATATTT +TATAAAACTTTAAAGGAAAATGTTTGAGTGTACCAGTTGGAATACTAAAGGATTACAACAAGTTAAAGGAGAGAAAGTTA +TGTCAGAAGAAATGAATGACCAAATGTTGGTTCGACGTCAAAAATTACAAGAATTATATGATCTTGGTATAGACCCGTTT +GGTTCTAAATTTGACCGTTCAGGTTTATCTAGTGATTTGAAAGAAGAGTGGGACCAGTATTCTAAAGAAGAATTGGTAGA +AAAAGAAGCGGATAGTCATGTCGCTATAGCTGGACGATTAATGACTAAGCGTGGTAAAGGTAAAGCAGGATTTGCACACG +TTCAGGACTTAGCTGGACAAATTCAAATTTACGTTCGTAAAGATCAAGTTGGCGATGACGAATTTGATTTATGGAAAAAT +GCTGATTTAGGCGATATCGTTGGTGTTGAAGGTGTAATGTTCAAAACAAATACTGGCGAATTATCGGTTAAAGCGAAGAA +ATTCACGCTACTAACTAAATCATTGCGACCATTACCGGATAAATTCCACGGTTTACAGGATATTGAACAGAGATATCGTC +AAAGATATTTAGATTTAATTACGAACGAAGATAGCACTCGTACATTTATTAATCGTAGTAAAATCATTCAAGAAATGCGT +AATTATTTAAATAATAAAGGTTTCTTGGAAGTAGAAACACCTATGATGCACCAAATTGCTGGTGGAGCAGCTGCTAGACC +ATTTGTAACACATCATAATGCATTAGATGCAACGTTATACATGCGTATTGCTATTGAGTTGCATTTAAAACGTTTAATTG +TCGGTGGACTTGAAAAAGTATATGAAATTGGTAGAGTATTCCGTAATGAAGGTGTATCAACTAGACATAACCCTGAATTC +ACAATGATTGAATTATATGAAGCATATGCAGATTATCATGACATTATGGATTTAACAGAATCTATGGTGAGACATATTGC +CAATGAAGTGTTAGGTTCTGCAAAAGTACAATACAATGGGGAAACGATTGATTTAGAATCTGCTTGGACTCGTTTGCATA +TTGTTGATGCTGTAAAAGAAGCTACTGGTGTAGATTTTTATGAAGTTAAAAGTGATGAAGAAGCTAAAGCTTTAGCTAAA +GAACATGGTATTGAAATTAAAGATACAATGAAATATGGTCATATTTTAAATGAATTCTTTGAGCAAAAAGTTGAAGAAAC +ACTTATTCAGCCAACGTTTATCTATGGTCATCCGACTGAAATTTCACCTTTAGCGAAGAAAAATCCTGAAGATCCTAGAT +TTACTGATCGTTTCGAATTGTTCATTGTAGGTAGAGAGCATGCAAATGCATTTACTGAATTAAATGATCCTATTGATCAA +AAAGGTCGTTTTGAAGCGCAACTTGTTGAAAAAGCGCAAGGTAATGATGAAGCGCATGAAATGGATGAAGATTACATTGA +AGCGTTAGAATATGGTATGCCTCCGACAGGTGGTCTTGGTATCGGTATTGACAGATTGGTTATGTTATTAACTGACTCTC +CATCAATCAGAGACGTATTATTATTCCCTTATATGAGACAAAAATAAATGACGTTGATTGTTAGTAAGAGCTCTCGTGTA +TACAACATGTGTATGCGAGGGCTTTCTTAATTATGGTAATTAGTTCGTGTTTGAATGTTTTTGATAGTAATAGTTAACGA +TAGTGGTGCTATTTTTGACTGTTAAACAAGGTAGTTGGCTGATAGATGAAAATGACGATACGTATATAGAGTAGTTCGGA +TGAGAAATGTTAACGATAGACATTGAGATATCTTATAACAAAAATTGCTATATTAGTATAATTTATCTTAATCAGCTATA +AAAGTACTTTAAAATTGTATAGAATGTGTATGGTTTTGTACATATGTGTATGATAGAATACTAAAAGTGTTATGAATTAG +AGGGCACGAGAAATGTCAGTTTTGAAGAATAAAAAAGTTGATTAAAAGTGTTGACTTTATCAATTGAATGAAGTAATATA +TAAAAGTCGTCAAAAACAGACGAAACACACTAAAAGCTGATGTGACAAAGTTTACATCAAAGTGTAAAATTAACTATTGC +ACCTTATTAATTAAGCGTGTATCATGAATAAGTAAGTTATTTTGTCTGGTGACTATAGCAAGGAGGTCACACCTGTTCCC +ATGCCGAACACAGAAGTTAAGCTCCTTAGCGTCGATGGTAGTCGAACTTACGTTCCGCTAGAGTAGAACGTTGCCAGGCA +AATGACAAATCGGAGAATTAGCTCAGCTGGGAGAGCATCTGCCTTACAAGCAGAGGGTCGGCGGTTCGAACCCGTCATTC +TCCACCATTTATTCTTAGATATAGCCGGCCTAGCTCAATTGGTAGAGCAACTGACTTGTAATCAGTAGGTTGGGGGTTCA +AGTCCTCTGGCCGGCACCATCTTTTGAGCCATTAGCTCAGCTGGTAGAGCATCTGACTTTTAATCAGAGGGTCAGAGGTT +CGAATCCTCTATGGCTCATTACGATTTAATTTTTATATTTAGCAAAATAATGCAGAAGTAGTTCAGCGGTAGAATACAAC +CTTGCCAAGGTTGGGGTCGCGGGTTCGAATCCCGTCTTCTGCTCCATTATTTTGCCGGGGTGGCGGAACTGGCAGACGCA +CAGGACTTAAAATCCTGCGGTGAGAGATCACCGTACCGGTTCGATTCCGGTCCTCGGCACCATTTTAGCGCCCGTAGCTC +AATTGGATAGAGCGTTTGACTACGGATCAAGAGGTTATGGGTTCGACTCCTATCGGGCGCGCCATTTTTAAATTAATTGA +ATAACGGGAAGTAGCTCAGCTTGGTAGAGCACTTGGTTTGGGACCAAGGGGTCGCAGGTTCGAATCCTGTCTTCCCGATT +ACTTCTTAAATTCCATTTTATGGGGGCTTAGCTCAGCTGGGAGAGCGCCTGCTTTGCACGCAGGAGGTCAGCGGTTCGAT +CCCGCTAGTCTCCACCATTTATTTTTTACACGATGAACATTGAAAACTGAATGACAATATGTCAACGTTAATTCCAAAAA +ACGTAACTATAAGTTACAAACATTATTTAGTATTTATGAGCTAATCAAACATCATAATTTTTATGGAGAGTTTGATCCTG +GCTCAGGATGAACGCTGGCGGCGTGCCTAATACATGCAAGTCGAGCGAACGGACGAGAAGCTTGCTTCTCTGATGTTAGC +GGCGGACGGGTGAGTAACACGTGGATAACCTACCTATAAGACTGGGATAACTTCGGGAAACCGGAGCTAATACCGGATAA +TATTTTGAACCGCATGGTTCAAAAGTGAAAGACGGTCTTGCTGTCACTTATAGATGGATCCGCGCTGCATTAGCTAGTTG +GTAAGGTAACGGCTTACCAAGGCAACGATGCATAGCCGACCTGAGAGGGTGATCGGCCACACTGGAACTGAGACACGGTC +CAGACTCCTACGGGAGGCAGCAGTAGGGAATCTTCCGCAATGGGCGAAAGCCTGACGGAGCAACGCCGCGTGAGTGATGA +AGGTCTTCGGATCGTAAAACTCTGTTATTAGGGAAGAACATATGTGTAAGTAACTGTGCACATCTTGACGGTACCTAATC +AGAAAGCCACGGCTAACTACGTGCCAGCAGCCGCGGTAATACGTAGGTGGCAAGCGTTATCCGGAATTATTGGGCGTAAA +GCGCGCGTAGGCGGTTTTTTAAGTCTGATGTGAAAGCCCACGGCTCAACCGTGGAGGGTCATTGGAAACTGGAAAACTTG +AGTGCAGAAGAGGAAAGTGGAATTCCATGTGTAGCGGTGAAATGCGCAGAGATATGGAGGAACACCAGTGGCGAAGGCGA +CTTTCTGGTCTGTAACTGACGCTGATGTGCGAAAGCGTGGGGATCAAACAGGATTAGATACCCTGGTAGTCCACGCCGTA +AACGATGAGTGCTAAGTGTTAGGGGGTTTCCGCCCCTTAGTGCTGCAGCTAACGCATTAAGCACTCCGCCTGGGGAGTAC +GACCGCAAGGTTGAAACTCAAAGGAATTGACGGGGACCCGCACAAGCGGTGGAGCATGTGGTTTAATTCGAAGCAACGCG +AAGAACCTTACCAAATCTTGACATCCTTTGACAACTCTAGAGATAGAGCCTTCCCCTTCGGGGGACAAAGTGACAGGTGG +TGCATGGTTGTCGTCAGCTCGTGTCGTGAGATGTTGGGTTAAGTCCCGCAACGAGCGCAACCCTTAAGCTTAGTTGCCAT +CATTAAGTTGGGCACTCTAAGTTGACTGCCGGTGACAAACCGGAGGAAGGTGGGGATGACGTCAAATCATCATGCCCCTT +ATGATTTGGGCTACACACGTGCTACAATGGACAATACAAAGGGCAGCGAAACCGCGAGGTCAAGCAAATCCCATAAAGTT +GTTCTCAGTTCGGATTGTAGTCTGCAACTCGACTACATGAAGCTGGAATCGCTAGTAATCGTAGATCAGCATGCTACGGT +GAATACGTTCCCGGGTCTTGTACACACCGCCCGTCACACCACGAGAGTTTGTAACACCCGAAGCCGGTGGAGTAACCTTT +TAGGAGCTAGCCGTCGAAGGTGGGACAAATGATTGGGGTGAAGTCGTAACAAGGTAGCCGTATCGGAAGGTGCGGCTGGA +TCACCTCCTTTCTAAGGATATATTCGGAACATCTTCTTCAGAAGATGCGGAATAACGTGACATATTGTATTCAGTTTTGA +ATGTTTATTTAACATTCAAATATTTTTTGGTTAAAGTGATATTGCTTATGAAAATAAAGCAGTATGCGAGCGCTTGACTA +AAAAGAAATTGTACATTGAAAACTAGATAAGTAAGTAAAATATAGATTTTACCAAGCAAAACCGAGTGAATAAAGAGTTT +TAAATAAGCTTGAATTCATAAGAAATAATCGCTAGTGTTCGAAAGAACACTCACAAGATTAATAACGCGTTTAAATCTTT +TTATAAAAGAACGTAACTTCATGTTAACGTTTGACTTATAAAAATGGTGGAAACATAGATTAAGTTATTAAGGGCGCACG +GTGGATGCCTTGGCACTAGAAGCCGATGAAGGACGTTACTAACGACGATATGCTTTGGGGAGCTGTAAGTAAGCTTTGAT +CCAGAGATTTCCGAATGGGGAAACCCAGCATGAGTTATGTCATGTTATCGATATGTGAATACATAGCATATCAGAAGGCA +CACCCGGAGAACTGAAACATCTTAGTACCCGGAGGAAGAGAAAGAAAATTCGATTCCCTTAGTAGCGGCGAGCGAAATGG +GAAGAGCCCAAACCAACAAGCTTGCTTGTTGGGGTTGTAGGACACTCTATACGGAGTTACAAAGGACGACATTAGACGAA +TCATCTGGAAAGATGAATCAAAGAAGGTAATAATCCTGTAGTCGAAAATGTTGTCTCTCTTGAGTGGATCCTGAGTACGA +CGGAGCACGTGAAATTCCGTCGGAATCTGGGAGGACCATCTCCTAAGGCTAAATACTCTCTAGTGACCGATAGTGAACCA +GTACCGTGAGGGAAAGGTGAAAAGCACCCCGGAAGGGGAGTGAAATAGAACCTGAAACCGTGTGCTTACAAGTAGTCAGA +GCCCGTTAATGGGTGATGGCGTGCCTTTTGTAGAATGAACCGGCGAGTTACGATTTGATGCAAGGTTAAGCAGTAAATGT +GGAGCCGTAGCGAAAGCGAGTCTGAATAGGGCGTTTAGTATTTGGTCGTAGACCCGAAACCAGGTGATCTACCCTTGGTC +AGGTTGAAGTTCAGGTAACACTGAATGGAGGACCGAACCGACTTACGTTGAAAAGTGAGCGGATGAACTGAGGGTAGCGG +AGAAATTCCAATCGAACCTGGAGATAGCTGGTTCTCTCCGAAATAGCTTTAGGGCTAGCCTCAAGTGATGATTATTGGAG +GTAGAGCACTGTTTGGACGAGGGGCCCCTCTCGGGTTACCGAATTCAGACAAACTCCGAATGCCAATTAATTTAACTTGG +GAGTCAGAACATGGGTGATAAGGTCCGTGTTCGAAAGGGAAACAGCCCAGACCACCAGCTAAGGTCCCAAAATATATGTT +AAGTGGAAAAGGATGTGGCGTTGCCCAGACAACTAGGATGTTGGCTTAGAAGCAGCCATCATTTAAAGAGTGCGTAATAG +CTCACTAGTCGAGTGACACTGCGCCGAAAATGTACCGGGGCTAAACATATTACCGAAGCTGTGGATTGTCCTTTGGACAA +TGGTAGGAGAGCGTTCTAAGGGCGTTGAAGCATGATCGTAAGGACATGTGGAGCGCTTAGAAGTGAGAATGCCGGTGTGA +GTCGCGAAAGACGGGTGAGAATCCCGTCCACCGATTGACTAAGGTTTCCAGAGGAAGGCTCGTCCGCTCTGGGTTAGTCG +GGTCCTAAGCTGAGGCCGACAGGCGTAGGCGATGGATAACAGGTTGATATTCCTGTACCACCTATAATCGTTTTAATCGA +TGGGGGGACGCAGTAGGATAGGCGAAGCGTGCGATTGGATTGCACGTCTAAGCAGTAAGGCTGAGTATTAGGCAAATCCG +GTACTCGTTAAGGCTGAGCTGTGATGGGGAGAAGACATTGAGTCTTCGAGTCGTTGATTTCACACTGCCGAGAAAAGCCT +CTAGATAGAAAATAGGTGCCCGTACCGCAAACCGACACAGGTAGTCAAGATGAGAATTCTAAGGTGAGCGAGCGAACTCT +CGTTAAGGAACTCGGCAAAATGACCCCGTAACTTCGGGAGAAGGGGTGCTCTTTAGGGTTAACGCCCAGAAGAGCCGCAG +TGAATAGGCCCAAGCGACTGTTTATCAAAAACACAGGTCTCTGCTAAACCGTAAGGTGATGTATAGGGGCTGACGCCTGC +CCGGTGCTGGAAGGTTAAGAGGAGTGGTTAGCTTCTGCGAAGCTACGAATCGAAGCCCCAGTAAACGGCGGCCGTAACTA +TAACGGTCCTAAGGTAGCGAAATTCCTTGTCGGGTAAGTTCCGACCCGCACGAAAGGCGTAACGATTTGGGCACTGTCTC +AACGAGAGACTCGGTGAAATCATAGTACCTGTGAAGATGCAGGTTACCCGCGACAGGACGGAAAGACCCCGTGGAGCTTT +ACTGTAGCCTGATATTGAAATTCGGCACAGCTTGTACAGGATAGGTAGGAGCCTTTGAAACGTGAGCGCTAGCTTACGTG +GAGGCGCTGGTGGGATACTACCCTAGCTGTGTTGGCTTTCTAACCCGCACCACTTATCGTGGTGGGAGACAGTGTCAGGC +GGGCAGTTTGACTGGGGCGGTCGCCTCCTAAAAGGTAACGGAGGCGCTCAAAGGTTCCCTCAGAATGGTTGGAAATCATT +CATAGAGTGTAAAGGCATAAGGGAGCTTGACTGCGAGACCTACAAGTCGAGCAGGGTCGAAAGACGGACTTAGTGATCCG +GTGGTTCCGCATGGAAGGGCCATCGCTCAACGGATAAAAGCTACCCCGGGGATAACAGGCTTATCTCCCCCAAGAGTTCA +CATCGACGGGGAGGTTTGGCACCTCGATGTCGGCTCATCGCATCCTGGGGCTGTAGTCGGTCCCAAGGGTTGGGCTGTTC +GCCCATTAAAGCGGTACGCGAGCTGGGTTCAGAACGTCGTGAGACAGTTCGGTCCCTATCCGTCGTGGGCGTAGGAAATT +TGAGAGGAGCTGTCCTTAGTACGAGAGGACCGGGATGGACATACCTCTGGTGTACCAGTTGTCGTGCCAACGGCATAGCT +GGGTAGCTATGTGTGGACGGGATAAGTGCTGAAAGCATCTAAGCATGAAGCCCCCCTCAAGATGAGATTTCCCAACTTCG +GTTATAAGATCCCTCAAAGATGATGAGGTTAATAGGTTCGAGGTGGAAGCATGGTGACATGTGGAGCTGACGAATACTAA +TCGATCGAAGACTTAATCAAAATAAATGTTTTGCGAAGCAAAATCACTTTTACTTACTATCTAGTTTTGAATGTATAAAT +TACATTCATATGTCTGGTGACTATAGCAAGGAGGTCACACCTGTTCCCATGCCGAACACAGAAGTTAAGCTCCTTAGCGT +CGATGGTAGTCGAACTTACGTTCCGCTAGAGTAGAACGTTGCCAGGCAAAAAATGGATGCGATGAGCCGCATTGAGACCG +CAAGGTCTCTTTTTTTTATGTCTAAAACGTCAAAATAAAAAGCAAACACAAAGAAAAATGGCTTGGCGAAGTGAAAACGT +TTGAATCTGACGAAACGAGAAAAGAACGCAACGAGTTTAGTAGAGCTAAATGAGTAAGTGAGAGCCGAAGGAGAGGAAAG +AAGCAAGCGATTGTCACAAGTCAAGAAAGGTTCTTAGCGAGGATGGTAGCCAACTTACGTTCCGCTAGAGTAGAACTGGA +AATGATAATTTAATAATGTACACTTTCGATTGTCTAAGTATGTACAACTTTAATTTTGTGTTTATATAAATTTAAAATGA +TATCATCGAAAACAAAATATTGTATAAATAGAGAAGAGCAGTAAGACGGTATCTAATTGAAAATGATCTTACTGCTCTTT +TATATACTTTATTGAAATACAAAAAGGAAATTAATTATTATACAATAGACAAGCTATTGCATAAGTAACACTAACTTTTA +TCAAAGAAGTGTTACTTTATAATTAATGATTTTATTAGAGCGTCTACATGCGGTTTTAAAGCATCATCGTCTATACCGCC +AAAGCCTAATATAAATTTAGGGGTTTTCTTATAGTCTTGATCATCATCAAAATTATAAACTTGTAATTTTAACTTTACTT +TGTTTGCTCTATCAAGACACTCTTGTAATGTTAATCCATTTTTTACTGTAATTGTAAAATGCATACCCGTTTCAGCACCT +TGAATATCAAGCTGCTCTTTGTAAGGTTTCAATCTTTTTAAAATATAGGTTAGTTTTCTACGATAAATTCGTCTCATTTT +ATTTAAATGCCTTTCAAAACCACCGGAAGATATAAACGTTGCAATAAGGTTTTGCATATGAACAGGTACAGTGTTGCCTT +CAATGTGATTTTGAGAATGATATTTTTTCATTATAGAATAGGGTAACACCATATATGCAACTCGACAGCTAGGAAAAATA +GACTTTGAAAATGTACTGATATAAATCACTTTTTCTCCTCTTGAATATAGACCTTGAATTGCTGGAATGGGTTTGCCGAA +ATATCTAAACTCGGAATCATAATCATCTTCTATAATAAATCGTTCTTCTTTTTCTTGAGCCCATTGTATTAATTGAGTTC +GTTTTTTTAAGTCCATCACATATCCAGTTGGAAATTGATGGGAAGGCGTTATATATACTATATTTTTTTGTGATTTAATA +ACTTCATCTACGTTTATTCCATTATCTTCAACTTCAATTTGTTCATATTCAACTTGTTTTTTATCTAAAATATTTTTGAT +TGGTGGATAACTAGGTTTTTCGATAATAAATGTTGAAGTATAAAGTAAATCGACTAATTGATTTACTAATTGTTCGGTAG +ATGAGCCAATTATAATTTGATTAGGATCACAAATTACGCCACGATTAGTAAATAAATAAAATGCCAGTTGAAACCGCAAA +TGTAATTCTCCTTGAAAATGTCCTCTACGTAATTGATTTAAATGATTTGTATCATAAAGATCTTTGGAATACTTTCTGAA +AAGTTCTATAGGGAAATGTTTCGTATCTATTTCATCCAAATTAAAAGCATAATCATAAGCTTCATCACTCGCTTTTGGTT +TATATGAATCATCATCAAAAAGAGAGGGGATAGGTTGATTGTTTAAAATTGTTAAAGATTCAATTTCGGACACAAAATAT +CCAGAGCGAGGTCTTGAATAAATGTAACCTTCGTCTAATAGAAGTTGATATGCATGCTCTACGGTTGTTTGGCTAATAGA +TAAATGTTTGCTTAATTGTCTTTTAGAATAAAATTTATCGCCTTCTTTAAATTGACCTTCAATTATTTGTTTTTTTAATT +TTTCATAAAGTTGATGGTATAAAGTGTTTTTCAATTTTATAACTGACCTCCTAAATTTATCTTATTTTGTACCTTTTTAA +ATATCAGTTTATACATTACAATGTATTTAATCAACTTGAAAAGGGGTTTTATGTATAATGAGTAAAATTATTGGATCAGA +CAGAGTCAAAAGAGGTATGGCTGAAATGCAAAAAGGCGGCGTTATTATGGATGTCGTTAATGCTGAGCAAGCAAGAATTG +CAGAAGAAGCTGGCGCGGTAGCAGTTATGGCATTAGAACGAGTACCTTCTGATATTAGAGCTGCTGGTGGTGTTGCACGT +ATGGCAAACCCTAAAATTGTAGAAGAAGTAATGAATGCTGTTTCTATTCCAGTCATGGCTAAAGCACGTATTGGTCATAT +CACTGAAGCAAGAGTATTAGAGGCGATGGGTGTTGACTATATTGATGAATCAGAAGTGTTAACACCAGCAGATGAGGAAT +ATCACTTAAGAAAAGATCAATTTACAGTACCATTTGTATGTGGATGTCGTAATTTAGGTGAAGCTGCGCGTAGAATTGGT +GAAGGTGCTGCTATGTTACGTACTAAAGGTGAACCAGGTACAGGTAATATTGTTGAAGCTGTAAGACATATGAGACAAGT +TAATTCAGAAGTTAGTCGATTGACTGTAATGAATGATGATGAGATTATGACTTTTGCGAAAGATATCGGTGCGCCTTATG +AAATTTTAAAACAAATTAAAGACAATGGTCGTTTACCGGTAGTTAACTTTGCAGCTGGTGGCGTTGCGACTCCTCAAGAT +GCTGCTTTAATGATGGAATTAGGTGCTGACGGTGTATTCGTTGGATCAGGTATTTTTAAATCAGAAGATCCAGAAAAATT +TGCTAAAGCAATTGTTCAAGCAACAACACATTACCAAGACTATGAACTAATTGGAAGATTAGCAAGTGAACTTGGCACTG +CTATGAAAGGTTTAGATATCAATCAATTATCATTAGAAGAACGTATGCAAGAGCGTGGTTGGTAAGATATGAAAATAGGT +GTATTAGCATTACAAGGTGCAGTACGTGAACATATTAGACATATTGAATTAAGTGGTCATGAAGGTATTGCAGTTAAAAA +AGTTGAACAATTAGAAGAAATCGAGGGCTTAATATTACCTGGTGGCGAGTCTACAACGTTACGTCGATTAATGAATTTAT +ATGGATTTAAAGAGGCTTTACAAAATTCAACTTTACCTATGTTTGGTACATGCGCAGGATTAATAGTTCTAGCGCAAGAT +ATAGTTGGTGAAGAAGGATACCTTAACAAGTTGAATATTACTGTACAACGAAACTCATTCGGTAGACAAGTTGACAGCTT +TGAAACAGAATTAGATATTAAAGGTATCGCTACAGATATTGAAGGTGTCTTTATAAGAGCCCCACATATTGAAAAAGTAG +GTCAAGGCGTAGATATCCTATGTAAGGTTAATGAGAAAATTGTAGCTGTTCAGCAAGGTAAATATTTAGGCGTATCATTC +CATCCTGAATTAACAGATGACTATAGAGTAACTGATTACTTTATTAATCATATTGTAAAAAAAGCATAGCTTAATGTATG +CTAAATCAACGAATTATTGATATTTATAGATTTGTTGAGAAGAAAATATCTCCTTCAAACTTAGCTTTGGAGGAGTTATT +TTTTATGTCAAAATTAAAAATGATAAAAAATAAAGCTATACATAAGAAAAAAACCCTTCAAAGAGACTGAGAATAGTCAA +AATTTTGAAGGGGTTAATTCGATGTTGATGTATTTGTTAAATAAAGAATCCAGCGATTGCAGCTGAAATGAAAGATACTA +GTGTTGCACCGAATAATAATTTCAAACCAAAGCGGGCAACTGTATCTCCTTTTTTGTCATTAAGTGATTTAATCGCACCT +GAAATAATACCGATAGAGCTAAAGTTAGCAAATGATACTAAGAATACAGATGTAACACCTTTTGCGTGTTCAGATAAATC +ACTAAGTTTACCAAGTGCTTGCATTGCTACAAATTCGTTAGATAATAGTTTTGTCGCCATAACTGAACCGGCTTGAACTG +CATCTTGCCATGGCACACCGACTAAGAATGCAAATGGTGCAAAGACAAAACCAATTAATGTTTGGAAATCCCAAGAAATA +GCGCCACCTGAAACTGTACTAAAGATATTGCTTACAATTCCATTTAATAGAGCGATAATGGCAATGTATCCGATTAACAT +TGCGCCTACAATGACAGCTACTTTAAATCCATCTAAAATATATTCTCCTAGCATTTCGAAGAATGATTGTTGTCTTTCTT +CAGTTTCTTCAACTAATAATTTGTCATCTTCTTCATTAACTTTATAAGGGTTAATAATTGAAGCGATGATGAAACCACCA +AATAAGTTTAAGACAACAGCCGTTACAACATATTTAGGTTCAATTAAGGTAAAGTATGCACCGATAATTGAAGCAGAAAC +AGTCGACATTGCTGAAGCTGTTAATGTGTATAAACGTTGCTTAGGTATGTATGGTAATTGTTTTTTAATTGAAATAAATA +CTTCAGATTGTCCCAAAATTGCTGCAGCAACTGCATTGTATGATTCTAAACGTCCCATACCATTAATTTTAGAAATTAAG +AATCCTAAAACATTAATGATTAAAGGTAAAATCTTTGTGTATTGAAGGATACCGATAATCGCTGAAATAAATACGATAGG +TAATAATACACTGAAGAAGAATGGTGGTTGCTTAGGATCGATATATTGAATACCACCGAATACAAAGTTAACACCATCTG +CTGCTTTTAATAATAAGTAGTTAAAACCGTTTGAAATACCACCAATAACCTTGATTCCCATTGTAGTTTTAAGCAAGATA +AATGCAAAGATAAGCTGAATTGCAAGTAAAATTCCTACATATTTCCAGCGAATATTTTTCCTGTCTGAGCTAAATAGAAA +CGCAAGTGCTAAAAAGAAGATAATTCCGATAATCCCAATTAGAATATGCATATATTTCTCATTCCTTTAGTTTTTTCTAC +AATCTATCATACAATAAAATGGAAGGGCTAACATCATAAATTTTTGAAAATATAAAAACAAATTAATTGAAAAAGGTCAA +AATAGGTCATATAATATAGTCAAAGAAGGTCAAAAAGGGGTGATATACATGCACAATATGTCTGACATCATAGAACAATA +CATCAAACGTTTATTTGAAGAGTCGAATGAAGATGTCGTTGAAATTCAGAGAGCGAATATCGCACAGCGTTTTGATTGCG +TACCATCACAATTAAATTATGTAATCAAAACACGATTCACTAATGAACATGGTTATGAAATCGAAAGTAAACGTGGTGGT +GGTGGTTACATCCGAATCACTAAAATTGAAAATAAAGATGCAACAGGTTATATTAATCATTTGCTTCAGCTGATTGGACC +TTCTATTTCTCAACAACAAGCTTATTATATTATTGATGGGCTTTTAGATAAAATGTTAATAAATGAACGTGAAGCTAAAA +TGATTCAAGCAGTTATTGATAGAGAAACGCTATCAATGGATATGGTTTCTAGAGATATTATTAGAGCAAATATTTTAAAA +CGTTTGTTACCAGTTATAAATTATTACTAAATGAAATGAGGTGTTGAAGTGCTTTGTGAAAATTGTCAACTTAATGAAGC +GGAATTAAAAGTTAAAGTTACAAGTAAAAATAAAACAGAAGAAAAAATGGTGTGTCAAACTTGTGCTGAGGGGCACCATC +CGTGGAATCAAGCTAATGAACAACCTGAATATCAAGAACATCAAGATAATTTCGAAGAAGCATTTGTTGTTAAGCAAATT +TTACAACATTTAGCTACGAAACATGGAATTAATTTTCAAGAAGTAGCGTTTAAAGAAGAAAAACGTTGCCCATCATGTCA +TATGACTTTGAAAGATATTGCACATGTTGGTAAATTTGGGTGTGCTAATTGTTATGCAACATTTAAAGATGACATCATTG +ATATCGTCCGCAGAGTTCAAGGTGGACAATTTGAGCACGTTGGAAAGACACCACATTCTTCACATAAAAAGATAGCTTTA +AAGCGAAAAATCGAAGAAAAGAATGAATATTTGAAAAAACTTATTGAAATCCAAGATTTTGAGGAAGCAGCCATTGTTAG +AGATGAAATTAAAGCACTAAAAGCTGAGAGTGAGGTGCAACATGATGACGCATAATATTCATGATAATATCAGCCAATGG +ATGAAAAGTAATGAAGAAACACCAATTGTTATGTCTTCTAGAATTCGGTTAGCGCGTAATTTAGAAAATCATGTGCATCC +ACTAATGTATGCTACTGAAAATGATGGATTTAGAGTTATAAATGAGGTACAAGATGCCTTGCCAAACTTTGAATTAATGC +GTCTTGATCAAATGGATCAACAAAGTAAAATGAAAATGGTTGCAAAGCATTTGATTAGTCCTGAACTAATAAAACAACCA +GCAGCCGCAGTATTAGTGAATGACGATGAATCTTTAAGTGTCATGATAAATGAAGAGGACCATATTCGTATTCAAGCTAT +GGGAACTGACACGACATTACAGGCTTTATATAATCAAGCTTCATCAATTGATGATGAATTAGATCGAAGCCTTGATATAA +GTTATGATGAACAACTTGGTTATTTAACTACATGTCCTACCAATATAGGTACTGGTATGAGAGCAAGCGTGATGCTACAT +TTGCCAGGTCTATCTATTATGAAAAGAATGACACGGATTGCTCAAACCATTAATCGTTTTGGATATACAATCAGAGGTAT +TTACGGTGAAGGTTCGCAAGTTTATGGACATACTTATCAAGTATCCAACCAACTTACACTTGGTAAATCTGAGTTAGAAA +TCATAGAAACATTAACAGAAGTTGTTAATCAAATCATTCATGAAGAAAAACAAATACGACAAAAGTTAGACACTTATAAT +CAATTAGAAACACAAGACCGTGTTTTTCGCTCGCTAGGTATTTTACAAAACTGTAGAATGATAACTATGGAAGAGGCTTC +TTATAGATTAAGCGAAGTTAAACTTGGTATAGATTTAAATTACATTGAATTACAAAACTTTAAATTTAATGAATTGATGG +TAGCTATACAGTCACCATTTTTATTAGATGAAGAAGATGACAAATCTGTAAAAGAAAAACGAGCAGATATACTAAGAGAA +CATATAAAGTAGGAGGTCATTATTTATGTTATTTGGTAGATTAACTGAGCGTGCACAGCGCGTATTAGCACATGCCCAAG +AAGAAGCAATTCGTTTAAATCATTCAAATATAGGAACAGAACACCTATTATTGGGGTTAATGAAAGAACCTGAAGGAATT +GCTGCAAAAGTATTAGAAAGTTTTAATATCACTGAAGATAAAGTAATTGAAGAAGTTGAAAAATTAATCGGACATGGTCA +AGATCATGTTGGTACATTGCATTATACACCTAGAGCTAAAAAAGTCATTGAATTATCGATGGATGAAGCTAGAAAATTAC +ATCACAATTTTGTTGGAACGGAACATATTTTATTAGGCTTGATTCGTGAAAATGAAGGTGTTGCAGCAAGAGTTTTTGCA +AATCTAGATTTAAATATTACTAAAGCACGTGCACAAGTTGTGAAAGCTTTAGGAAACCCTGAAATGAGTAATAAAAATGC +ACAAGCTAGTAAGTCAAATAATACTCCAACTTTAGATAGTTTAGCTCGTGACTTAACAGTCATTGCCAAAGACGGTACAT +TAGATCCTGTTATAGGACGTGATAAAGAAATTACACGTGTAATTGAAGTATTAAGTAGACGTACGAAAAACAATCCTGTG +CTTATTGGAGAGCCAGGTGTTGGTAAAACTGCTATTGCTGAAGGTTTAGCGCAAGCCATAGTGAATAATGAGGTACCAGA +GACATTAAAAGATAAGCGTGTTATGTCTTTAGATATGGGAACAGTAGTTGCAGGTACTAAATATCGTGGTGAATTTGAAG +AGCGTCTGAAAAAGGTTATGGAAGAAATCCAACAAGCAGGTAATGTCATCCTATTTATTGATGAGTTGCATACTTTAGTT +GGTGCTGGTGGTGCTGAAGGTGCTATCGATGCTTCGAATATTTTGAAGCCGGCATTAGCACGTGGTGAATTACAATGTAT +TGGTGCTACTACATTAGATGAATATCGCAAAAATATTGAAAAAGACGCGGCTTTAGAACGTCGTTTCCAACCTGTACAAG +TTGATGAACCTTCAGTAGTAGATACAGTTGCTATTTTAAAAGGATTAAGAGATCGTTACGAAGCACACCATCGTATTAAT +ATTTCAGACGAAGCTATTGAAGCAGCTGTTAAATTAAGTAACAGATACGTTTCAGATCGTTTCTTACCAGATAAAGCAAT +TGATTTAATTGATGAAGCAAGTTCTAAAGTAAGACTTAAGAGTCATACGACACCTAATAATTTAAAAGAAATTGAACAAG +AAATTGAAAAAGTTAAAAATGAAAAAGATGCCGCAGTACATGCTCAAGAGTTTGAAAATGCTGCTAACCTGCGTGATAAA +CAAACAAAACTTGAAAAGCAATATGAAGAAGCTAAAAATGAATGGAAGAATGCACAAAATGGCATGTCAACTTCATTGTC +AGAAGAAGATATTGCTGAAGTTATTGCAGGATGGACAGGTATCCCATTAACTAAAATCAATGAAACAGAATCTGAAAAAC +TTCTTAGTCTAGAAGATACATTACATGAGAGAGTTATTGGGCAAAAAGATGCTGTTAATTCAATCAGTAAAGCGGTTAGA +CGTGCCCGTGCAGGGTTAAAAGATCCTAAACGACCAATTGGTAGCTTTATCTTCCTTGGACCAACTGGTGTTGGTAAAAC +TGAATTAGCTAGAGCTTTAGCTGAATCAATGTTTGGCGATGATGATGCGATGATCCGTGTAGACATGAGTGAATTTATGG +AAAAACACGCAGTGAGCCGATTAGTTGGTGCTCCTCCAGGATATGTTGGTCATGATGATGGTGGACAATTAACTGAAAAA +GTTAGACGTAAACCATATTCTGTAATTTTATTTGATGAAATTGAAAAAGCTCATCCAGATGTATTTAATATTCTATTACA +AGTTTTAGATGATGGACATTTGACAGATACAAAAGGACGTACAGTTGATTTCAGAAATACAATTATCATAATGACATCAA +ACGTTGGGGCACAAGAATTACAAGATCAACGATTTGCTGGATTCGGTGGTTCAAGTGATGGACAAGATTATGAAACAATT +CGAAAAACGATGTTAAAAGAATTAAAAAATTCATTCCGTCCAGAATTTTTAAACCGTGTAGATGATATCATTGTATTCCA +TAAACTAACAAAAGAAGAATTAAAAGAAATTGTAACAATGATGGTTAATAAATTAACAAATCGATTATCTGAACAAAACA +TAAATATTATTGTAACTGATAAAGCGAAAGACAAAATCGCAGAAGAAGGATATGATCCAGAATATGGTGCAAGACCATTA +ATTAGAGCGATACAAAAAACTATCGAAGATAATTTAAGTGAATTAATATTAGATGGTAATCAAATTGAAGGTAAGAAAGT +TACAGTAGATCATGATGGTAAAGAGTTTAAATATGACATTGCTGAACAAACTTCAGAAACTAAAACACCATCGCAAGCAT +AATTATAAAACAGTCCAAAACAAATTAAAGTTTTGGGCTGTTTTTTTAGTAGCATTGAACTATAGAAATTCGTGAAAGTA +TCCATCAACGAAACAATCTAATAAAACAATCATCAAAGGATAGTTAAGAATTATATGTAACAAGTTAATGAGCCTACAGC +GATAGCATAAAGGTATGAATTTTTATAAAGGTTTTTTGTTTGAAGATACTAGCAACTTACGTCAAAATAAAATAGGTCTA +TATAATATTGTGATAAATCTAGAGAAAAGAGTTTACTAAGAAATTATTAAGATTTTATCTTTGAAAGATACCGACTATCA +ACATGATGTAAGTTTATTTTATAATATTCATAAAAAATAAATCTGGTAAAACAGTTTGCGTTAGTAGTCATGTTAAAATA +CAAACAAATGTAACTCACATTTAATTTGTCATAATGGGAATGTGCGTTTAAAATAGATTTGTTCAAAGGAAAGTGGAGGT +GCAATTTTGGCCAAGAAAAAAGTGATTTTTGAATGTATGGCTTGTGGTTATCAATCTCCTAAATGGATGGGGAAATGTCC +TAATTGTGGCGCTTGGAATCAAATGGAGGAAATTGTTGAAAAAGCAGCCAATCCTAAACATGGAGTTAAAACCAAGGAAT +TAGCAGGTAAAGTACAAAAATTAAATAGTATTAAACATGAAACAACGCCGAGAGTGTTAACAGATTCAGCAGAATTCAAC +CGTGTATTAGGTGGAGGTATTGTGAGCGGATCGTTAGTACTTATTGGTGGGGATCCAGGTATTGGTAAGTCAACGTTACT +TTTACAAATTTGTGCATCGTTATCTCAAAAGAAAAAAGTACTATATATTACTGGAGAAGAATCGCTTAGTCAGACTAAAT +TACGTGCAGAGCGATTAGATGAAGATTCAAGTGAATTGCAAGTATTAGCTGAAACAGATCTTGAAGTTATTTATCAAACA +GTAAAAGAAGAACAACCTGATTTATTAGTAGTGGATTCGATTCAAACAATATATCATCCTGAAATCAGCTCTGCGCCAGG +TTCTGTTTCACAAGTTCGTGAAAGTACACAAAGTTTAATGAATATTGCTAAACAAATGAACATTGCAACTTTTATAGTGG +GTCATGTAACGAAAGAAGGTCAAATTGCTGGCCCAAGATTGCTAGAACACATGGTTGATACTGTGCTTTATTTTGAAGGC +GATGAACACCACGCATATCGAATTTTGCGAGCTGTTAAAAACCGTTTTGGTTCAACGAATGAAATGGGAATCTTCGAAAT +GAAGCAAAGTGGATTAAAAGGTGTAAATAATCCATCTGAAATGTTTTTAGAAGAACGTTCAACAAATGTTCCAGGTTCAA +CAATTGTTGCAACCATGGAGGGAACCAGACCACTTTTAATAGAAGTTCAAGCGCTGGTAACTCCAACGACTTTTAACAAT +CCGAGACGAATGGCAACAGGGATTGATCATAATCGATTAAGTTTGTTGATGGCTGTTTTGGAAAAGAAAGAAAATTATCT +ATTACAACAACAAGATGCTTATATCAAAGTAGCTGGCGGTGTAAAGTTAACGGAGCCAGCAGTTGATTTAAGTGTAATTG +TAGCAACTGCATCTAGCTTTAAAGATAAAGCTGTCGACGGATTAGATTGCTATATTGGAGAAGTTGGTTTAACGGGTGAG +GTACGTCGTGTATCTCGGATAGAACAACGCGTGCAAGAGGCTGCAAAACTAGGTTTCAAACGTGTAATTATTCCTAAAAA +TAATATAGGCGGATGGACATATCCTGAAGGTATACAAGTAATAGGTGTAACTACTGTACATGAAGTATTGTCATTTGCTC +TTCATTCATAAAACATCAAGAAAGGAGGACATTGTGTGAATATCGTTAAACTAATGGTTATTATTATTTACTTAATTATT +GGGAGCGCATTAGGAATAATTATTATTCCTGAAATTGCAAATGATCTTGGATTACAAAACTCCAGCTTTTTAAAAAATCA +CTATGTAGATGGCATTATCGGTAGTATTTTTATGTTCTTAATTTTTGGTGTATTTATTAGACGAGTTACTAACGCTATAA +AAGGTTTAGAACATTTTATTATGCGTAGAAGTGCTGTTGAAATACTATTCGCAACAATAGGTTTAATAATCGGATTACTT +ATTTCTGTTATGGTGTCGTTTATATTAGAATCAATTGGCAACTCTATTTTTAATCATTTCATTCCTGTCATAATTACGAT +ATTACTATGTTATTTCGGTTTCCAATTTGGCCTTAAAAAACGAGATGAAATGTTAATGTTTTTACCTGAGAATATAGCGC +GTTCCATGTCACAACATACTAAAAGTGCTACGCCAAAAATTATCGACACAAGCGCAATTATTGATGGTCGTATTTTAGAA +GTCATTCGTTGCGGTTTTATCGATGGCAATATTTTAATTCCACAAGGTGTTATTAATGAATTACAAATTGTTGCAGATTC +AAATGACAGTGTTAAACGTGAAAAGGGTAAAAGAGGCTTAGATATTTTAAATGAATTGTATGATTTAGACTATCCTACAA +AGGTTATACATCCAACTAAAACACATAGTGATATTGATACGATGTTATTAAAATTAGCAAAACAATATCATGCAAGTATT +ATAACGACAGATTTCAACCTAAATAAAGTTTGTCATGTACATGGTATCAAAGCATTAAATGTTAATGATTTATCAGAAGC +AATCAAACCTAATGTACATCAAGGTGATCAACTGCATATTTTACTGACAAAAATGGGTAAGGAGCCTGGTCAGGCAGTAG +GATATCTAGATGATGGTACGATGGTAGTTGTTGATAATGCTAAAAATCTTATTGGCAGTCATGTCAATTTAGAAGTAGTC +AGCTTATTGCAAACATCTTCAGGAAGAATTGTTTTTGCTAAAAAAATCGAAGATACAGTATCATTATAAAAACTGTAGTT +GACGAAAACAAAATAGATATTAAAAGGTATATGAAATGGCGAAGTTTGTAATTTGAAGTATAACTATAAATAAATGCAAA +ACATATTAGTTATCAAAAAATACAAATAGTGCTAATATATCATTGAAACAAGTTCATTAAGTGTTAAATTTTAGATATTA +ATAAAGTTATTCTTATAGCAATCAAGACATACAAAATTAGAATCAATTTTAACGTATTTGAATAGTTAATTTGAAGTTTA +ATTTCACAGTACATAATATTCATTTTTAAGTCGTTGAAATGTTTATGTAGTGCAGTCTTGATTCTTAGGGATAGCTTTTT +TAAAGTGTTGAAAAAACAACAGCTTTCTATTTAAAATTTAAGCATATTACCCAGGGAATTATGTTATGATTAAACAATGA +AATTATTTTAAGGTCATGTTAAAAAGCATTGTTTCGTTTTAACAATAAGACGAGTTAGACATTTGTTATGACGGTCGAAG +ATAGCAAATGAACAATTTATATGAACTAACTTGAAAATCTGATATTTAATTTAGGAGTGAAAAAAATGAGCGATCGTATA +AGAGTAAGATATGCACCAAGTCCAACTGGGTATCTTCATATTGGTAATGCAAGAACAGCATTATTCAATTACTTGTATGC +TAAACATTACAACGGAGATTTTGTGATTCGAATTGAAGATACTGATAAAAAACGTAATTTAGAAGATGGAGAAACATCAC +AATTTGATAATCTTAAATGGTTAGGATTAGATTGGGATGAGTCTGTAGATAAAGACAATGGCTACGGACCATATCGTCAA +TCTGAACGTCAACATATCTACCAACCATTAATAGATCAGTTACTAGCAGAAGATAAAGCATATAAATGCTATATGACAGA +AGAAGAATTAGAAGCTGAACGTGAAGCGCAAATCGCTCGTGGTGAAATGCCTCGCTATGGTGGTCAACATGCGCATTTGA +CTGAAGAACAACGTCAACAATTTGAAGCAGAAGGACGCCAACCATCAATTCGTTTCCGAGTACCTCAAAACCAAACGTAT +TCATTTGATGATATGGTAAAAGGAAATATTTCATTTGATTCAAATGGTATTGGTGACTGGGTTATCGTAAAAAAAGATGG +CATTCCAACGTACAATTTTGCAGTAGCTATAGATGATCATTACATGCAAATTTCAGATGTAATTCGTGGTGATGATCATA +TTTCAAACACGCCTAAACAAATTATGATTTATGAAGCATTTGGCTGGGAGCCACCTCGTTTTGGTCATATGTCATTAATT +GTTAATGAAGAACGTAAAAAGTTAAGTAAACGTGATGGGCAAATTTTACAATTTATTGAGCAATATCGTGACTTAGGTTA +TTTACCTGAAGCGTTATTTAATTTTATTGCGTTATTAGGTTGGTCTCCTGAAGGTGAAGAAGAAATCTTTTCTAAAGAAG +AATTTATCAAAATCTTTGATGAAAAGCGTTTGTCAAAATCACCAGCATTTTTCGATAAGCAAAAATTAGCATGGGTTAAT +AACCAATATATGAAACAAAAAGATACTGAAACAGTATTCCAATTAGCATTACCTCATTTAATTAAAGCAAATTTGATTCC +TGAGGTGCCGTCAGAAGAGGATTTATCTTGGGGACGCAAATTAATTGCGCTTTATCAAAAAGAAATGAGTTATGCCGGTG +AAATTGTACCTTTATCAGAAATGTTCTTTAAAGAAATGCCAGCTCTTGGTGAAGAAGAACAACAAGTGATTAATGGAGAG +CAAGTACCAGAGTTAATGACGCACTTATTCAGTAAATTAGAAGCACTTGAACCATTTGAAGCGGCTGAAATTAAAAAGAC +AATTAAAGAAGTTCAAAAAGAAACAGGAATAAAAGGCAAGCAATTATTTATGCCTATTCGTGTTGCTGTAACAGGCCAAA +TGCATGGTCCTGAATTACCAAATACAATTGAAGTACTTGGTAAAGAAAAAGTGCTAAACCGTTTAAAACAATATAAGTAA +TGAAATAGACAAGATCAAGGTAGTATATACTTGAATTTTATAACGTAACCACATATGATAAAGTTGTATTAACAAATTAC +AACAATATGAATAAAGTATTTTATATAAAACGAAGGATTAGTAATTAAATTTATACGATGCAGAGAGTGTACGGTTGCTG +TGAGTACAACGTAGAAATTAATGAATGCACCTTCGTAAAATGAATTAAATATATAATGAGAGTGATGAGCATTAAGTTGA +CTTAGTTTCCTTGATAATTTGGAAGCGCCCGCAATATTATTAATGTTATTCGCTAAATTCAGAGTGGAACCGTGCGGAAG +CGCCTCTAACAATACAATTTGTATGTTAGTGGTGCTTTTTTGATATTTAATTTCGCAGGTACTTCAATTATGTATTTACG +TAGTTCAATCATTGAGGAGGAAATGATCTTGTTAAAAAGAATGAGAGACGATATAAAAATGGTATTTGAGCAGGATCCAG +CGGCACGTTCAACATTAGAAGTCATTACAACGTATGCAGGTTTACATGCAGTTTGGAGTCATTTGATTGCACATAAGTTA +TACAACCAAAAAAAATATGTTGCAGCACGCGCGATATCTCAAATTTCAAGATTTTTCACAGGTATAGAAATCCATCCAGG +TGCTAAAATTGGAAAGCGTCTATTTATAGATCATGGTATGGGCGTTGTAATAGGAGAAACATGTACAATTGGTGATAATG +TGACAATCTATCAAGGCGTGACACTTGGTGGGACAGGGAAAGAAAGAGGGAAAAGACACCCAGATATAGGAGACAATGTT +TTAATAGCAGCCGGTGCGAAAGTTTTAGGAAATATTAAAATAAATTCAAATGTAAATATTGGTGCAAATTCAGTTGTTTT +ACAATCAGTTCCAAGTTATTCAACGGTTGTTGGTATACCAGGACATATTGTTAAGCAAGATGGTGTTCGAGTTGGAAAAA +CATTTGATCATCGCCATCTACCTGATCCAATTTATGAACAAATTAAGCATTTAGAACGACAACTTGAAAAGACTAGGAAT +GGAGAGATTCAAGATGATTACATTATATAATACGCTTACACGTCAAAAAGAAGTGTTCAAGCCTATAGAACCAGGGAAAG +TAAAAATGTATGTATGTGGTCCTACTGTATATAACTACATTCATATTGGTAACGCAAGACCAGCAATTAATTATGACGTA +GTGAGACGTTACTTTGAATACCAAGGATATAATGTAGAATATGTATCAAATTTTACAGACGTAGATGATAAATTAATTAA +ACGTTCTCAAGAATTAAATCAGTCTGTTCCCGAAATTGCAGAAAAATATATCGCTGCTTTTCATGAAGATGTTGGTGCGT +TAAATGTTAGAAAAGCGACTTCAAATCCAAGGGTAATGGACCATATGGATGACATTATTCAATTTATTAAAGATTTGGTG +GATCAAGGTTATGCATATGAAAGTGGTGGCGATGTTTACTTTAGAACACGTAAATTTGAAGGTTATGGTAAATTAAGTCA +TCAATCCATAGATGACTTAAAAGTGGGTGCTCGTATAGATGCAGGAGAGCATAAAGAAGATGCACTTGATTTTACATTGT +GGAAAAAAGCGAAGCCTGGCGAGATTAGTTGGGATAGCCCATTTGGTGAAGGTAGACCAGGATGGCATATAGAATGTTCT +GTAATGGCATTTCATGAGCTAGGACCTACAATTGATATACATGCGGGTGGTTCAGATTTACAATTTCCACATCATGAAAA +TGAAATAGCACAATCAGAAGCACATAATCATGCGCCATTTGCTAATTATTGGATGCATAATGGTTTCATTAATATTGATA +ATGAAAAAATGAGTAAATCACTAGGCAACTTTATTTTAGTTCACGATATTATTAAAGAAGTTGATCCAGATGTACTAAGA +TTCTTTATGATTAGCGTACATTATAGAAGCCCAATTAACTATAATCTAGAATTGGTAGAATCAGCACGTAGTGGACTAGA +GCGTATTCGCAATAGTTATCAATTAATTGAAGAGCGCGCACAAATTGCTACTAATATTGAAAATCAACAGACATATATTG +ATCAAATTGATGCGATTTTAAATCGTTTTGAAACAGTTATGAATGATGATTTTAATACAGCTAATGCAATTACAGCTTGG +TATGATTTAGCAAAACTTGCGAATAAATATGTACTAGAGAACACAACATCAACAGAAGTAATTGATAAATTTAAAGCAGT +TTATCAAATTTTCAGCGATGTTTTAGGTGTACCGTTAAAATCTAAAAATGCAGATGAATTATTGGATGAAGATGTTGAAA +AATTAATCGAAGAGCGTAATGAAGCAAGGAAAAACAAAGATTTTGCACGAGCAGATGAAATTCGAGACATGCTGAAATCA +CAAAACATTATATTAGAAGACACACCTCAAGGGGTTAGATTTAAACGTGGATAATCAACAAGATAATCACATTAAATTAT +TGAATCCATTGACCTTAGCATATATGGGAGACGCAGTCTTAGATCAATATGTACGTACCTATATCGTTTTAAAGCTTAAA +AGTAAGCCTAATAAACTACATCAAATGTCTAAAAAATATGTATCTGCCAAAAGTCAGGCGCAAACGTTAGAATATTTAAT +GGAGCAAGAATGGTTTACAGACGAAGAAATGGATATTTTGAAGCGAGGGCGTAACGCGAAAAGTCATACTAAAGCTAAAA +ACACTGATGTTCAAACATATCGTAAAAGTTCAGCGATAGAAGCAGTGATAGGTTTTCTTTATTTAGAAAAAAGAGAAGAA +CGATTAGAGGCATTATTAAATAAAATAATAACAATAGTAAACGAAAGGTAGTGACGATGTGGAAGATACGGTTATTGTTG +GTAGGCATGCTGTTAGAGAAGCGATTATTACTGGGCATCCGATAAATAAGATATTGATTCAAGAAGGTATTAAAAAGCAA +CAAATTAATGAAATTTTAAAAAATGCAAAAGATCAAAAAATCATTGTTCAAACTGTACCAAAATCTAAATTAGATTTTTT +AGCAAATGCACCACATCAGGGTGTTGCAGCGCTTATTGCACCATATGAATATGCTGACTTCGATCAATTTTTAAAACAGC +AAAAAGAAAAAGAAGGTTTATTGACAGTACTTATATTAGACGGCTTAGAAGACCCACATAACTTGGGATCAATTTTAAGA +ACAGCCGATGCAACGGGAGTTGATGGTGTTATTATTCCTAAACGTCGTTCAGTTACACTAACGCAAACAGTTGCAAAAGC +CTCAACAGGTGCAATTGAACATGTACCAGTTATTCGAGTGACAAATTTAGCTAAAACTATCGATGAACTAAAAGATAATG +GCTTTTGGGTAGCTGGCACTGAAGCTAATAATGCAACAGATTATAGAAATCTAGAAGCGGACATGTCATTGGCTATTGTA +ATTGGTAGCGAAGGACAGGGTATGAGTCGCCTAGTAAGTGATAAATGCGATTTTTATATTAAGATTCCAATGGTTGGACA +TGTAAACAGTTTGAATGCTTCGGTTGCAGCAAGTTTAATGATGTACGAAGTATTTCGAAAAAGACATGATGTTGGAGAAA +TATAATGAAAGAACGTTACTTAATCATTGATGGATACAATATGATAGGACAATCACCAACGCTAAGCGCCATTGCAAAAG +AGAATTTAGAAGAAGCTAGAATGCAATTAATAGATGCAATTGCAAATTATAATGCAGTTATTTCAGATGAAATTATTTGT +GTTTTCGATGCTTATGACCAATCGGGTGTTGAAAGAGAATACATGTATCATGGCGTTAAAACGATTTTTACCAAGGAAAA +AGAAACAGCTGATAGTTTCATAGAACGTTATGTTTATGAACTTTATGACAAGCATACTAAGCATATTACAGTTGTAACAA +GTGATATGAGTGAGCAACATGCTATCTTTGGATCAGGTGCATATAGAATATCATCTCGCGAAATGTGGAGAGATTTAAAA +GAAAATGAAATTGATGTGAGTAAATCATTAGATGATATAAGTGAAAACAAGCCAAGAACTCGAATTCCGTTATCTTCTGA +AATCCTTGCAGAATTTGAAAAAATACGAAGAGGACATCATAAGAAATGACATTTCCGTCATCTTGAAATTTTGAATGTAA +CAATATTAAACTAACCTTAAATTTTAGATAGAAGGGGTTAGTTTAATATTTGAAATACGATTTGACAACTCAAGACAGTA +CAATCAAACGTAACAATGCAATAAATGATAAAGACTTCGAAAAGTTAGTAATGGATCTACAACCATTAATTATTCGACGC +ATCAAAACATTTGGATTTAATCATTATGATTTAGAAGACTTATATCAAGAAATACTTATACGGATGTATAGGTCGGTCCA +AACATTTGATTTTAGTGGAGAGCAGCCTTTCACAAATTATGTTCAATGTTTAATTACGTCTGTAAAGTATGATTATTTGA +GAAAATATTTAGCTACAAATAAAAGAATGGATAATTTGATTAATGAATATAGAGTTACGTATCCATGTGCAATAAAGCGT +TTTGATGTTGAAAACAATTATTTGAATAAATTAGCAATTAAAGAGTTGATTTGTCAGTTTAAGTATTTGAGTGCATTTGA +AAAAGATGTCATGTATTTAATGTGTGAACAATATAAGCCGAGAGAAATTGCTCAATTGATGCATGTAAAAGAGAAAGTGA +TTTATAATGCCATACAACGATGTAAAAATAAAATAAAACGTTATTTCAAAATGATTTGAAAAGCGCCTTAGGACGTGAAT +TGAATTATAACGTGTTACTTACTGATGGTTTGACATTTGTTATAAATTTTATGTATAGTATACTGGTATTATAATGAATA +AAGGTGAATTATTGTGAGAAAAATACCTTTAAATTGTGAAGCTTGTGGCAATAGAAATTATAATGTTCCTAAGCAAGAAG +GCTCGGCAACAAGATTAACCTTAAAGAAATATTGTCCAAAATGTAACGCGCACACAATTCATAAAGAATCGAAATAAATA +CATTCGAAATAATACTTTGATAATATGTTCAAAGGATTTGGAGGTTGAGCAGATGGCTAAAAAAGAAAGTTTCTTTAAAG +GCGTTAAGTCTGAAATGGAAAAAACAAGTTGGCCGACGAAAGAAGAGCTATTTAAATATACTGTAATTGTAGTTTCTACT +GTTATATTCTTCTTAGTCTTTTTCTATGCCTTAGATTTAGGAATTACAGCATTGAAAAATTTATTATTTGGTTAGAGGAG +TGAAGACATGTCTGAAGAAGTTGGCGCAAAGCGTTGGTATGCAGTGCATACATATTCTGGATATGAAAATAAAGTTAAAA +AGAATTTAGAAAAAAGAGTAGAATCTATGAATATGACTGAACAAATCTTTAGAGTAGTCATACCGGAAGAAGAAGAAACT +CAAGTAAAAGATGGCAAAGCTAAAACGACTGTTAAAAAAACATTCCCTGGATATGTTTTAGTGGAATTAATCATGACAGA +TGAATCATGGTATGTGGTAAGAAATACACCAGGCGTTACTGGTTTTGTAGGTTCTGCAGGTGCAGGGTCTAAGCCAAATC +CATTGTTACCAGAAGAAGTTCGCTTCATCTTAAAACAAATGGGTCTTAAAGAAAAGACTATCGATGTTGAACTCGAAGTT +GGCGAGCAAGTTCGTATTAAATCAGGTCCATTTGCGAATCAAGTTGGTGAAGTTCAAGAAATTGAAACAGATAAGTTTAA +GCTAACAGTATTAGTAGATATGTTTGGCCGAGAAACACCAGTAGAAGTTGAATTCGATCAAATAGAAAAGCTTTAATTAA +CAATTAAAGTTATTAAACTAACCAAAAGATAAAAAAGAGTATTGATTTTTTAATTAGAAAAGTGTTAAAATTATGTGGTC +GCGCTTTTAGAGCGCCCATTTCGTCACGAAATGTTAAGAGTGGGAGGGCAAAACTGAGCCCTGTGACCACATCACGATAT +CAAGGAGGTGCACATCGTGGCTAAAAAAGTAGATAAAGTTGTTAAATTACAAATTCCTGCAGGTAAAGCGAATCCAGCAC +CACCAGTTGGTCCAGCATTAGGTCAAGCAGGTGTGAACATCATGGGATTCTGTAAAGAGTTCAATGCACGTACTCAAGAT +CAAGCAGGTTTAATTATTCCGGTAGAAATCAGTGTTTATGAAGATCGTTCATTTACATTTATTACAAAAACTCCACCGGC +TCCAGTATTACTTAAAAAAGCAGCTGGTATTGAAAAAGGTTCAGGCGAACCAAACAAAACTAAAGTTGCTACAGTAACTA +AAGATCAAGTACGCGAAATTGCTAACAGCAAAATGCAAGACTTAAACGCTGCTGACGAAGAAGCAGCTATGCGTATTATC +GAAGGTACTGCACGTAGTATGGGTATCGTTGTAGAATAATTTTACGAATATTAAATTTGATTACATGATTTAAACGATGA +AGCAGATAACAGAGATAATAATGATGAATTATAAATATAATCTGAATGACTAGATTAATGATTGATTTATTCATAAGATT +AATTCTTCTGTTGTCTGCTCTTAACTTGCATATAGCAAGTAATGTGGGAGGAAATTCCGCTAAAACCACTAAAGGAGGAA +CTATAAATGGCTAAAAAAGGTAAAAAGTATCAAGAAGCAGCTAGTAAAGTTGACCGTACTCAGCACTACAGTGTTGAAGA +AGCAATTAAATTAGCTAAAGAAACAAGCATTGCTAACTTTGACGCTTCTGTTGAAGTTGCATTCCGTTTAGGAATTGATA +CACGTAAAAATGACCAACAAATCCGTGGTGCAGTTGTATTACCAAACGGAACTGGTAAATCACAAAGTGTATTAGTATTC +GCTAAAGGTGACAAAATTGCTGAAGCTGAAGCAGCAGGTGCTGACTATGTAGGTGAAGCAGAATACGTTCAAAAAATCCA +ACAAGGTTGGTTCGACTTCGATGTAGTAGTTGCTACACCAGACATGATGGGTGAAGTTGGTAAATTAGGTCGTGTATTAG +GACCAAAAGGTTTAATGCCAAACCCTAAAACTGGAACTGTAACAATGGATGTTAAAAAAGCTGTTGAAGAAATCAAAGCT +GGTAAAGTAGAATATCGTGCTGAAAAAGCTGGTATCGTACATGCATCAATTGGTAAAGTTTCATTTACTGATGAACAATT +AATTGAAAACTTCAATACTTTACAAGATGTATTAGCTAAAGCTAAACCATCATCTGCTAAAGGTACATACTTCAAATCTG +TTGCTGTAACTACAACAATGGGTCCTGGAGTTAAAATTGATACTGCAAGTTTCAAATAATAAATGATATAAACAATTACA +GGCTGAAAGAAATATCTTTCAGTCTGTAAAAATATATTGACAATAAGTAATTTCCAAGTTATATTACTTATTGTGATTAT +TTTACCTAAGACAGTAGGAGTTATTTATAACTTAAAATTTATCCTGCCGAGGCTAAAATTGACTTGAACGTGATGATCTA +TGATCTTTCAAGCACTTTTTGCCGTGGGTAGAAAGTGCTTTTTTTATTAATTTTAAAAAAAGCACCAAAAATTTAAATGG +AGGTGTCTGAATGTCTGCTATCATTGAAGCTAAAAAACAACTAGTTGATGAAATTGCTGAGGTACTATCAAATTCAGTTT +CAACAGTAATCGTTGACTACCGTGGATTAACAGTAGCTGAAGTTACTGACTTACGTTCACAATTACGTGAAGCTGGTGTT +GAGTATAAAGTATACAAAAACACTATGGTACGTCGTGCAGCTGAAAAAGCTGGTATCGAAGGCTTAGATGAATTCTTAAC +AGGTCCTACTGCTATTGCAACTTCAAGTGAAGATGCTGTAGCTGCAGCGAAAGTAATTTCTGGATTTGCTAAAGATCATG +AAGCATTAGAAATTAAATCAGGCGTTATGGAAGGCAATGTTATTACAGCAGAAGAAGTTAAAACTGTTGGTTCATTACCT +TCACACGATGGTCTTGTATCTATGCTTTTATCAGTATTACAAGCTCCTGTACGCAACTTCGCTTATGCGGTTAAAGCTAT +TGGAGAACAAAAAGAAGAAAACGCTGAATAATTTTTAGCGTAAAAAAATTAAAAATAATGGAGGAATTATAAAATGGCTA +ATCATGAACAAATCATTGAAGCGATTAAAGAAATGTCAGTATTAGAATTAAACGACTTAGTAAAAGCAATTGAAGAAGAA +TTTGGTGTAACTGCAGCTGCTCCAGTAGCAGTAGCAGGTGCAGCTGGTGGCGCTGACGCTGCAGCAGAAAAAACTGAATT +TGACGTTGAGTTAACTTCAGCTGGTTCATCTAAAATCAAAGTTGTTAAAGCTGTTAAAGAAGCAACTGGTTTAGGATTAA +AAGATGCTAAAGAATTAGTAGACGGAGCTCCTAAAGTAATCAAAGAAGCTTTACCTAAAGAAGAAGCTGAAAAACTTAAA +GAACAATTAGAAGAAGTTGGAGCTACTGTAGAATTAAAATAATTCAAGTATCTTAAACTTAATAATCAAAGTTTTATAGC +AAGTATTGCTATAATATAATGATTCTTTGAGAAGTTAAAACCCCGTTATTTTGATAACGGGGTTTTATTCATTTAAAGAC +TGAGTGAAATGTTATAATTATAATGACGAGTTACAAAGTGAAGATGAGGTGGAATAATGAGTCATTATTACGATGAAGAT +CCAAGTGTAATTAGCAATGAACAACGTATTCAATATCAATTAAACCATCATAAAATTGATTTAATAACTGATAACGGAGT +GTTTTCGAAAGATAAAGTAGATTATGGTTCAGATGTTCTTGTTCAAACTTTTTTAAAAGCGCATCCACCTGGTCCAAGTA +AGCGAATTGCCGATGTTGGTTGTGGTTACGGACCAATTGGTTTGATGATTGCTAAAGTATCACCACATCATTCAATTACA +ATGCTAGATGTTAATCACAGAGCGCTAGCCTTAGTTGAAAAAAACAAAAAATTAAATGGTATTGATAATGTGATCGTAAA +GGAAAGTGATGCTTTGTCTGCTGTGGAAGACAAAAGTTTTGATTTTATTTTAACCAATCCACCAATAAGAGCAGGGAAAG +AAACCGTGCATCGTATATTCGAGCAAGCATTACATAGATTAGACTCGAACGGTGAACTATTCGTTGTAATTCAGAAGAAG +CAAGGTATGCCATCTGCAAAGAAAAGAATGAATGAACTTTTTGGAAATGTAGAAGTGGTAAATAAAGATAAAGGATATTA +CATTCTGAGAAGTATAAAAGCTTGAAATGAAATGGATATTCTGTTATAGTTATATAATGTAAAAATTTATGTTCAATAAG +TGTGTACTTTTACGTTAAATAGATAAGTTAATTAAGAATAAATATAGAATCGAAAATGGTGTCATCATTAGTGTTGCCGT +TTTCTTTTTGTCTTTTTATTAATATGCTTATGGTATTTAGCTAAAAGCGGATCACATAATTTTTGAGGGGTGAATCTGTT +TGGCAGGTCAAGTTGTCCAATATGGAAGACATCGTAAACGTAGAAACTACGCGAGAATTTCAGAAGTATTAGAATTACCA +AACTTAATAGAAATTCAAACTAAATCTTACGAGTGGTTCCTAAGAGAAGGTTTAATCGAAATGTTTAGAGACATTTCTCC +AATTGAAGATTTTACTGGTAATTTGTCATTAGAGTTTGTGGATTACCGTTTAGGAGAACCAAAATATGATTTAGAAGAAT +CTAAAAACCGTGACGCTACTTATGCTGCACCTCTTCGTGTAAAAGTGCGTCTAATCATTAAAGAAACAGGAGAAGTTAAA +GAACAAGAAGTCTTTATGGGTGATTTCCCATTAATGACTGATACAGGTACGTTCGTTATCAATGGTGCAGAACGTGTAAT +CGTATCTCAATTAGTTCGTTCACCATCCGTTTATTTCAATGAAAAAATCGACAAAAATGGTCGTGAAAACTATGATGCAA +CAATTATTCCAAACCGTGGTGCATGGTTAGAATATGAAACAGATGCTAAAGATGTTGTATACGTACGTATTGATAGAACA +CGTAAACTACCATTAACAGTATTGTTACGTGCATTAGGTTTCTCAAGCGACCAAGAAATTGTTGACCTTTTAGGTGACAA +TGAATATTTACGTAATACTTTAGAGAAAGACGGCACTGAAAACACTGAACAAGCGTTATTAGAAATCTATGAACGTTTAC +GTCCAGGTGAACCACCAACTGTTGAAAATGCTAAAAGTCTATTGTATTCACGTTTCTTTGATCCAAAACGCTATGACTTA +GCAAGCGTGGGTCGTTATAAAACAAACAAAAAATTACATTTAAAACATCGTTTATTTAATCAAAAATTAGCTGAGCCAAT +TGTAAATACTGAAACTGGTGAAATTGTAGTTGAAGAAGGTACAGTGCTTGATCGTCGTAAAATCGACGAAATCATGGATG +TACTTGAATCAAATGCAAACAGCGAAGTGTTTGAATTGCATGGTAGCGTTATAGACGAGCCAGTAGAAATTCAATCAATT +AAAGTATATGTTCCTAACGATGATGAAGGTCGTACGACAACTGTAATTGGTAATGCTTTCCCTGACTCAGAAGTTAAATG +CATTACACCAGCAGATATCATTGCTTCAATGAGTTACTTCTTTAACTTATTAAGCGGTATTGGATATACAGATGATATTG +ACCATTTAGGTAACCGTCGTTTACGTTCTGTAGGTGAATTACTACAAAACCAATTCCGTATCGGTTTATCAAGAATGGAA +AGAGTTGTACGTGAAAGAATGTCAATTCAAGATACTGAGTCTATCACACCTCAACAATTAATTAATATTCGACCTGTTAT +TGCATCTATTAAAGAATTCTTTGGTAGCTCTCAATTATCACAATTCATGGACCAAGCAAACCCATTAGCTGAGTTAACGC +ATAAACGTCGTCTATCAGCATTAGGACCTGGTGGTTTAACACGTGAACGTGCTCAAATGGAAGTACGTGACGTTCACTAC +TCTCACTATGGCCGTATGTGTCCAATTGAAACACCTGAGGGACCAAACATTGGATTGATTAACTCATTATCAAGTTATGC +ACGTGTAAATGAATTCGGCTTTATTGAAACACCATATCGTAAAGTTGATTTAGATACACATGCTATCACTGATCAAATTG +ACTATTTAACAGCTGACGAAGAAGATAGCTATGTTGTAGCACAAGCAAACTCTAAATTAGATGAAAATGGTCGTTTCATG +GATGATGAAGTTGTATGTCGTTTCCGTGGTAACAATACAGTTATGGCTAAAGAAAAAATGGATTATATGGATGTATCGCC +GAAGCAAGTTGTTTCAGCAGCGACAGCATGTATTCCATTCTTAGAAAATGATGACTCAAACCGTGCATTGATGGGTGCGA +ACATGCAACGTCAAGCAGTGCCTTTGATGAATCCAGAAGCACCATTTGTTGGTACAGGTATGGAACACGTTGCAGCACGT +GATTCTGGTGCGGCTATTACAGCTAAGCACAGAGGTCGTGTTGAACATGTTGAATCTAATGAAATTCTTGTTCGTCGTCT +AGTTGAAGAGAACGGCGTTGAGCATGAAGGTGAATTAGATCGCTATCCATTAGCTAAATTTAAACGTTCAAACTCAGGTA +CATGTTACAACCAACGTCCAATCGTTGCAGTTGGAGATGTTGTTGAGTATAACGAGATTTTAGCAGATGGACCATCTATG +GAATTAGGAGAAATGGCATTAGGTAGAAACGTAGTAGTTGGTTTCATGACTTGGGACGGTTACAACTATGAGGATGCCGT +TATCATGAGTGAAAGACTTGTGAAAGATGACGTGTATACTTCTATTCATATTGAAGAGTATGAATCAGAAGCACGTGATA +CTAAGTTAGGACCTGAAGAAATCACAAGAGATATTCCTAATGTTTCTGAAAGTGCACTTAAGAACTTAGACGATCGTGGT +ATCGTTTATATTGGTGCAGAAGTAAAAGATGGAGATATTTTAGTTGGTAAAGTAACGCCTAAAGGTGTAACTGAGTTAAC +TGCCGAAGAAAGATTGTTACATGCAATCTTTGGTGAAAAAGCACGTGAAGTTAGAGATACTTCATTACGTGTACCTCACG +GCGCTGGCGGTATCGTTCTTGATGTAAAAGTATTCAATCGTGAAGAAGGCGACGATACATTATCACCTGGTGTAAACCAA +TTAGTACGTGTATATATCGTTCAAAAACGTAAAATTCATGTTGGTGATAAGATGTGTGGTCGACATGGTAACAAAGGTGT +CATTTCTAAGATTGTTCCTGAAGAAGATATGCCTTACTTACCAGATGGACGTCCGATCGATATCATGTTAAATCCTCTTG +GTGTACCATCTCGTATGAACATCGGACAAGTATTAGAGCTACACTTAGGTATGGCTGCTAAAAATCTTGGTATTCACGTT +GCATCACCAGTATTTGACGGTGCAAACGATGACGATGTATGGTCAACAATTGAAGAAGCTGGTATGGCTCGTGATGGTAA +AACTGTACTTTATGATGGACGTACAGGTGAACCATTCGATAACCGTATTTCAGTAGGTGTAATGTACATGTTGAAACTTG +CGCACATGGTTGATGATAAATTACATGCGCGTTCAACAGGACCATATTCACTTGTTACACAACAACCACTTGGCGGTAAA +GCGCAATTCGGTGGACAACGTTTTGGTGAGATGGAGGTATGGGCACTTGAAGCATATGGTGCTGCATACACATTACAAGA +AATCTTAACTTACAAATCCGATGATACAGTAGGACGTGTGAAAACATACGAGGCTATTGTTAAAGGTGAAAACATCTCTA +GACCAAGTGTTCCAGAATCATTCCGAGTATTGATGAAAGAATTACAAAGTTTAGGTTTAGATGTAAAAGTTATGGATGAG +CAAGATAATGAAATCGAAATGACAGACGTTGATGACGATGATGTTGTAGAACGCAAAGTAGATTTACAACAAAATGATGC +TCCTGAAACACAAAAAGAAGTTACTGATTAATACGCAATTTACAAAACAGGCAAAAAGATACTAAGCTGAATTTTATTGA +TGATTCAGTTTAGTACTTTAAGCCATTTTAAATAAATGCAAATCAATCAAATAGCACAGCTAATCTAAATTGAAGGAGGT +AGGCTCCTTGATTGATGTAAATAATTTCCATTATATGAAAATAGGATTGGCTTCACCTGAAAAAATCCGTTCTTGGTCTT +TTGGTGAAGTTAAAAAACCTGAAACAATCAACTACCGTACATTAAAACCTGAAAAAGATGGTCTATTCTGTGAAAGAATT +TTCGGACCTACAAAAGACTGGGAATGTAGTTGTGGTAAATACAAACGTGTTCGCTACAAAGGCATGGTCTGTGACAGATG +TGGAGTTGAAGTAACTAAATCTAAAGTACGTCGTGAAAGAATGGGTCACATTGAACTTGCTGCTCCAGTTTCTCACATTT +GGTATTTCAAAGGTATACCAAGTCGTATGGGATTATTACTTGACATGTCACCAAGAGCATTAGAAGAAGTTATTTACTTT +GCTTCTTATGTTGTTGTAGATCCAGGTCCAACTGGTTTAGAAAAGAAAACTTTATTATCTGAAGCTGAATTCAGAGATTA +TTATGATAAATACCCAGGTCAATTCGTTGCAAAAATGGGTGCAGAAGGTATTAAAGATTTACTTGAAGAGATTGATCTTG +ACGAAGAACTTAAATTGTTACGCGATGAGTTGGAATCAGCTACTGGTCAAAGACTTACTCGTGCAATTAAACGTTTAGAA +GTTGTTGAATCATTCCGTAATTCAGGTAACAAACCTTCATGGATGATTTTAGATGTACTTCCAATCATCCCACCAGAAAT +TCGTCCAATGGTTCAATTAGATGGTGGACGATTTGCAACAAGTGACTTAAACGACTTATACCGTCGTGTAATTAATCGAA +ATAATCGTTTGAAACGTTTATTAGATTTAGGTGCACCTGGTATCATCGTTCAAAACGAAAAACGTATGTTACAAGAAGCC +GTTGACGCTTTAATTGATAATGGTCGTCGTGGTCGTCCAGTTACTGGCCCAGGTAACCGTCCATTAAAATCTTTATCTCA +TATGTTAAAAGGTAAACAAGGTCGTTTCCGTCAAAACTTACTTGGTAAACGTGTTGACTATTCAGGACGTTCAGTTATTG +CAGTAGGTCCAAGCTTGAAAATGTACCAATGTGGTTTACCAAAAGAAATGGCACTTGAACTATTTAAACCATTCGTAATG +AAAGAATTAGTTCAACGTGAAATTGCAACTAACATTAAAAATGCGAAGAGTAAAATCGAACGTATGGATGATGAAGTTTG +GGACGTATTGGAAGAAGTAATTAGAGAACATCCTGTATTACTTAACCGTGCACCAACACTTCATAGACTTGGTATTCAAG +CATTTGAACCAACTTTAGTTGAAGGTCGTGCGATTCGTCTACATCCACTTGTAACAACAGCTTATAACGCTGACTTTGAC +GGTGACCAAATGGCGGTTCACGTTCCTTTATCAAAAGAGGCACAAGCTGAAGCAAGAATGTTGATGTTAGCAGCACAAAA +CATCTTGAACCCTAAAGATGGTAAACCTGTAGTTACACCATCACAAGATATGGTACTTGGTAACTATTACCTTACTTTAG +AAAGAAAAGATGCAGTAAATACAGGCGCAATCTTTAATAATACAAATGAAGTATTAAAAGCATATGCAAATGGCTTTGTA +CATTTACACACTAGAATTGGTGTACATGCAAGTTCGTTCAATAATCCAACATTTACTGAAGAACAAAACAAAAAGATTCT +TGCTACGTCAGTAGGTAAAATTATATTCAATGAAATCATTCCAGATTCATTTGCTTATATTAATGAACCTACGCAAGAAA +ACTTAGAAAGAAAGACACCAAACAGATATTTCATCGATCCTACAACTTTAGGTGAAGGTGGATTAAAAGAATACTTTGAA +AATGAAGAATTAATTGAACCTTTCAACAAAAAATTCTTAGGTAATATTATTGCAGAAGTATTCAACAGATTTAGCATCAC +TGATACATCAATGATGTTAGACCGTATGAAAGACTTAGGATTCAAATTCTCATCTAAAGCTGGTATTACAGTAGGTGTTG +CTGATATCGTAGTATTACCTGATAAGCAACAAATACTTGATGAGCATGAAAAATTAGTCGACAGAATTACAAAACAATTC +AACCGTGGTTTAATCACTGAAGAAGAAAGATATAATGCAGTTGTTGAAATTTGGACAGATGCAAAAGATCAAATTCAAGG +TGAATTGATGCAATCACTTGATAAAACTAACCCAATCTTCATGATGAGTGATTCAGGTGCCCGTGGTAACGCATCTAACT +TTACACAGTTAGCAGGTATGCGTGGATTGATGGCCGCACCATCTGGTAAGATTATCGAATTACCAATCACATCTTCATTC +CGTGAAGGTTTAACAGTACTTGAATACTTCATCTCAACTCACGGTGCACGTAAAGGTCTTGCCGATACAGCACTTAAAAC +AGCTGACTCAGGATATCTTACTCGTCGTCTTGTTGACGTGGCACAAGATGTTATTGTTCGTGAAGAAGACTGTGGTACTG +ATAGAGGTTTATTAGTTTCTGATATTAAAGAAGGTACAGAAATGATTGAACCATTTATCGAACGTATTGAAGGTCGTTAT +TCTAAAGAAACAATTCGTCATCCTGAAACTGATGAAATAATCATTCGTCCTGATGAATTAATTACACCTGAAATTGCTAA +GAAAATTACAGATGCTGGTATTGAACAAATGTATATTCGCTCAGCATTTACTTGTAACGCACGACATGGTGTTTGTGAAA +AATGTTACGGTAAAAACCTTGCTACTGGTGAAAAAGTTGAAGTTGGTGAAGCAGTTGGTACAATTGCAGCCCAATCTATC +GGTGAACCAGGTACACAGCTTACAATGCGTACATTCCATACAGGTGGGGTAGCAGGTAGCGATATCACACAAGGTCTTCC +TCGTATTCAAGAGATTTTCGAAGCACGTAACCCTAAAGGTCAAGCGGTAATTACGGAAATCGAAGGTGTCGTAGAAGATA +TTAAATTAGCAAAAGATAGACAACAAGAAATTGTTGTTAAAGGTGCTAATGAAACAAGATCATACCTTGCTTCAGGTACT +TCAAGAATTATTGTAGAAATCGGTCAACCAGTTCAACGTGGTGAAGTATTAACTGAAGGTTCTATTGAACCTAAGAATTA +CTTATCTGTTGCTGGATTAAACGCGACTGAAAGCTACTTATTAAAAGAAGTACAAAAAGTTTACCGTATGCAAGGTGTAG +AAATCGACGATAAACACGTTGAGGTTATGGTTCGACAAATGTTACGTAAAGTTAGAATTATCGAAGCAGGTGATACGAAG +TTATTACCAGGTTCATTAGTTGATATTCATAACTTTACAGATGCAAATAGAGAAGCATTTAAACACCGTAAGCGTCCTGC +AACAGCTAAACCAGTATTACTTGGTATTACTAAAGCATCACTTGAAACAGAAAGTTTCTTATCTGCAGCATCATTCCAAG +AAACAACAAGAGTTCTTACAGATGCAGCAATTAAAGGTAAGCGTGATGACTTATTAGGTCTTAAAGAAAACGTAATTATT +GGTAAGTTAATTCCAGCTGGTACTGGTATGAGACGTTATAGCGACGTAAAATACGAAAAAACAGCTAAACCAGTTGCAGA +AGTTGAATCTCAAACTGAAGTAACGGAATAACAAGTATATAACAGAGGCTAATGCTTTAGCCTCTTGTTATTTTTATGTA +AATTATTTGATTTAATGTTGACGAATTCTCTTGTTCAATGTTAATATATTAAAGGTTGATGCAAGCAGAACTTTGGAGGA +TAAATTATTGTCTAAGGAAAAAGTTGCACGCTTTAACAAACAACATTTTGTAGTTGGTCTTAAAGAAACGCTTAAAGCGT +TAAAGAAAGATCAAGTTACATCTTTGATTATTGCTGAAGACGTTGAAGTATATTTAATGACTCGCGTGTTAAGCCAAATC +AATCAGAAAAATATACCTGTATCTTTTTTCAAAAGCAAACATGCTTTGGGTAAACATGTAGGTATTAACGTCAATGCGAC +AATAGTAGCATTGATTAAATGAGAATTAGTAAGTGTTTTACTTACTAAATTTTATTTAACCTAAAAATGAACCACCTGGA +TGTGTGGGATTAAAAAGTGAAGAGAGGAGGACATATCACATGCCAACTATTAACCAATTAGTACGTAAACCAAGACAAAG +CAAAATCAAAAAATCAGATTCTCCAGCTTTAAATAAAGGTTTCAACAGTAAAAAGAAAAAATTTACTGACTTAAACTCAC +CACAAAAACGTGGTGTATGTACTCGTGTAGGTACAATGACACCTAAAAAACCTAACTCAGCGTTACGTAAATATGCACGT +GTGCGTTTATCAAACAACATCGAAATTAACGCATACATCCCTGGTATCGGACATAACTTACAAGAACACAGTGTTGTACT +TGTACGTGGTGGACGTGTAAAAGACTTACCAGGTGTGCGTTACCATATTGTACGTGGAGCACTTGATACTTCAGGTGTTG +ACGGACGTAGACAAGGTCGTTCATTATACGGAACTAAGAAACCTAAAAACTAAGAATTTAGTTTTTAATTAAATCTTAAA +CTTAAAATATTTAATATAAGGAAGGGAGGATTTACATTATGCCTCGTAAAGGATCAGTACCTAAAAGAGACGTATTACCA +GATCCAATTCATAACTCTAAGTTAGTAACTAAATTAATTAACAAAATTATGTTAGATGGTAAACGTGGAACAGCACAAAG +AATTCTTTATTCAGCATTCGACCTAGTTGAACAACGCAGTGGTCGTGATGCATTAGAAGTATTCGAAGAAGCAATCAACA +ACATTATGCCAGTATTAGAAGTTAAAGCTCGTCGCGTAGGTGGTTCTAACTATCAAGTACCAGTAGAAGTTCGTCCAGAG +CGTCGTACTACTTTAGGTTTACGTTGGTTAGTTAACTATGCACGTCTTCGTGGTGAAAAAACGATGGAAGATCGTTTAGC +TAACGAAATTTTAGATGCAGCAAATAATACAGGTGGTGCCGTTAAGAAACGTGAGGACACTCACAAAATGGCTGAAGCAA +ACAAAGCATTTGCTCACTACCGTTGGTAAGATAAAAGCTTTTACCCTGAGTGTGTTCTATATTAATGAATTTTCATTAAG +CGTTCATGCTTAGGGCATCGCCATATCTATCGTATTTATTCAGTAATATAAACTGGAAGGAGAAAAAATACATGGCTAGA +GAATTTTCATTAGAAAAAACTCGTAATATCGGTATCATGGCTCACATTGATGCTGGTAAAACGACTACGACTGAACGTAT +TCTTTATTACACTGGCCGTATCCACAAAATTGGTGAAACACACGAAGGTGCTTCACAAATGGACTGGATGGAGCAAGAAC +AAGACCGTGGTATTACTATCACATCTGCTGCAACAACAGCAGCTTGGGAAGGTCACCGTGTAAACATTATCGATACACCT +GGACACGTAGACTTCACTGTAGAAGTTGAACGTTCATTACGTGTACTTGACGGAGCAGTTACAGTACTTGATGCACAATC +AGGTGTTGAACCTCAAACTGAAACAGTTTGGCGTCAGGCTACAACTTATGGTGTTCCACGTATCGTATTTGTAAACAAAA +TGGACAAATTAGGTGCTAACTTCGAATACTCTGTAAGTACATTACATGATCGTTTACAAGCTAACGCTGCTCCAATCCAA +TTACCAATTGGTGCGGAAGACGAATTCGAAGCAATCATTGACTTAGTTGAAATGAAATGTTTCAAATATACAAATGATTT +AGGTACTGAAATTGAAGAAATTGAAATTCCTGAAGACCACTTAGATAGAGCTGAAGAAGCTCGTGCTAGCTTAATCGAAG +CAGTTGCAGAAACTAGCGACGAATTAATGGAAAAATATCTTGGTGACGAAGAAATTTCAGTTTCTGAATTAAAAGAAGCT +ATCCGCCAAGCTACTACTAACGTAGAATTCTACCCAGTACTTTGTGGTACAGCTTTCAAAAACAAAGGTGTTCAATTAAT +GCTTGACGCTGTAATTGATTACTTACCTTCACCACTAGACGTTAAACCAATTATTGGTCACCGTGCTAGCAACCCTGAAG +AAGAAGTAATCGCGAAAGCAGACGATTCAGCTGAATTCGCTGCATTAGCGTTCAAAGTTATGACTGACCCTTATGTTGGT +AAATTAACATTCTTCCGTGTGTATTCAGGTACAATGACATCTGGTTCATACGTTAAGAACTCTACTAAAGGTAAACGTGA +ACGTGTAGGTCGTTTATTACAAATGCACGCTAACTCACGTCAAGAAATCGATACTGTATACTCTGGAGATATCGCTGCTG +CGGTAGGTCTTAAAGATACAGGTACTGGTGATACTTTATGTGGTGAGAAAAATGACATTATCTTGGAATCAATGGAATTC +CCAGAGCCAGTTATTCACTTATCAGTAGAGCCAAAATCTAAAGCTGACCAAGATAAAATGACTCAAGCTTTAGTTAAATT +ACAAGAAGAAGACCCAACATTCCATGCACACACTGACGAAGAAACTGGACAAGTTATCATCGGTGGTATGGGTGAGCTTC +ACTTAGACATCTTAGTAGACCGTATGAAGAAAGAATTCAACGTTGAATGTAACGTAGGTGCTCCAATGGTTTCATATCGT +GAAACATTCAAATCATCTGCACAAGTTCAAGGTAAATTCTCTCGTCAATCTGGTGGTCGTGGTCAATACGGTGATGTTCA +CATTGAATTCACACCAAACGAAACAGGCGCAGGTTTCGAATTCGAAAACGCTATCGTTGGTGGTGTAGTTCCTCGTGAAT +ACATTCCATCAGTAGAAGCTGGTCTTAAAGATGCTATGGAAAATGGTGTTTTAGCAGGTTATCCTTTAATTGATGTTAAA +GCTAAATTATATGATGGTTCATACCATGATGTCGATTCATCTGAAATGGCCTTCAAAATTGCTGCATCATTAGCACTTAA +AGAAGCTGCTAAAAAATGTGATCCTGTAATCTTAGAACCAATGATGAAAGTAACTATTGAAATGCCTGAAGAGTACATGG +GTGATATCATGGGTGACGTAACATCTCGTCGTGGACGTGTTGATGGTATGGAACCTCGTGGTAATGCACAAGTTGTTAAT +GCTTATGTACCACTTTCAGAAATGTTCGGTTATGCAACATCATTACGTTCAAACACTCAAGGTCGCGGTACTTACACTAT +GTACTTCGATCACTATGCTGAAGTTCCAAAATCAATCGCTGAAGATATTATCAAGAAAAATAAAGGTGAATAATATAACT +TGTTTTGACTAGCTAGCCTAGGTTAAAATACAAGGTGAGCTTAAATGTAAGCTATCATCTTTATAGTTTGATTTTTTGGG +GTGAATGCATTATAAAAGAATTGTAAAATTCTTTTTGCATCGCTATAAATAATTTCTCATGATGGTGAGAAACTATCATG +AGAGATAAATTTAAATATTATTTTTAATTAGAATAGGAGAGATTTTATAATGGCAAAAGAAAAATTCGATCGTTCTAAAG +AACATGCCAATATCGGTACTATCGGTCACGTTGACCATGGTAAAACAACATTAACAGCAGCAATCGCTACTGTATTAGCA +AAAAATGGTGACTCAGTTGCACAATCATATGACATGATTGACAACGCTCCAGAAGAAAAAGAACGTGGTATCACAATCAA +TACTTCTCACATTGAGTACCAAACTGACAAACGTCACTACGCTCACGTTGACTGCCCAGGACACGCTGACTACGTTAAAA +ACATGATCACTGGTGCTGCTCAAATGGACGGCGGTATCTTAGTAGTATCTGCTGCTGACGGTCCAATGCCACAAACTCGT +GAACACATTCTTTTATCACGTAACGTTGGTGTACCAGCATTAGTAGTATTCTTAAACAAAGTTGACATGGTTGACGATGA +AGAATTATTAGAATTAGTAGAAATGGAAGTTCGTGACTTATTAAGCGAATATGACTTCCCAGGTGACGATGTACCTGTAA +TCGCTGGTTCAGCATTAAAAGCTTTAGAAGGCGATGCTCAATACGAAGAAAAAATCTTAGAATTAATGGAAGCTGTAGAT +ACTTACATTCCAACTCCAGAACGTGATTCTGACAAACCATTCATGATGCCAGTTGAGGACGTATTCTCAATCACTGGTCG +TGGTACTGTTGCTACAGGCCGTGTTGAACGTGGTCAAATCAAAGTTGGTGAAGAAGTTGAAATCATCGGTTTACATGACA +CATCTAAAACAACTGTTACAGGTGTTGAAATGTTCCGTAAATTATTAGACTACGCTGAAGCTGGTGACAACATTGGTGCA +TTATTACGTGGTGTTGCTCGTGAAGACGTACAACGTGGTCAAGTATTAGCTGCTCCTGGTTCAATTACACCACATACTGA +ATTCAAAGCAGAAGTATACGTATTATCAAAAGACGAAGGTGGACGTCACACTCCATTCTTCTCAAACTATCGTCCACAAT +TCTATTTCCGTACTACTGACGTAACTGGTGTTGTTCACTTACCAGAAGGTACTGAAATGGTAATGCCTGGTGATAACGTT +GAAATGACAGTAGAATTAATCGCTCCAATCGCGATTGAAGACGGTACTCGTTTCTCAATCCGTGAAGGTGGACGTACTGT +AGGATCAGGCGTTGTTACTGAAATCATTAAATAATTTCTAATTTCTTAGATTTTATATAAAAAGAAGATCCCTCAATCGA +GGGGTCTTTTTTTAATGTGTAAATTTTGTAATGGCTATTCGATTTAGAAGAACAATAATTGATGAAAGACTGACTAATAA +AACTTATAACTGATAATACTGTTTAAATAAAATTGTTGAGTCTTGGACATTGTAAAATGCTCCCTTCAAAGTTTTCATTT +TTTCAATGTCTACTTTGAAGGGAGCATTTCATTAGTTTATGTCTCAGATTCATATCTTTCAATTAATTTAAATGCTTAAT +TTGTTTTAAATACTTGCTCTAATTCTATGATTTTTAAAAATACAGCTACAGCGTATTTTAATGATTTTTCATCAATATCA +AATTTGGGATTATGGTGTGGCGCTGTAATACCTTTACTTTCATTACCACAACCAGTCAGAAAGAATGCACCTGGTCGTAC +TTTCAAATAATGTGAAAAATCTTCTCCAATCATCATTAAATCTGATTCATTAAAGCGTACATGTAAGTCATTTGTTGCTT +CTTTAATAACTTGATATGCTTTCTCGTTATTATGGACAGGCAAATACCCTTTAATATAATTCAAATCATAGTTAATATCA +TTTGCTATTGCTAAACCTTGTAGAAGCTTATCCATTTTGTCCATTACATGATTCTGTATATCTGAATCGAAAGTTCTAAC +TGTACCTTTACAAAATGCTTGATCAGGAATAACGCTATCTGTGGTGCCTGCTTGAATCATTCCAAATGAAAGTACAGCTT +GTTTAACTGGATCGATCGTACGTGAAATTATTTTTTGTGCACTTAAAATGAACTCTGCCATGATTACTATTGGGTCAATG +GTTTCATGAGGTTTGGCACCATGACCACCACGACCTTTAAATGTGACGCTAAATTCATCTGGAGAGGCCATGATTGCCCC +CGCACGTGAATGAATAGTTCCAGTAGGATAACCACTCCATAAATGTGTACCGTAAATTCTATCTACATTTTCCAGACATC +CAGCATCTATCATTTCTTGAGAACCACCTGGCATGATTTCTTCACCGTACTGGAATATTAATACAACATTACCTTCTAAT +AAATGTTTATGTTCATCTAAAATCTCTGCTACAGTAAGTAAAATTGCTGTATGACCATCATGCCCACACGCATGCATACA +TCCTGGATTTTTAGACTTATAAGGCACATCGTTTAATTCCTCGACAGGTAACGCATCAAAGTCAGCTCTTAATGCAATGG +TAGGTCCTGTGCCCAAGCCTTTAAATGTGGCTTTGATACCATTGCGGCCGATAGGAGTTTCAATATCACAAGATAACTGG +CTTAATTGGTTAACAATATAATCATGTGTTTGAAATTCTTCAAAAGATAACTCAGGATATTGGTGTAAATAACGTCTGAG +TTGAATTGTTTTATTTTCTTTATTATTTGCTAGTTGGAACCAATCTAACACCCTTATCACTACTTTCTAAAATAATGTTT +ATAGTATAACATTTTATGAAATTATCGTACTAAATGATTGCTTTGAGATATTTTATCTATGAATGATAAGGCTTTCAAGT +TATGTAGAATTACTGTATGATAAAGGTATTACCAAACAATACTTAAGGGGGATTATATACTGTGGTTCAATCATTACATG +AGTTTTTAGAGGAAAATATAAATTATCTAAAAGAAAATGGTTTGTATAATGAAATAGATACAATTGAAGGTGCAAACGGA +CCAGAAATCAAAATCAATGGGAAATCATACATTAACTTATCTTCAAATAATTATTTAGGACTAGCAACAAATGAAGATTT +GAAATCAGCTGCAAAAGCAGCTATTGATACACATGGTGTAGGTGCAGGCGCTGTTCGTACAATCAATGGTACATTAGATT +TACACGACGAATTAGAAGAAACACTAGCAAAATTTAAAGGAACAGAAGCTGCAATAGCTTATCAATCAGGATTTAATTGT +AATATGGCTGCTATTTCAGCTGTCATGAATAAAAATGATGCTATTTTATCAGATGAGCTTAATCATGCATCAATTATTGA +TGGATGTCGCTTATCTAAAGCTAAAATTATTCGAGTTAACCATTCAGACATGGATGATTTACGTGCGAAAGCAAAAGAAG +CAGTTGAATCAGGTCAATACAATAAAGTGATGTATATCACTGATGGCGTTTTTAGTATGGATGGTGATGTGGCTAAATTA +CCTGAAATTGTAGAAATTGCAGAAGAATTTGGTTTATTAACTTATGTTGACGACGCTCATGGTTCAGGTGTTATGGGTAA +AGGCGCTGGTACGGTTAAACATTTTGGTTTACAAGATAAAATCGATTTCCAAATAGGTACGCTTTCTAAAGCAATTGGTG +TCGTTGGCGGTTATGTAGCAGGTACAAAAGAGTTAATAGATTGGTTAAAAGCACAATCACGACCATTCTTATTCTCTACA +TCATTAGCACCTGGGGATACCAAAGCAATAACTGAAGCAGTTAAAAAGTTAATGGATTCAACTGAATTACATGATAAATT +ATGGAACAATGCACAATATTTAAAAAATGGATTGTCAAAATTAGGATATGATACAGGTGAGTCAGAAACTCCAATTACAC +CAGTAATTATTGGTGATGAAAAAACAACTCAAGAATTTAGTAAGCGTTTAAAAGACGAAGGTGTCTATGTGAAATCTATC +GTTTTCCCAACAGTACCAAGAGGTACAGGACGTGTAAGAAATATGCCTACAGCTGCACATACAAAAGACATGTTAGATGA +AGCAATTGCGGCTTATGAAAAAGTAGGAAAAGAAATGAAGTTGATTTAATATTTATTTATTCCCACGGCAAATATTGTCG +TGGGCTTTTTTTAATGTTTAGTTTATTAACAGTAAGTTCGTATATCAATGTTTAGTGCTCCCCAAAATTGAAGTTTGAAT +TTTAAAAGCATCTTGTAGAATTTAGTTGTATTTTTTTCAAAGAAATTCATTTTGATTATTTTTGATAATGAGCATTTTAA +TAGTAATACATGTTTATAGTGTGTAGTATATGTCTATACTAGTAGTAACTATATAGAGAAAGTAGGAATAAACTATGTCA +CAAGATGTAAATGAATTAAGTAAGCAACCAACGCCAGATAAAGCAGAAGATAACGCATTTTTCCCATCACCATATTCCCT +TAGTCAATATACAGCACCTAAAACAGATTTTGATGGTGTTGAACACAAAGGTGCCTATAAAGATGGTAAATGGAAAGTAT +TGATGATTGCTGCTGAAGAGCGATATGTATTATTGGAAAATGGAAAAATGTTCTCTACGGGTAATCATCCTGTTGAAATG +TTATTACCTTTACATCATTTAATGGAAGCAGGTTTTGACGTTGATGTTGCGACATTATCTGGTTATCCAGTTAAATTAGA +ATTATGGGCTATGCCAACTGAAGACGAGGCAGTTATAAGTACTTATAATAAATTGAAAGAAAAATTAAAACAGCCAAAAA +AATTAGCAGATGTGATTAAAAATGAATTAGGACCTGATTCAGACTATTTATCTGTCTTTATCCCAGGCGGACATGCTGCA +GTTGTTGGTATTTCTGAAAGTGAGGACGTTCAACAAACATTAGATTGGGCATTAGACAATGACCGCTTTATAGTTACATT +ATGTCATGGACCAGCAGCACTACTTTCAGCAGGGCTTAACAGAGAAAAATCTCCATTAGAAGGATACTCTGTTTGTGTCT +TCCCTGACTCATTAGATGAAGGTGCAAATATTGAAATAGGTTATTTACCTGGACGCTTGAAATGGTTAGTTGCTGATTTA +TTAACTAAACAAGGATTAAAAGTAGTTAACGACGATATGACAGGAAGAACGTTAAAAGATCGTAAATTATTAACAGGTGA +CAGTCCTTTAGCTTCAAATGAGTTAGGAAAATTAGCAGTTAATGAAATGTTAAATGCAATACAAAATAAATAATTAAATA +TTAATTAGAGGAGCCTCATATGTAAATGTATGAGGGCTCTTTTTTTTGGCAAAATTTAAGTGATACTTGTAAAATAGAAC +CTATTATGAGTATGATTTAAGAAAACGCTTGCAAAACTAATAACCGCAACTAGCGATATGGAGGAAACATGATGTCTTAT +AGCATTGGAATTGATTATGGAACTGCTTCAGGCCGTGTGTTTTTAATTAATACAACTAACGGTCAAGTAGTATCAAAATT +TGTGAAACCATATACACATGGTGTCATTGAGAGTGAATTAAATGGTTTGAAAATACCACATACATATGCACTTCAAAATA +GTAATGATTATTTAGAAATTATGGAAGAAGGAATATCATATATAGTACGTGAATCAAAAATAGATCCAGACAATATAGTA +GGTATTGGTATAGACTTTACTTCATCTACTATTATTTTTACTGACGAAAACCTTAACCCGGTACATAACTTAAAACAATT +TAAAAACAATCCACATGCGTATGTGAAACTTTGGAAACATCATGGTGCATATAAAGAGGCAGAGAAATTATATCAAACTG +CTATTGAAAATAATAATAAGTGGTTAGGCCATTATGGATATAATGTTAGTAGTGAATGGATGATTCCCAAAATAATGGAG +GTCATGAATCGAGCACCAGAAATTATGGAAAAAACGGCTTATATTATGGAAGCGGGCGATTGGATTGTAAATAAATTAAC +TAATAAAAATGTACGCTCGAATTGTGGATTAGGTTTCAAAGCATTTTGGGAAGAAGAAACAGGGTTTCATTATGATTTAT +TTGATAAAATAGACCCCAAATTATCAAAAGTAATTCAAGATAAAGTATCTGCACCGGTTGTTAATATTGGTGAAGCAGTA +GGGAAACTGGATGATAAAATGGCACAGAAATTAGGATTATCAAAAGAAACTATGGTAAGTCCTTTTATTATTGATGCCCA +TGCTAGTTTATTAGGTATTGGGTCTGAAAAAGATAAAGAAATGACTATGGTGATGGGAACAAGCACATGCCATCTTATGT +TAAATGAAAAGCAACATCAAGTGCCAGGTATATCAGGTTCTGTAAAAGGAGCAATTATTCCAGAATTATTTGCTTATGAA +GCGGGGCAATCAGCAGTAGGTGATTTGTTTGAGTATGTCGCTAAGCAAGCACCAAAGTCATATGTAGATGAAGCAGAAAA +TAGAAATATGACTGTATTTGAATTAATGAATGAAAAGATAAAACATCAAATGCCAGGTGAAAGTGGGCTCATTGCTCTTG +ATTGGCATAATGGAAATCGAAGTGTATTAAGTGATAGCAATTTAACAGGTTGTATCTTTGGATTAACTTTACAAACTAAG +CATGAGGATATTTATAGAGCATATTTAGAAGCTACAGCATTTGGTACTAAGATGATTATGCAACAGTATCAAGATTGGCA +TATGGAAGTAGAAAAGGTATTTGCATGTGGCGGTATACCTAAAAAGAATGCTGTTATGATGGATATCTATGCGAATGTAC +TGAATAAAAAACTAATTGTTATGGATAGTGAGTATGCACCAGCAATAGGCGCAGCAATATTAGGTGCAGTCAGTGGTGGC +GCACATAATTCAATTAATGACGCAGTTGATGCTATGAAAGAGCCAATTTTATACGAAATTAATCCAGAAGCGGAAAAAGT +ACAAAGGTATGAAACATTATTTAAAGCTTATAAGGCTTTACATGATATCCATGGTTATAAAAAAGCTAATATAATGAAAG +ATATCCAGAGTTTAAGAGTTGAGGGATAAAAAATTTAATTTGACAGTAATTAGCAATAATAAACGCTATAAATTACTAAA +ATCAATTAATACAACTAGAATTTCCATTTACAGAAATATCAGCAACATGTACATGTCATATATACAAGAAAGCGCTTTTA +TATTATAATTATTATGAAAATGAATATTATGTCCGCTTGTGATGGACTATAGCTATATAGAAGAGACAAAGGAGATATTT +CTATGAAAAAAATTATGATTACTGGTGCATTAGGACAAATTGGTACAGAATTAGTTGTTAAGTGCAGAGAAATTTATGGG +ACAGATAATGTTCTTGCTACAGATATTAGGGAACCTGAAGCAGACTCACCTGTACAAAATGGACCATTTGAAATCTTAGA +CGTAACAGATCGTGACCGTATGTTTGAGTTAGTTAGGGACTTTGAAGCGGATAGTCTAATGCATATGGCAGCATTATTAT +CAGCAACTGCTGAGAAAAATCCAATTCTAGCTTGGGATTTAAATATGGGTGGATTAATGAATGCATTAGAAGCTGCAAGA +ACTTATAATTTGCACTTTTTCACACCAAGTTCAATTGGTGCATTTGGAGACTCAACTCCTAAAGTTAATACGCCACAAGT +AACGATTCAGCAACCTACGACAATGTATGGTGTAAATAAAGTAGCTGGAGAATTATTGTGTCAATACTATTTCAAACGTT +TTGGTGTAGATACAAGAAGTGTTAGATTCCCAGGTTTAATCTCGCATGTTAAAGAGCCAGGTGGCGGTACTACAGACTAT +GCTGTTGAAATATACTTCAAAGCAGTAAGAGAGGGTCATTATACAAGCTTCATAGATAAAGGCACGTATATGGATATGAT +GTATATGGATGATGCAATTGAAGCAATTATTAAACTTATGGAAGCAGACGACGCTAAATTAGAAACTAGAAATGGTTATA +ATTTGAGCGCAATGAGTTTTGATCCAGAGATGGTAAAAGAAGCAATTCAAGAATACTATCCCAATTTTACATTAGATTAC +GATGTTGATCCTATTAGACAAGGTATCGCTAATAGTTGGCCGGATTCTATTGATACAAGCTGTTCACGTGGCGAATGGGG +ATTTGATCCTAAATATGATTTAGCGAGCATGACTAAATTAATGTTAGAAGCTATTGAACAAAAAGATACTGTTAAAAATA +ATAACTAATCATTTCCATTCACTTTAATACACGGAATGATATTTTAAATTACTCTTTATTTTAATAAACTAGTGCATGAA +TTCTAATATTATTCATTATACTTATTGAATTCGCGAGCTTAGTTCATTTTAAAGTAAAGAGTTTTTTGTATGTAAAGTAT +ATTAGTAAAACACAGATTACTTGTCTCTGTGAAATTAATACGTTTATACTAAAAGATATAGTCTTTCTAGAAATCTGTTA +ATTAATTTTGAATTTTTAGAAAATTTGTTGAACAGCAAAATATGGATTGTTATAATTTAAGTTAAACAAATTCTTATACA +ATATTATTAGGAGGCAATCACCATGTCACAAGCAGTTAAAGTTGAACGACGAGAAACATTAAAACAAAAACCAAATACAT +CTCAACTAGGTTTTGGTAAATATTTTACTGATTATATGTTGAGTTATGATTATGATGCAGATAAAGGATGGCATGATTTG +AAGATAGTACCTTATGGTCCTATTGAAATTTCACCTGCTGCACAAGGTGTTCATTATGGTCAATCGGTATTCGAAGGATT +AAAAGCATATAAAAGAGATGGGGAAGTTGCACTTTTCCGTCCTGAAGAAAATTTTAAGCGTCTTAATAACTCGTTAGCAC +GATTAGAAATGCCTCAAGTAGACGAAGCAGAATTGTTAGAGGGGCTAAAACAATTAGTTGATATTGAAAGAGATTGGATT +CCTGAAGGGGAAGGTCAATCATTATATATTCGTCCATTTGTTTTTGCAACAGAAGGGGCACTTGGCGTTGGTGCATCACA +TCAGTATAAATTATTAATTATTTTATCTCCTTCAGGTGCATATTATGGTGGTGAAACTTTAAAACCAACTAAAATCTATG +TAGAAGATGAATATGTGCGTGCTGTTCGTGGCGGTGTAGGCTTTGCAAAAGTTGCAGGTAACTATGCGGCAAGTTTATTA +GCACAAACTAATGCAAATAAATTAGGTTATGACCAAGTATTATGGCTTGATGGTGTTGAACAGAAATATATCGAAGAAGT +TGGTAGCATGAACATTTTCTTCGTTGAAAATGGCAAAGTAATTACACCAGAGTTGAATGGCAGTATTTTACCTGGTATTA +CACGTAAATCTATTATCGAATTAGCTAAAAACTTAGGATATGAAGTCGAAGAGCGCCGCGTTTCAATCGATGAATTATTC +GAATCATATGATAAAGGTGAGTTAACAGAAGTATTTGGTAGTGGTACTGCAGCAGTTATTTCACCTGTGGGTACATTGAG +ATACGAAGATCGTGAAATCGTTATTAATAATAATGAGACTGGTGAAATTACTCAAAAATTATACGACGTCTATACTGGTA +TTCAAAATGGTACTTTAGAAGATAAAAATGGTTGGAGAGTCGTTGTACCAAAATATTAATAAAAATTGAATATAATCATG +AAAACAATATGCAAAAGTTAATCATGACTAAAATCCCTTTCAATGAAGCGAAGCATGGTAATAAATTAAGTTTACAATGT +TTATTGTTAAGTATTGAAGGGGATTTCACTTATTATTATATTTAATTCAATATTTAAATAGAACAATACTACGAGTTCAT +TTTCAGGGGATAACGAATATCATAGAATCAGCATAATACGAAATAAATAAATAGGAGTATTGATATCAATGGAATGGATA +TTATTTGATAAAGATGGTACGTTAATTGAATTTGATAGAAGTTGGGAAAAAATAGGGGTACGATTTGTACAATCATTGCT +TGAGACTTTCCCAGTACATAATAAAGAAGCTGCTTTAAGACAACTCGGTGTCATTAAAGAATCTATTGATCCAAAATCAG +TGATGGGTTCAGGATCTTTACAACAAATTATCCAGGCATTTAATGATGTGACGGGACAAGATACAACCGACTGGTCCAAG +TCAACAAGTCAAAAGCTGGTAGATGAACGTATTCCTGAAATTAATTGGGTAGAAGGTGTTAAAGAAGCACTTATCGATTT +GAAAGCAAAAGGCTATCAACTTGGTATTGTTACGAGTGATACTAAAAAAGGTGTAGAACAATTTTTAGCACATACCAATG +CTACCTCGTTGTTCGATTTGATCATTTCTACCGAAGCGGATGCCTATGAGAAGCCAAATCCTAAAGTATTATCGCCTTTA +TTTGAGCAATATAATGTAGATCCTCAGAAAGTAGCTATAGTAGGAGACACTGCTAATGATATGAAGACAGCAAGTAATGC +AAATTTAGGTATGGCAATAGGTGTATTAACAGGTATTGCAACAAAAGAAGAATTACATGAAGCTGATATTATTTTAAATA +GTGCGGCAGATATTTTAGAAGCTTTAAATTAAAAAGAAAGACATAGTGTATATGTGAATTAATGTATTCGATAGAATTGA +TAAAATAATCGATAATATCTATGGAATATACATTGAATATAAACATATACGCTATGTCTTTAGTCTTTTATCGTGTATCT +ACTTGTCGATATGTTTGAATAATTCGAGCAATTTTGTTTATCATAGGATTTAAAGATTCGGGGTCCTTATGGATATCATA +TTCATTAATATTGATACGTACAACTGGACATGCATTAAAGCTATTAATCCAATCGTCATAGCGTTTAAATAGCTTTTTCC +AGTATTCAGGGTCTGTATTAATTTCCATTTCGCGACCACGTTCAATAATACGATCAATGACCTCATCATAGTTACATTCT +AAATAAATCATTACATCAGGTTTAGGAAAATAAGGTGTCATGACCATGGCATTAAATAAGTCTGAATATGTTTTGAAATC +TTCTTTACTCATTGTGCCTTCTTCTTCATGCATTTTTGCAAAAATATCAACATCTTCATAAATTGATCGATCTTGGACAA +AGCCACCACCATATTCAAACATACGCTTTTGTTCTTTAAAACGTTCAGCTAAGAAGTAAATTTGCAAATGGAAACTCCAT +CGTTCAAAATCGCTGTAAAATTTATCTAAATATGGATTATGTTCGACATTTTCAAAAGACGTTTTAAAGTTTAATTTATC +TGCAAGTGCTTGCGTTAGTGTTGATTTTCCAACACCAACTGTACCTGCAATGGTTATAATGGCATTTTGTGGAATACCGT +AATTATTCATTGGTAATATCTCCTATCATAGGTAATATAATATGTAATATATCTTCGTAATCTTGTTCATTTTTAAGAAA +ATCAATAGAAGTTGTATCGATTAAAACTACATTTGAACCATTACTTTGTAAGGACTCATAATACTCACGATAATCTTTTT +TTAACTTTAACAGATATTCATCTTCTATTTGATGCTCAAAACTACGGTTACGTTTAGCAATTCTAGATTTTAACACATCA +AGGTCTGCATCTAAAAAGATAATCATATTCGGCATAATCATATCTTCAGTTAAAATATCATAAATTTTACTGAATTTCTG +AAATTCAACAGAACTCAAAGTATTTTTAGCAAATATCTTATTTTTATGTATATGATAATCACTAACTACACCTTGATTTA +GTTGTGTTACATCTTGAAATTGCTTATATCTATTGCATAAAAAGAACATTTCAGTTTGAAAACTCCATTTAGAGATATCT +TCATAAAAGTCTGATAAAAATGGATTTTCTGTGATGATTTCTTTTTCTTCATAAAAATCTAAAGTTTGACTTAATTTGTG +TGCAAGTGAAGATTTACCTACGCCAATAGGACCTTCAATTGCTATAAAAGGTTTGTTCATATTCACATCTCCAAATGTTA +AAATAGACATTGAGTATTGTACCATAAGTGAGGTATCTGGACATAAATGACAAATGATATATATTTTATGACATTAGCGA +TTGAAGAAGCTAAAAAAGCAGCTCAACTAGGCGAAGTACCTATAGGTGCTATCATCACTAAAGATGATGAAGTTATCGCT +AGAGCACATAATTTAAGAGAAACACTACAACAACCAACGGCGCATGCTGAACATATTGCAATTGAACGTGCAGCCAAAGT +GTTAGGTAGTTGGCGTTTAGAAGGTTGCACATTATATGTAACCTTAGAACCATGTGTCATGTGCGCAGGAACAATTGTAA +TGAGTCGCATTCCAAGAGTCGTCTATGGCGCAGATGATCCTAAAGGTGGTTGTAGTGGCAGTTTAATGAATTTATTGCAA +CAATCTAATTTTAATCATCGTGCAATTGTTGATAAAGGTGTACTTAAAGAAGCATGTAGCACATTATTAACAACATTTTT +TAAAAACTTAAGAGCCAATAAGAAATCCACCAATTAGACGATAGTACATACTAATTAAAATTTGTTAGAATTACATTATA +TGATAAATTAATGACAATATAAATGTTAATAGAAAAGTATGTGCGCTGAGTATATATTCTTATTTAAGAACAAGATTTTA +AATTTACGAAACGAGGTAACATAATGATAAAACTAATAGCCACTGATATGGATGGCACGCTACTTAATGCAGCACATGAA +ATTTCTCAACCTAATATTGATGCGATTAAATACGCTCAAGAACAAGGGATAACGGTTGTTATCGCGACAGGTCGAGCATT +TTATGAAGCACAAGCACCAGTTGCTGACACAGATTTAACAGTACCATATATTTGTTTGAATGGTGCTGAAGTACGTGATG +AAACTTTCAATGTAATGAGCACTTCACACCTTAATAAATCGTTAGTACACAAAATTACAAATGTTTTAAAAGATGCAGGT +ATTTATTATCAAGTATACACGAGTCGTGCGATTTATACTGAAGATCCACAAAGAGATTTAGACATTTACATAGATATTGC +TGAGCGTGCAGGTCAACATGCAAACGTTGAGCGTATTAAAAATGGTATTCAAAGACGCATAGATAATGGTACGTTGAAAG +TTGTTGATAATTATGATGCTATTGAAAACATACCTGGTGAATTAATTATGAAAATATTAGCATTTGATGGAAATTTAGAA +AAAATTGACAAAGCTAGTAAAATTTTAGCTGAATCTCCGAATTTAGCTATATCATCATCTTCGAGAGGAAATATAGAAAT +AACGCATTCAGATGCACAAAAAGGTATTGCGCTAGAAACAATTGCCGAAAGATTAGGGATTGAAATGAAAGAAGTCATGT +CAATAGGTGACAATTTAAATGACTTATCAATGTTAGAGAAAGTTGGCTATCCAGTTGCGATGGAAAATGGTGCAGAAGAA +GTTAAAAAAATAGCGAAATATGTCACAGATACGAATGAAAATAGTGGTGTTGGAAAAGCTATTATGAAATTATTACGTGA +ACAACAAGTTTAATAAAAAAAGAGGGGTCAAATATGAAAGGATTAATTATTATTGGCAGTGCACAAGTGAATTCACATAC +AAGTGCACTAGCAAGATACTTAACTGAGCATTTTAAAACACATGATATTGAAGCGGAAATATTCGATTTAGCAGAAAAAC +CGTTAAATCAATTAGATTTTTCAGGAACAACACCGTCTATTGATGAAATCAAACAAAATATGAAAGATTTAAAAGAGAAA +GCAATGGCGGCGGACTTTTTAATATTAGGAACGCCAAACTATCATGGTTCATATTCTGGAATATTGAAAAATGCATTAGA +TCATCTAAATATGGATTATTTTAAAATGAAACCTGTAGGCTTAATAGGAAATAGTGGTGGTATTGTTAGTTCAGAGCCAT +TGTCACATTTAAGAGTAATCGTCAGAAGTTTACTAGGCATTGCTGTACCAACTCAAATAGCAACACATGATTCTGATTTT +GCTAAAAATGAAGATGGTTCATATTACTTAAATGATAGTGAATTCCAATTACGAGCAAGATTATTTGTCGATCAAATTGT +ATCTTTTGTGAATAATAGTCCATATGAACATTTAAAATAATATTAAAAAATATGTAAATATACATTAAAAATAGGTATGT +AGCTTAAGGTATTTCATTGAGAATGCTTTTAAGTCACATGCCTATTTTTTGTTCTAATGCACCAATAGTAAGCCTTTGAT +ATTACTGTTGTGAGCAATGTTATTAATTAAAATAAGATGTAATAGCGAATTGAAAACAAGCTTAGATTAAAAGTGAGTTT +TAAAAATATAGATAAATAATTTAAAGCAATTAAAGAAAAAAAGTATTAAAAATTGCAATTCTTAAACTGTTATTCATATT +AATATTTCTAGCAAATAACATTGTAAAATAAAGAAAAATAATTAAAGTATTGCACTTTATTGAAATTTATATTACGATAG +TAATGCAGAAATTTATATATGCAAAATATTATATTTATCAAATTTTGATATTTTAAAGGAGTATTATTAAATGAATAATA +AAAAGACAGCAACAAATAGAAAAGGCATGATACCAAATCGATTAAACAAATTTTCGATAAGAAAGTATTCTGTAGGTACT +GCTTCAATTTTAGTAGGGACAACATTGATTTTTGGGTTAAGTGGTCATGAAGCTAAAGCGGCAGAACATACGAATGGAGA +ATTAAATCAATCAAAAAATGAAACGACAGCCCCAAGTGAGAATAAAACAACTAAAAAAGTTGATAGTCGTCAACTAAAAG +ACAATACGCAAACTGCAACTGCAGATCAGCCTAAAGTGACAATGAGTGATAGTGCAACAGTTAAAGAAACTAGTAGTAAC +ATGCAATCACCACAAAACGCTACAGCTAATCAATCTACTACAAAAACTAGCAATGTAACAACAAATGATAAATCATCAAC +TACATATAGTAATGAAACTGATAAAAGTAATTTAACACAAGCAAAAGATGTTTCAACTACACCTAAAACAACGACTATTA +AACCAAGAACTTTAAATCGCATGGCAGTGAATACTGTTGCAGCTCCACAACAAGGAACAAATGTTAATGATAAAGTACAT +TTTTCAAATATTGACATTGCGATTGATAAAGGACATGTTAATCAGACTACTGGTAAAACTGAATTTTGGGCAACTTCAAG +TGATGTTTTAAAATTAAAAGCAAATTACACAATCGATGATTCTGTTAAAGAGGGCGATACATTTACTTTTAAATATGGTC +AATATTTCCGTCCAGGATCAGTAAGATTACCTTCACAAACTCAAAATTTATATAATGCCCAAGGTAATATTATTGCAAAA +GGTATTTATGATAGTACAACAAACACAACAACATATACTTTTACGAACTATGTAGATCAATATACAAATGTTAGAGGTAG +CTTTGAACAAGTTGCATTTGCGAAACGTAAAAATGCAACAACTGATAAAACAGCTTATAAAATGGAAGTAACTTTAGGTA +ATGATACATATAGCGAAGAAATCATTGTCGATTATGGTAATAAAAAAGCACAACCGCTTATTTCAAGTACAAACTATATT +AACAATGAAGATTTATCGCGTAATATGACTGCATATGTAAATCAACCTAAAAATACATATACTAAACAAACGTTTGTTAC +TAATTTAACTGGATATAAATTTAATCCAAATGCAAAAAACTTCAAAATTTACGAAGTGACAGATCAAAATCAATTTGTGG +ATAGTTTCACCCCTGATACTTCAAAACTTAAAGATGTTACTGATCAATTCGATGTTATTTATAGTAATGATAATAAAACA +GCTACAGTCGATTTAATGAAAGGCCAAACAAGCAGCAATAAACAATACATCATTCAACAAGTTGCTTATCCAGATAATAG +TTCAACAGATAATGGAAAAATTGATTATACTTTAGACACTGACAAAACTAAATATAGTTGGTCAAATAGTTATTCAAATG +TGAATGGCTCATCAACTGCTAATGGCGACCAAAAGAAATATAATCTAGGTGACTATGTATGGGAAGATACAAATAAAGAT +GGTAAACAAGATGCCAATGAAAAAGGGATTAAAGGTGTTTATGTCATTCTTAAAGATAGTAACGGTAAAGAATTAGATCG +TACGACAACAGATGAAAATGGTAAATATCAGTTCACTGGTTTAAGCAATGGAACTTATAGTGTAGAGTTTTCAACACCAG +CCGGTTATACACCGACAACTGCAAATGTAGGTACAGATGATGCTGTAGATTCTGATGGACTAACTACAACAGGTGTCATT +AAAGACGCTGACAACATGACATTAGATAGTGGATTCTACAAAACACCAAAATATAGTTTAGGTGATTATGTTTGGTACGA +CAGTAATAAAGATGGTAAACAAGATTCGACTGAAAAAGGAATTAAAGGTGTTAAAGTTACTTTGCAAAACGAAAAAGGCG +AAGTAATTGGTACAACTGAAACAGATGAAAATGGTAAATACCGCTTTGATAATTTAGATAGTGGTAAATACAAAGTTATC +TTTGAAAAACCTGCTGGCTTAACTCAAACAGGTACAAATACAACTGAAGATGATAAAGATGCCGATGGTGGCGAAGTTGA +TGTAACAATTACGGATCATGATGATTTCACACTTGATAATGGCTACTACGAAGAAGAAACATCAGATAGCGACTCAGATT +CTGACAGCGATTCAGACTCAGATAGCGACTCAGATTCAGATAGCGACTCAGATTCAGACAGCGATTCAGACAGCGACTCA +GACTCAGATAGCGATTCAGATTCAGACAGCGACTCAGACTCAGACAGCGATTCAGACTCGGATAGCGACTCAGACTCAGA +TAGCGACTCAGATTCGGATAGCGACTCAGACTCAGATAGCGATTCAGATTCAGATAGCGATTCGGACTCAGACAGTGATT +CAGATTCAGACTCAGATAGCGACTCAGATTCTGACAGCGATTCAGACTCAGACAGCGACTCAGACTCAGACAGTGATTCA +GATTCAGACAGCGACTCAGATTCAGATAGCGACTCAGACTCAGATAGCGACTCAGATTCAGATAGCGATTCGGACTCAGA +CAACGACTCAGATTCAGATAGCGATTCAGATTCAGATAGCGACTCAGATTCGGACAGCGATTCAGACTCAGATAGCGATT +CAGACTCAGACAGCGATTCAGATTCAGATAGCGACTCAGACTCAGATAGCGACTCAGACTCGGATAGCGATTCAGATTCA +GACAGCGACTCAGATTCAGATAGCGATTCGGACTCAGACAACGACTCAGATTCAGATAGCGATTCAGATTCAGATGCAGG +TAAACATACTCCGGCTAAACCAATGAGTACGGTTAAAGATCAGCATAAAACAGCTAAAGCATTACCAGAAACAGGTAGTG +AAAATAATAATTCAAATAATGGCACATTATTCGGTGGATTATTCGCGGCATTAGGATCATTATTGTTATTCGGTCGTCGT +AAAAAACAAAATAAATAATATGATTAACTTAACCAGGTCCATGTGGCCTGGTTTTTTCTGTTTAGAAATTCCCAGATATA +AAACAAAAAATCTTTAAGGAATAGATACAATTTAATTTGTTACAGAAATGTAATTGTCTATGAATTAGAAAAGCTAAGAT +ATTAATTTCAAGGGCAAATAATACTATTTTTTATGATGATTAAATAAAGATTACAAAACTCATAAAAACTTTTATTTAAA +CACAAATGTTAAATAACGTTTTACGATAAAGAAAAATAATTAAAGTATTGTGCTTTATCTAAAAATGTAATACTATATTG +ATATACATTTTTGTATTTAAAACAATTGTATTATTTAAAAATTTGATGAATTAGGAGTAATCTAATGCTAAACAGAGAAA +ATAAAACGGCAATAACAAGGAAAGGCATGGTATCCAATCGATTAAATAAATTTTCGATTAGAAAGTACACAGTGGGAACA +GCATCAATTTTAGTAGGTACAACATTAATTTTTGGTCTGGGGAACCAAGAAGCAAAGGCTGCAGAAAGTACTAATAAAGA +ATTGAACGAAGCGACAACTTCAGCAAGTGATAATCAATCGAGTGATAAAGTTGATATGCAGCAACTAAATCAAGAAGACA +ATACTAAAAATGATAATCAAAAAGAAATGGTATCATCTCAAGGTAATGAAACGACTTCAAATGGGAATAAATTAATAGAA +AAAGAAAGTGTACAATCTACCACTGGAAATAAAGTTGAAGTTTCAACTGCCAAATCAGATGAGCAAGCTTCACCAAAATC +TACGAATGAAGATTTAAACACTAAACAAACTATAAGTAATCAAGAAGCGTTACAACCTGATTTGCAAGAGAATAAATCAG +TGGTAAATGTTCAACCAACTAATGAGGAAAACAAAAAGGTAGATGCCAAAACTGAATCAACTACATTAAATGTTAAAAGT +GATGCTATCAAGAGTAATGATGAAACTCTTGTTGATAACAATAGTAATTCAAATAATGAAAATAATGCAGATATCATTTT +GCCAAAAAGTACAGCACCTAAACGTTTGAATACAAGAATGCGTATAGCAGCAGTACAGCCATCATCAACAGAGGCTAAAA +ATGTTAATGATTTAATCACATCAAATACAACATTAACTGTCGTTGATGCAGATAAAAACAATAAAATCGTACCAGCCCAA +GATTATTTATCATTAAAATCACAAATTACAGTTGATGACAAAGTTAAATCAGGTGATTATTTCACAATTAAATACTCAGA +TACAGTACAAGTATATGGATTGAATCCGGAAGATATTAAAAATATTGGTGATATTAAAGATCCAAATAATGGTGAAACAA +TTGCGACTGCAAAACATGATACTGCAAATAATTTAATTACATATACATTTACAGATTATGTTGATCGATTTAATTCTGTA +CAAATGGGAATTAATTATTCAATTTATATGGATGCTGATACAATTCCTGTTAGTAAAAACGATGTTGAGTTTAATGTTAC +GATAGGTAATACTACAACAAAAACAACTGCTAACATTCAATATCCAGATTATGTTGTAAATGAGAAAAATTCAATTGGAT +CAGCGTTCACTGAAACAGTTTCACATGTTGGAAATAAAGAAAATCCAGGGTACTATAAACAAACGATTTATGTAAATCCA +TCGGAAAATTCTTTAACAAATGCCAAACTAAAAGTTCAAGCTTACCACTCAAGTTATCCTAATAATATCGGGCAAATAAA +TAAAGATGTAACAGATATAAAAATATATCAAGTTCCTAAAGGTTATACATTAAATAAAGGATACGATGTGAATACTAAAG +AGCTTACAGATGTAACAAATCAATACTTGCAGAAAATTACATATGGCGACAACAATAGCGCTGTTATTGATTTTGGAAAT +GCAGATTCTGCTTATGTTGTAATGGTTAATACAAAATTCCAATATACAAATAGCGAAAGCCCAACACTTGTTCAAATGGC +TACTTTATCTTCAACAGGTAATAAATCCGTTTCTACTGGCAATGCTTTAGGATTTACTAATAACCAAAGTGGCGGAGCTG +GTCAAGAAGTATATAAAATTGGTAACTACGTATGGGAAGATACTAATAAAAACGGTGTTCAAGAATTAGGAGAAAAAGGC +GTTGGCAATGTAACTGTAACTGTATTTGATAATAATACAAATACAAAAGTAGGAGAAGCAGTTACTAAAGAAGATGGGTC +ATACTTGATTCCAAACTTACCTAATGGAGATTACCGTGTAGAATTTTCAAACTTACCAAAAGGTTATGAAGTAACCCCTT +CAAAACAAGGTAATAACGAAGAATTAGATTCAAACGGCTTATCTTCAGTTATTACAGTTAATGGCAAAGATAACTTATCT +GCAGACTTAGGTATTTACAAACCTAAATACAACTTAGGTGACTATGTCTGGGAAGATACAAATAAAAATGGTATCCAAGA +CCAAGATGAAAAAGGTATATCTGGCGTAACGGTAACATTAAAAGATGAAAACGGTAACGTGTTAAAAACAGTTACAACAG +ACGCTGATGGCAAATATAAATTTACTGATTTAGATAATGGTAATTATAAAGTTGAATTTACTACACCAGAAGGCTATACA +CCGACTACAGTAACATCTGGTAGCGACATTGAAAAAGACTCTAATGGTTTAACAACAACAGGTGTTATTAATGGTGCTGA +TAACATGACATTAGATAGTGGATTCTACAAAACACCAAAATATAATTTAGGTAATTATGTATGGGAAGATACAAATAAAG +ATGGTAAGCAGGATTCAACTGAAAAAGGTATTTCAGGCGTAACAGTTACATTGAAAAATGAAAACGGTGAAGTTTTACAA +ACAACTAAAACAGATAAAGATGGTAAATATCAATTTACTGGATTAGAAAATGGAACTTATAAAGTTGAATTCGAAACACC +ATCAGGTTACACACCAACACAAGTAGGTTCAGGAACTGATGAAGGTATAGATTCAAATGGTACATCAACAACAGGTGTCA +TTAAAGATAAAGATAACGATACTATTGACTCTGGTTTCTACAAACCGACTTACAACTTAGGTGACTATGTATGGGAAGAT +ACAAATAAAAACGGTGTTCAAGATAAAGATGAAAAGGGCATTTCAGGTGTAACAGTTACGTTAAAAGATGAAAACGACAA +AGTTTTAAAAACAGTTACAACAGATGAAAATGGTAAATATCAATTCACTGATTTAAACAATGGAACTTATAAAGTTGAAT +TCGAGACACCATCAGGTTATACACCAACTTCAGTAACTTCTGGAAATGATACTGAAAAAGATTCTAATGGTTTAACAACA +ACAGGTGTCATTAAAGATGCAGATAACATGACATTAGACAGTGGTTTCTATAAAACACCAAAATATAGTTTAGGTGATTA +TGTTTGGTACGACAGTAATAAAGACGGCAAACAAGATTCAACTGAAAAAGGTATCAAAGATGTTAAAGTTACTTTATTAA +ATGAAAAAGGCGAAGTAATTGGAACAACTAAAACAGATGAAAATGGTAAATACTGCTTTGATAATTTAGATAGCGGTAAA +TACAAAGTTATTTTTGAAAAGCCTGCTGGCTTAACACAAACAGTTACAAATACAACTGAAGATGATAAAGATGCAGATGG +TGGCGAAGTTGACGTAACAATTACGGATCATGATGATTTCACACTTGATAACGGATACTTCGAAGAAGATACATCAGACA +GCGATTCAGACTCAGATAGTGACTCAGACAGCGACTCAGACTCAGACAGCGACTCAGACTCAGACAGTGATTCAGATTCA +GACAGCGACTCAGATTCAGATAGCGACTCAGATTCGGACAGCGATTCAGACTCAGATAGCGACTCAGATTCAGATAGCGA +TTCAGACTCAGACAGCGACTCAGATTCAGATAGCGATTCGGACTCAGACAGCGATTCAGACTCAGATAGCGACTCAGACT +CAGACAGCGACTCAGATTCAGATAGCGATTCGGACTCAGATAGCGACTCAGATTCAGACAGCGATTCAGACTCAGATAGC +GACTCAGATTCAGACAGCGATTCAGACTCAGATAGCGACTCAGACTCAGACAGTGATTCAGATTCAGACAGCGACTCAGA +CTCAGATAGCGACTCAGATTCGGACAGCGACTCAGACTCTGATAGCGACTCAGACTCAGACAGTGATTCAGACAGCGATT +CAGACTCGGATGCAGGAAAACATACACCTGTTAAACCAATGAGTACTACTAAAGACCATCACAATAAAGCAAAAGCATTA +CCAGAAACAGGTAGTGAAAATAACGGCTCAAATAACGCAACGTTATTTGGTGGATTATTTGCAGCATTAGGTTCATTATT +GTTATTCGGTCGTCGCAAAAAACAAAACAAATAATACAATATGACCCAGGTCCTTGTGGCCTGGTTTTTTTATAATTACA +CATGCAATAGATGTATTTTTCATATAAAATAAACAATAAAGATACGGAATAAAACTTATATGAGGCGATAATATGAATTA +CATTTTAGGAACAATTTTAGAAAGTAAAATTACAGGTGTAGAAAAAGCGCAAATAAATAGATTGAAGTTGTTCAAACAAC +ACGGCATATCTTCAAAATGTGTATATGTTAAATGGAATCCTTATTCATACACATATGCGAAGCAACATCAGATTGAAAAT +GATGTATTTACAATGTATGACTATTTTCAAAAAGCAATCAATTATAAAAAGACAAAGCAAGTTAACTGGATACAGTATTG +GGAAAAGTCATGTAGGTACACATTGAAATTTGTGGAAAATTCAAATGATGTCAGAATATATGATGAAGAGCAATTTGTAA +TGTATGCTCATTTTTTAGATAAACAGTATCATCAATTAAATTATGTGAATTATTTTGATCATAAAAGAAGAAAAGTAAAA +CGCGAATTGTATGATGGAAGAGGCTTTTTAAGTTGTTCTCGAATTTTAGGTGAAGGACAACGGATTGTACTCGAAAATTA +CTATACACCTAATGGGGAAATCGTCATCCAAAAATATTTTGACGATATAAAAGGGAAAAACACGCTCACAAAGGTTATCT +TAAATGAAGACCAGCATCAACAATTTTTTGATACAGAAGATGAATTAGTTCAATATTTTCTCCATCAATTATGTAAAAAT +AATGATCAAATCATATTAGATCGTCCTCATGAATTAGGAAATGTTATAGCGGGATTAAATCAAAGTATTCCAGTTATTGT +TGTGCTCCATAGTACACATTTATCCGGTGCCGGTAATGGTATAAAAAGTTTTTATAAAACAGTGTTTAATAATTTAACAC +GTTATAAAGCGATTGTTGTATCAACAGAAAAGCAATGCCAAGATATTTCACAATATATTGAAAATAAAATACCAGTTATC +AATATTCCGGTTGGCTACGTGGCAAATTTAAAGTATCAATTTGACATCAATCAAAAGGAGAAAAATCATATCATATCAAT +TGCTCGCCTCGTTGAAAATAAACAAATTAAACATCAAATTGAAGTAATCAAGCAATTAGTAACAAAACATCCCAATATTC +AATTGAATATTTATGGACATGGAAATGGTTTGTCAGAATATCGACAACTTGTAGAAGATTATCATTTATCGGAACATGTT +AAATTTCATGGTTTTAAGACGCATATTAATGAAGAGATTGCTAAAGCAGAACTGATGTTATCGACAAGTAAAATGGAAGG +TTTTGGCTTAGCAATTTTAGAGTCGCTTTCAGTAGGTACACCAGTGATCAGTTATGATGTAGATTATGGTCCATCAGAAC +TGATTCAAGATGGATTTAATGGCTATTTAGTACCTCAAGGTGACATCAATCAAATGGTTGAAAAGGTCGACCAATTACTA +AATAATACTCAAAAATTGCAACAGTTTTCAATTAATAGCATAGAATCTGCACAACAGTACAATGCAACTACTATCAGTAC +AAAGTGGCAAAATATTTTAAACTAAGTCAAAAGAAAAAAGCATTTTCCTAGAATCATCTATTTAACAAACCTCGTTCATA +CGAACTTACATGTTTAGTTAAATGACATAGGAAAATGCTATTTTTGTAAAAATAAAGGTAAAGTACATTTATATAAGACG +AACAAATTGGTCCCATTGTTTAATTAACGACGCTTTACTATATTGTTGTGCTTTTGCCAAACTACCTTTTGACAGTCGTT +GCTGTACTTCAGGATGATCAATCACATATTTTACTTTATCAAATAGGGCATCTTCATCATTTTTAGTAATTAAATAACCA +TTGAAATCTGAAGTAATCAGTTCGTTAGGTCCATATTTAATATCATAACTAATAACTGGAACACCATGTGCTAAAGATTC +AAGTAGCGCTAAAGAGAAACCTTCCATGTTACTTGTTATTAAACTCAAATAGGCATCGCTATATTCTTGGTCTAGATTGC +TTAAAAAGCCGCGTAAGTAAACATGATTTTCCAATCCATATTTTTGTATCAATTCATTTAATTTTTTACTTTCAGAACCA +AAACCATACATATGAAGCTCTATTTTTGGGACATACGATACTAAGCGTTTAATTAATTCAATTTGTTGATGTAATTGTTT +TTCAGGTGAATAACGAGCAACGGAAATTAATTTAACACTGCGCTGATCTAATGTTTGGACTGGTGTATCAATTGTTTCAC +TATAGCCGACAGGAATATTAACAACTGGAATAGTATGGTTAATACGTTTTTCAACATCTAATTTTTGCTGCTCAGTAGAA +ACGATAATTGCACGATATCGAGATAAATTTTCAAACATCGCTTTATATACATTTTTAAATGGCGATGAATCTAATGCATC +AATATTTTTAATGTGTGTACTGTGAAGCACAGCTACTACTGGGATTGACTCAGGCGTTAAGTTGAAAATAGGTGCTGTGT +ACACATTACGATCACTGAAAAATAAATCCCCATGTTGATATAGTTGTTTAATGAAAAATGCGCCTAATTCCGTTTCATTA +TTAAAGAAATATTGTTTGTTAGCATAGTAAACAATAATTTTTTGTACTTCTGGTTTGCCATCCTTGTAAGAAAAATACTT +TTCTAATTTTGTGTCACCTTCTGGATTATAGAAAAATTCACATAATGTTTGTTGTTTATCAACAAGAATCCTACTACAAC +TTAAAAAGCCACGCACATCATAAAAATCACGTTTTACTTTTCGTCTTTGACTATCAAAATGATTTACATAATCTAATATA +CGATATTTAGGATCTTGAAAATGGGCATACATTAAGAAACGCTCTTGATCATATATTCTAAAGTCATGACTATTTTCAAC +ATGTTTTAAAGTATAATGACATTCATCAGTCCAATACGACAACCAGTCAAATGGTTCATTGCGTTCTAAATATGTTGCTT +CTTGGAAGAAATCATACATATTAATATAGTCAGAACTAGTAATATAATTTTGGGCATTTCTATATAAATATCTATTCCAT +GACAGAAATACACATTGCGCTGGTCTTCCCATTTCTTTAAATAAATTTAAACGATTAATAATTGCTTTCTCTATCCCAGT +TAAATTAACACCTAAACTATTACCTACAAAATAATTCATTTACAACACCACTTATATCTATTTTTTATAATTATATCACA +TAATATTTAATTACTTCTTTTAACTGGAAGATGTGTTTATTTATAAAACAACAAATTTTGATATTTATAATGATAGTAGT +TATTCAATCAACTACGACCAATATATCATTGTAGAGCTTAGGATATTGATTTATGACTCAGGCACATCAAATGAGAAGAT +TTATAAAAGAGATATACAACTCTAGAAGGTATAATAAAAACGCGCAACTAATGTTACGCGTTTGAATTAATCATATGATA +TTATTTGCGATACTTTAATTTAGCGAAAGCATCATGTTGATGGATAGACTCTTCATTACGACATTCGATATCGAAACCGT +CTAACCAATCAAATTCAACTAAGTCCGCGGCAATTAAACGAATTAAGTCTTCGACAAAACGTGGATTTTCATATGCACGC +TCTGTCACACGTTTTTCATCAGGACGTTTTAAAATAGGGTATAGAATTGAACTTGCATTAGCTTCCATTGCATCTAAAAT +TTTATTTTTATAGTCATCAACTATGTCTTGATCTTTATTAATATATGTTTTAACAGTGACAACACCACGTTGGTTGTGCG +CTGAATACTCACTTATTTCTTTTGAACAAGGGCATAGCGTTGTGACAGTTGCTTCAATAGTAAGTTCTTTACGTGTAACT +TTATCACCGTCAATTGCTAATCCATAAGTGACATCGGCATTACCAACTGCTTTAATATTTGTGGTTGGACTATAGCGATC +AAAGAACCATTTCCCAGAAACATCAACGCCTGCCGCATTTTGTTTCATATTCGTTTGTAAAGTGCGTAACACCTGATAAA +GTGTATTAAATTCAAGTTCAATACCATTATCATAGTGCTTTTCAACACTTTCGATTATACGGCTCATATTAATACCTTTT +TCGTCTTTTGTTAAACTTGTTGAAAAACTAAATGTGCCAGCTGTTTGATACTGGTCAACAAGTACAGGGTACACTAAGTT +TTTAATACCAACTTCTTCTATTTCAAATAAAAAATCTTTATGTGTACTTTGTAAATCTGTCATTTCGTTCTTAGTAGTAG +GTTTCGTGCCTTCAATAGGATCTACGGAACCAAAGTGTTTCCAACGACCTTCTCGTGTCGATAAATCAAATTCAGTCATT +TTTTTCCTCCGTTAAGATTTAAAGTGATATGTCCAATATGGTTCGACTGTTAAAAAGCTGTGTTGTTTACCATCGATTTC +AGGACTTGCTAATTGTTTTAAAAATGGACCTGTTTGAGAAGCATGTGCTTCAAATGCCTTAATTTTAAGTTCTTTAAAAT +CTGTAATATCATTTTGAATATCAGGTTCTCCAAGAGCTTCGGTTGCATCATTACTGAACGCAACTAAAGTTAAACGAGGG +CGTTCTTCTTTAGGCATGCGTTCAACCGTTCGAATTACAGCGTCTGCTGTTGCTTCGTGATCAGGATGTACTGCATATCC +AGGATAAAATGAAATAATCAATGATGGATTTGTATCATCGATTAAAGATTTAATCATACCATCTATATGTTCATAGGGTT +CAAATTCGACAGTTTTGTCACGTAAACCCATTTTTCTTAAATCAGTAATACCGATAACTTTACAAGCTTCTTCTAGTTCA +CGCTCACGAATACTTGGTAATGATTCGCGTGTTGCAAATGGGGGATTACCTAAATTTCTGCCCATTTGTCCTAGGGTTAA +ACATGCATATGTTACAGGTATGCCTTTTTGGATATAATTTGCTAATGTGCCTGCAGATGAGAAGGTTTCATCATCAGGAT +GTGGAAATATTACTAATACATGTCTTTCGTCAGTCATGTTGATGCCTCCTCTATAAATTAAATGGTCGCTCACTAATTTG +AAGTGCTGCAGCGAGTTGACCTTCGTAATTAAAACCTGCAATTAAAAATTCATCATGCTCATTGACCTCAAAATGCGTTA +GACCTTGTACATAAACCCAACCACCATTTGATAGTTTAAGACCAATGCGATAAGGTTCTTTATTACCACCTTTTAGTTGT +GCATGCGTATATGTTATTTGTATGTTTCTTAAAAAAGTACCAGCATTAAAAACACGTTGATCGAAATGGTTCGCATAGGC +CCCATTTGTCGTTTCAACATGCAGATACACAGGTTTATGTTCAAAAGAAGCAAGTAAATCTATAACTTCTTGTTCTTTAA +TTGGTTCCAACACGTTCACTCCTTACACTATCAATGTGTTTATCTTTCTATTTTACTAAAAACTATTCGATAATTGTATA +CGATTGCTCAATTATTTATAAATTAATTTTCATGAAGGGTAATTACTCAGGATTACGTAATCATACAGCATTAGTTTTTT +ACTTTTAAAAATCAAAAATTTGTTGGAATTTGAAAAGTGTTAAACATTAAAAATGATGCTATATTAATGGTGTATGAATG +AATTCATAAGTTTTTAAAATGTATTAAATTTGTGGAGGCATGTAAACAATGAAAGTATTAAACTTAGGATCGAAAAAACA +AGCATCATTCTATGTTGCATGTGAGTTATATAAAGAGATGGCATTTAATCAGCACTGTAAACTAGGTTTAGCAACTGGTG +GTACAATGACAGATTTGTATGAACAACTTGTTAAGTTGTTAAATAAAAATCAGTTAAACGTAGACAATGTATCCACGTTT +AATTTAGACGAATATGTAGGTTTAACCGCATCACATCCGCAAAGTTATCACTATTATATGGATGACATGCTTTTCAAACA +ATATCCTTATTTTAATAGAAAGAACATTCATATTCCAAATGGAGATGCCGATGATATGAATGCGGAAGCGTCAAAATATA +ATGACGTTTTAGAACAACAAGGTCAACGTGATATTCAAATTTTAGGTATTGGTGAAAATGGTCATATTGGATTTAATGAA +CCTGGTACGCCGTTTGATAGCGTTACTCATATCGTTGATTTGACTGAAAGTACTATTAAGGCTAATAGTCGATATTTTAA +AAACGAAGATGATGTTCCAAAGCAAGCCATTTCGATGGGACTTGCTAATATTCTTCAAGCCAAACGTATCATTTTACTCG +CATTTGGTGAAAAGAAACGTGCTGCTATTACACATTTATTAAATCAGGAAATTTCTGTTGATGTTCCAGCCACATTACTT +CACAAACACCCGAATGTTGAGATATATTTAGACGACGAAGCTTGCCCGAAAAATGTTGCGAAAATTCATGTCGATGAAAT +GGATTGATTGCAATGTTTAATTAAGAAATGCCTCGGGAAAGGTTCCAATAGAAAGATAAAAAGCATTGGAAGGATGATTT +TTAGTGGAATTACAATTAGCAATTGATTTATTAAACAAAGAAGACGCGGCTGAGTTAGCAAATAAAGTAAAAGATTATGT +AGATATCGTAGAAATCGGTACGCCAATCATTTACAACGAAGGTTTACCAGCAGTTAAACATATGGCAGACAACATTAGTA +ATGTAAAAGTATTAGCAGACATGAAAATTATGGATGCAGCTGATTATGAAGTTAGCCAAGCAATTAAATTTGGCGCGGAT +GTAATTACAATACTAGGTGTTGCAGAAGATGCATCAATTAAAGCAGCTATTGAAGAAGCTCATAAAAATAATAAACAATT +ACTAGTTGATATGATTGCTGTTCAAGATTTAGAAAAACGTGCAAAAGAACTAGATGAAATGGGTGCTGATTATATTGCAG +TACACACTGGTTATGATTTACAAGCAGAAGGGCAATCACCATTAGAAAGTTTAAGAACCGTTAAATCTGTTATTAAAAAT +TCTAAAGTTGCAGTAGCAGGTGGAATTAAACCAGATACAATTAAAGATATTGTCGCTGAAAGTCCTGATCTTGTTATTGT +TGGTGGCGGAATCGCAAATGCAGATGATCCAGTAGAAGCTGCGAAACAATGTCGCGCTGCAATCGAAGGTAAGTAATATG +GCTAAATTTAGTGACTATCAATTAATTCTAGATGAATTAAAGATGACTTTGTCACATGTTGAAGCGGATGAGTTTTCAAC +TTTTGCATCCAAAATACTACATGCTGAACATATATTTGTAGCTGGCAAAGGACGTTCAGGATTCGTGGCGAATAGTTTTG +CAATGCGCTTAAATCAGCTCGGCAAACAGGCACATGTTGTTGGAGAATCAACGACACCTGCGATTAAGTCGAATGATGTA +TTTGTAATTATCTCTGGTTCAGGTTCCACGGAACATTTAAGATTATTAGCAGACAAAGCAAAATCAGTAGGTGCTGACAT +CGTATTAATTACTACAAATAAAGATTCTGCAATAGGCAATCTAGCTGGGACGAACATCGTTTTGCCTGCAGGTACAAAAT +ATGATGAACAAGGCTCGGCACAACCATTAGGAAGTTTGTTTGAACAAGCATCTCAATTATTTTTAGATAGTGTTGTAATG +GGATTGATGACTGAAATGAATGTTACGGAACAAACGATGCAACAAAATCATGCTAATTTAGAATAAAATAAAGATAGTCG +ATAATATGATGCCTAGGCAGAAATATTATCGATTATTTTTTTATTTAAATAATAAATTATAGTATAATATCAATAATAAA +CGAATAGGGGTGTTAATATTGAAGTTTGACAATTATATTTTTGATTTTGATGGTACGTTGGCAGACACGAAAAAATGTGG +TGAAGTAGCAACACAAAGTGCATTTAAAGCATGTGGCTTAACGGAACCATCATCTAAAGAAATAACGCATTATATGGGAA +TACCTATTGAAGAATCATTTTTAAAATTAGCAGACCGACCATTAGATGAAGCAGCATTAGCAAAGTTAATCGATACATTT +AGACATACATATCAATCTATTGAAAAGGACTATATTTATGAATTTGCGGGTATAACTGAAGCCATTACAAGTTTGTATAA +CCAAGGGAAAAAACTTTTCGTGGTGTCTAGTAAGAAGAGTGATGTATTAGAAAGAAATTTATCGGCTATTGGATTAAATC +ACTTGATTACCGAAGCTGTTGGATCCGATCAAGTAAGTGCATATAAACCAAATCCTGAAGGCATACACACAATTGTGCAA +CGCTACAATTTAAATAGCCAACAAACGGTGTATATTGGTGATTCAACGTTTGATGTTGAGATGGCACAACGTGCTGGTAT +GCAATCTGCAGCTGTCACTTGGGGTGCACATGATGCAAGGTCATTACTTCATTCAAATCCGGATTTTATTATTAATGATC +CATCAGAAATTAATACCGTATTATAAAACTTGTTAAAACAGAGAATACCATGGTTAAAATGCATATTCATAAATATTAGA +TTATACTTAGAAATATTTCGCTTTAGATTAGGAATTTAAAATAAATATTTATTAAACATTATGAATTTTTAAAGAGTAAT +GTCTGACTCGTTGATAATTTATTTTTGTAAAAATAAATTAAAGTAATGACAAAGTTATTGAAGTAAATTGAGTATAAACA +TTTAAATACGATGTCGAAAATGGCGATAGCATATCACTTACATGAAGTTGTGTGCTATCGCTATTTTTAGTTATAATTCC +AAAAAGTTAATCGTTCGATGATTTAAGAATTATTATTGTTTAATTCAAATGTATGAGGGTATAAAATCATTGAATTTAAT +TCGATAAAGCGAAATTTTTGAACAAACATACTTTTGTATTTATATAAAAGTTTAAATTCTTATAAATTTGACAAAACTAA +TTAACTCCGTATAATTATGAAACATACAAGAGGGAGTGTATGAATTCATGGATTTTAATAAAGAGAATATTAACATGGTG +GATGCAAAGAAAGCTAAAAAAACCGTTGTTGCAACCGGTATCGGTAATGCAATGGAATGGTTCGATTTTGGTGTCTATGC +ATATACAACTGCGTACATTGGAGCGAACTTCTTCTCTCCAGTAGAGAATGCAGACATTCGACAAATGTTGACTTTCGCAG +CATTAGCCATTGCGTTTTTATTAAGACCAATTGGTGGTGTCGTATTTGGTATTATTGGTGACAAATATGGACGTAAAGTT +GTATTAACATCTACAATTATTTTAATGGCATTTTCAACATTAACCATTGGATTATTGCCAAGCTATGATCAAATTGGACT +TTGGGCACCAATACTATTATTGCTTGCAAGAGTACTACAAGGGTTTTCAACAGGTGGAGAGTATGCGGGGGCAATGACAT +ATGTTGCCGAATCATCTCCAGATAAGCGTCGTAACTCATTAGGTAGTGGACTAGAAATTGGGACATTATCAGGTTACATA +GCTGCTTCAATTATGATTGCTGTATTAACATTCTTTTTAACAGATGAACAAATGGCATCATTTGGTTGGAGAATCCCATT +CTTACTCGGTTTATTCCTAGGATTATTCGGCTTATATTTACGTCGTAAGCTGGAAGAATCACCAGTTTTCGAAAATGATG +TTGCAACACAACCAGAAAGAGATAACATTAACTTTTTACAAATCATCAGATTTTATTACAAAGATATATTTGTATGTTTT +GTAGCTGTTGTATTCTTCAATGTTACAAACTATATGGTAACTGCATATTTACCAACCTATTTAGAACAAGTTATTAAATT +AGATGCAACGACAACAAGTGTATTAATTACTTGTGTCATGGCAATAATGATTCCATTAGCATTAATGTTTGGTAAGTTAG +CGGATAAAATAGGTGAAAAGAAAGTATTTCTAATTGGTACTGGTGGGCTAACATTATTCAGTATCATCGCATTTATGTTA +TTACATTCACAATCATTTGTTGTAATAGTAATCGGTATATTTATATTAGGATTTTTCTTATCAACTTACGAAGCGACAAT +GCCAGGGTCGTTACCAACGATGTTTTACAGTCATATAAGATATCGAACTTTATCAGTAACATTTAATATCTCTGTTTCGA +TATTTGGTGGTACGACGCCATTAGTTGCAACATGGTTAGTTACGAAAACTGGAGATCCATTAGCACCTGCGTATTATTTA +ACAGCAATCAGTGTTATTGGCTTTTTAGTTATTACATTCTTACATTTAAGTACAGCAGGAAAATCTCTAAAAGGTTCGTA +TCCAAATGTAGATAACGAGCAAGATAGAGCTTATTATGCAGAACATCCAAAAGAAGCATTATGGTGGGTTAAAGAACGTA +AGAATTAGAGATTTTAATAAAAAGTATAAATCAATCGTATATAAGCACTTTAAAGCTAGTAGGTTCTGCTAACTTTAAAG +TGCTTTTTAAATTGAGAACTGTAATTAGCCGTAATAAAGTTTTTGTATATACATAAACCCCCACTGCAATGATTATCGCA +ATGGGGGAAAGAGGGGACTTAAAGCATATGTTTAGCTTTGAATACTTAAAATTCTCTTGCTATTGAAATGTTAGGATGTA +AATATGTCTTAGAGTATTTTGTCCAACGCAATTAATATTGAGACTCTAACCTTCAATATTATTATAGAGAACACAAACTT +AAATAGATTGGGTGACTTATTTGTGTCAGTTATTGCGATTGCGATAACTTCTTTTCTCTATATACATATAGTAACGTCTT +ATCTAATAAAAAACATGGTACTACAGTATCAAATTTATCTAGGGCTTAAGTTTGATTTTTATAATAGGCAGGTTTACCTG +ATAAAAATACTTATTCATTATATAATGTTAACAATATGTATTTTAAAGTTTACATTGAGTGAGGGATATTGATGAACGTA +ATTTTAGAACAGTTGAAAACACATACTCAAAATAAACCTAATGACATAGCATTACATATCGATGATGAAACAATTACATA +TAGTCAACTAAATGCCCGCATCACTAGCGCAGTTGAATCTTTGCAGAAATATTCACTTAACCCTGTCGTTGCTATTAATA +TGAAATCACCGGTGCAAAGTATTATTTGTTATTTAGCTTTGCATCGTTTACATAAAGTGCCTATGATGATGGAAGGTAAA +TGGCAAAGTACTATACATCGTCAATTGATTGAAAAATATGGTATTAAAGATGTAATTGGAGATACAGGTCTCATGCAGAA +TATAGACTCACCGATGTTTATTGATTCAACGCAATTACAGCACTACCCCAATTTATTACATATTGGTTTTACTTCAGGGA +CAACTGGACTGCCAAAAGCATATTATCGTGATGAAGATTCATGGTTGGCTTCTTTTGAAGTTAATGAAATGTTGATGTTA +AAAAATGAAAATGCAATAGCAGCCCCTGGACCACTATCGCACTCGTTAACATTATATGCGTTATTGTTTGCTTTAAGTTC +CGGTCGTACTTTTATAGGACAGACCACTTTTCATCCTGAAAAGTTACTTAATCAATGTCATAAAATATCATCATACAAAG +TTGCTATGTTTCTTGTTCCAACGATGATTAAATCATTATTGTTAGTTTACAACAATGAACATACAATCCAATCATTTTTT +AGCAGTGGAGATAAGCTGCATTCTTCTATTTTTAAAAAGATAAAAAATCAAGCAAATGACATAAATTTGATTGAATTTTT +TGGTACATCGGAAACCAGTTTTATCAGCTATAACTTGAATCAGCAAGCACCAGTTGAATCAGTAGGTGTGCTATTTCCAA +ATGTGGAATTGAAAACAACGAATCACGATCACAATGGTATAGGAACTATTTGTATAAAAAGTAATATGATGTTTAGTGGC +TATGTAAGTGAACAATGTATAAATAATGATGAATGGTTTGTTACTAATGATAATGGCTATGTAAAAGAGCAGTATTTATA +TTTAACGGGACGTCAACAGGATATGTTAATTATTGGTGGTCAAAATATATATCCAGCACATGTTGAACGCCTTTTAACGC +AATCTTCGAGCATTGATGAAGCAATTATCATCGGTATTCCAAATGAGCGTTTTGGTCAAATAGGCGTATTGCTTTATTCT +GGTGATGTGACACTTACACATAAAAATGTAAAACAATTTTTAAAAAAGAAAGTGAAACGCTATGAAATTCCATCGATGAT +TCATCATGTAGAAAAGATGTATTACACTGCAAGTGGTAAAATTGCTAGAGAAAAAATGATGTCGATGTATTTGAGAGGTG +AATTATAATATGAATCAAGCAGTCATAGTTGCAGCTAAACGAACTGCATTTGGGAAATATGGTGGCACTTTAAAACATTT +AGAGCCAGAACAATTGCTTAAACCTTTATTCCAACATTTTAAAGAGAAGTATCCAGAGGTAATATCTAAAATAGATGATG +TAGTTTTAGGTAATGTTGTTGGGAATGGTGGCAATATTGCAAGAAAAGCATTGCTTGAAGCGGGGCTTAAAGATTCAATA +CCTGGCGTCACAATCGATCGGCAATGTGGGTCTGGACTTGAAAGTGTTCAATATGCATGTCGCATGATCCAAGCCGGAGC +TGGCAAGGTATATATTGCAGGTGGTGTTGAAAGTACAAGTCGAGCACCTTGGAAAATCAAACGACCGCATTCTGTGTACG +AAACAGCATTACCTGAGTTTTATGAGCGTGCATCATTTGCACCTGAAATGAGCGACCCATCAATGATTCAAGGTGCTGAA +AATGTGGCCAAGATGTATGATGTTTCAAGAGAATTACAAGATGAATTTGCTTATCGAAGTCATCAATTGACAGCGGAAAA +TGTAAAGAATGGAAATATTTCTCAGGAAATATTACCTATAACCGTTAAAGGAGAAATATTCAACACTGATGAAAGTCTAA +AATCACATATTCCGAAAGATAACTTTGGCCGATTTAAGCCCGTGATCAAAGGTGGGACCGTTACCGCTGCGAATAGTTGT +ATGAAAAATGATGGTGCAGTTTTATTGCTTATTATGGAAAAAGATATGGCATACGAATTAGGTTTCGAGCATGGTTTATT +ATTTAAAGATGGTGTTACGGTAGGTGTTGATTCTAATTTTCCTGGCATTGGTCCAGTACCAGCCATTTCCAACTTACTAA +AAAGAAATCAATTAACGATAGAAAATATTGAAGTCATTGAAATTAACGAAGCGTTCAGTGCACAGGTAGTTGCCTGCCAA +CAAGCTTTAAATATTTCAAATACGCAATTAAATATATGGGGTGGTGCATTAGCATCAGGTCATCCATACGGTGCAAGCGG +TGCCCAATTAGTGACTCGATTATTTTATATGTTTGACAAAGAGACTATGATTGCATCTATGGGGATAGGGGGAGGTCTAG +GAAATGCAGCATTATTTACTCGATTCTAACCAGCGATTAAATGTGTCATTTTCTAAGGATAGTGTGGCTGCATATTATCA +GTGTTTTAACCAACCTTATAGAAAAGAAGTACCACCATTAATGTGTGCGTCATTATGGCCAAAATTTGATTTATTTAAAA +AATATGCAAATAGCGAACTGATTTTAACAAAATCAGCAATTAATCAAACTCAAAAGATAGAAGTAGACACAATATATGTA +GGGCATTTAGAAGATATTGAATGCCGACAGACTCGCAATATCACACGTTATACAATGGCTTTAACATTAACTAAAAATGA +TCAACATGTCATAACGGTTACACAAACTTTTATTAAGGCGATGAAGTAGAGATGGAGTTTAATGAGATATGGATAAATGA +ATATTTGGCGCTCGTAAATGATGATAATCCAATACATAATGAGATTGTGCCAGGACAATTAGTGAGTCAAATGATGCTGA +TGGCTATGTCATTAGAGACAAACCAGTGTCAAATTAACTACGTTAAACCTATTTTAATAAATGAAAATATCGAATTCATT +GAACAACACGAACACGAAATTATAGCAATTAATGACGATGGAGAGATTAAAATAAAAATTTCTTTGAGCACAAAAAAATA +ACCGATATTAGCTGCATGAACGCATATTAATTAGGAGATGAAAGGACAGCTAATATCAGTTATGTATTGTTATTATTATT +GGGAACAGAGATGAATATAGGTTACGTTTCTTTCTTTGCACGGGGATGCATTAATCTAAAATAATAATAACAACTATATC +AATGTTTAATAAATTCTGGATTATTGGAACGATTAGTCAATTTAACTAACTTTCATATGATCTATATCGTCTTGTAATAA +AGAGAGCAATTTGAATATTTCAGTATCACTAAATGAATCGTCACATTTAATTGAAACATGCTGAAACGTTTTGGTTATAA +TTTCATAAACTGGTGCGCCTTCATGGTGATACTGTCGATAAATAATCATAACCTATATTACCTCCTTTGCTACTCTATGG +TTATATTATAAATAACATTTTTATGTGTGACATCAACCTTAAGTATCAACTTTTTATCAGACATAGAACGTATGATTTAC +TAAGACTATTTATGTATAAAAGTTCTAAATAAATATATATTTATAGAGTCGCCTGGCAGTCATTTGGGAAATATAACATA +TATGATTAGAGAGGCATCTATCGCAAAAGAATGATAATGATAGAGGTATTGAGCATATAGATGAGTTTAAGTTCATCTTG +AAAATAAAGGGTTATTTAGTCATAGATGTAGATGTATAGGAAATATTTGTATGTATTGTTCGATATGTATGAAATTTTCA +ATAAAAGCTAATAACGCTTATATGTAACTTTCAAATTTAAATTATATACAGAGCATGATGATTATAAAAAAATAACCACA +TCACATAAATTGAGTTCATACCCAATTTAAGTGGTGTGGCTAATAATGTTGATTTATAGATGAACCGCCTAATCGTTAAA +CCTCTGTTACTTCAACATCGATATGTTCAATACGGTTGTATGCACCGTGATCCACAGGACCAACAAAATCATTCATTTTC +CAACCGTTTTTAATAGCAGAAGCGACGAAAGCTTTCGCGCTAATCACAGCTTCTTTCGGTGACTTACCGTTAGCTAAATA +TGCAGTTGTTGCCGCAGCAAATGTACAACCAGCACCATGGTTATAACTTTGTTGGAACATGTCTGTTGTTAGTTGATAAA +ATGTTTGACCATCATAGTATAAGTCATACGATTTATCTTGATCTAAAGCTTTGCCACCTTTAATGATGACATGCTGTGCG +CCTTTATCAAAGATAATTGTTGCAGCCTTTTTCATATCTTCAATTGAATTTAATTTACCTAATCCTGATAATTGACCCGC +TTCAAATAAGTTTGGTGTCACTACCGTTGCTTTAGGTAGTAAATATTTAATCATCGCCTCAGTATTTCCAGGATTAAGCA +CTTCATCTTCGCCTTTACAAACCATGACAGGATCTACTACAAAATATTGTGCATTAGATGCCTCATATACTTCTCCAGCA +CGTTTGATTATCTCCTCAGTACCTAACATACCTGTTTTAATAGCATCAGGTCCGATTGATAAAGCCGTTTCAAGTTGTTT +TTCAAATACATCCATTGGTAATGGTGTAACATCGTGTGACCATGTATCTTTATCCATAGTAACGATGGCAGTTAAAGCGA +CCATGCCATACGTATCTAATTCTTGGAACGTTTTCAAATCTGCTTGCATACCTGCGCCAGCACTTGTGTCAGAACCGGCA +ATTGTTAAAACTTTCTTTAAAGCCATTGAGCTTCACTCCTACATAATAATATTGTATTCATCATATCATTTTTAACCTAA +TTGAAAAATATTAAGCATTCAATATTTGATGATTGTTGAAATGAATCATTCATACTATTGTAACTTTTGAAAATGTCATT +CACTTTAGATAAGTGTGATATGTTAAAATATGTCCTGAGGTGAGATTGAATGGAATGGTCGCAAATTTTTCATGACATAA +CAACGAAACATGACTTTAAAGCTATGCATGATTTTTTAGAAAAAGAATATTCGACTGCAATCGTATACCCTGATAGGGAA +AATATATATCAAGCGTTTGATTTAACACCGTTTGAAAATATCAAAGTTGTTATATTAGGACAAGACCCGTATCATGGTCC +AAACCAAGCACATGGATTAGCATTTTCAGTGCAACCTAACGCAAAATTCCCTCCATCTTTACGTAATATGTATAAAGAAT +TAGCAGATGATATTGGATGCGTTAGACAAACACCGCATTTACAAGATTGGGCAAGAGAAGGCGTCTTGTTATTGAATACA +GTTTTAACCGTAAGACAGGGTGAAGCAAATTCTCATCGTGATATTGGTTGGGAAACATTTACTGATGAAATTATTAAAGC +AGTGTCTGATTATAAAGAACATGTTGTCTTTATTTTGTGGGGGAAACCTGCACAGCAAAAAATAAAGCTTATCGATACAT +CTAAACATTGTATTATAAAATCAGTGCATCCTAGTCCACTGTCTGCATATAGAGGATTCTTTGGATCAAAACCGTATTCC +AAAGCGAATGCCTATTTAGAGTCAGTAGGAAAATCACCAATTAATTGGTGTGAAAGTGAGGCGTAGATGTTGAATAGAGA +AACTTTAATAGCACGAATTGAGCAAGAATTAGTACAAGCAGAGCAGGCACAGCATGACCATGACTTTGAAAAACATATGT +ATGCCATACATATATTAACATCTTTATATGCTTCAACATCAAATACACCACATATTGGTGAACAACAAATGAATCGTCGT +ATTGCTAACCATAATCAAATGCCACAATCACAAATAACGCAGCCAACTCATCAAGTGACAGTTGCTGAAATTGAAGCGAT +GGGTGGTAAAGTAAATACGCATTCAGCACATCATCATAATAAGTCATATTCACAACCTTCAAACCAACAACAAAGATTAG +CGACAGATGATGACATTGGCAATGGTGAATCCATATTTGATTTTTAAAAAGCAACAATGAAACATAATTACTTAATAGCT +TGTTAAGTATGTAGGTTAATAATCAAGACGCATATACTTTTATTCGAGTGTTCGGATTTAAACATTTATTAATACTGAAT +TATATAAGGAGAGGTAGCAATGAAATTATTTATTATTTTAGGTGCATTAAACGCGATGATGGCTGTCGGTACAGGTGCAT +TTGGTGCGCATGGTTTACAAGGAAAAATAAGTGATCACTATTTATCAGTATGGGAAAAAGCAACGACGTATCAAATGTAC +CATGGCTTAGCATTATTAATTATAGGTGTAATTAGTGGTACAACTTCAATCAATGTTAACTGGGCTGGCTGGTTAATATT +TGCTGGTATTATTTTCTTTAGTGGATCATTATATATTTTAGTATTAACTCAAATTAAAGTTTTAGGTGCGATTACGCCAA +TTGGTGGCGTATTGTTCATCATTGGATGGATAATGTTAATCATTGCGACATTCAAATTTGCTGGTTAAATTTTAAAACTT +TAGATTACCTATGTAACTAAACATTAAATTTTTAATAAAAATAATCAAGAAAAAGAGTTACAAACTCATCTTTTGGGTAT +AGAATACCTTCGAGGTGAGTTTTTATTTATGGAAAAAAAGAATAAGCAAATAGATAGAGGCGATTTAAAACAAAACCTAT +CTGAAAAGTTTGTATGGGCGATTGCATATGGTTCATGTATCGGATGGGGCGCATTCATCTTACCAGGAGACTGGATTAAG +CAGTCAGGTCCGATTGCAGCATCAATTGGTATAGTTATTGGTGCATTATTAATGATATTAATTGCGGTTAGTTATGGCGC +ATTAGTAGAGAGATTTCCAGTATCAGGGGGCGCGTTTGCCTTTAGTTTCTTAAGTTTCGGCAGATATGTGAGTTTCTTCT +CATCATGGTTTTTAACTTTTGGTTATGTCTGTGTCGTTGCTTTAAATGCGACCGCATTCAGTTTACTAGTTAAATTCTTA +TTGCCAGATGTCTTAAATAATGGGAAACTATACACCATTGCGGGCTGGGACGTTTATATTACGGAAATCATTATTGCGAC +CGTATTACTACTTGTATTCATGCTAGTAACGATTCGTGGCGCAAGTGTATCTGGATCATTACAATATTATTTCTGTGTGG +CGATGGTAATCGTCGTATTATTGATGTTCTTTGGTTCATTCTTTGGTAATAATTTTGCACTTGAAAATTTACAACCGTTA +GCTGAACCTAGCAAAGGATGGTTAGTGTCTATTGTGGTTATTGTATCCGTGGCACCATGGGCATATGTTGGATTTGATAA +TATTCCACAAACAGCAGAAGAGTTTAACTTTGCACCAAACAAGACATTTAAGCTTATCGTGTACAGTTTATTAGCAGCAT +CATTAACTTATGTTGTCATGATTTTATACACTGGTTGGTTATCAACAAGTCATCAAAGTTTAAATGGGCAGTTGTGGTTA +ACAGGTGCTGTTACACAAACAGCATTTGGTTATATTGGATTAGGTGTATTAGCAATTGCAATTATGATGGGTATATTTAC +TGGTTTAAATGGATTCTTGATGAGTTCAAGTCGCTTGTTATTTTCTATGGGACGTTCAGGTATTATGCCAACAATGTTTA +GTAAATTACATAGTAAATACAAAACACCATATGTCGCAATCATATTCCTAGTAGGAGTGTCGTTAATTGCACCTTGGCTA +GGAAGAACTGCATTGACTTGGATTGTAGATATGTCATCTACTGGTGTATCCATTGCCTACTTTATTACATGTTTGTCTGC +AGCGAAATTATTCAGTTATAACAAACAAAGTAATACGTATGCACCGGTTTACAAAACGTTTGCTATTATCGGCTCATTTG +TATCATTCATTTTCTTAGCGTTGTTATTAGTGCCAGGTTCTCCTGCAGCACTGACTGCACCGTCTTATATTGCATTACTT +GGATGGTTAATCATCGGTTTAATATTCTTTGTGATTCGATATCCTAAATTGAAAAATATGGATAATGATGAATTAAGTCG +CTTGATTTTAAATAGAAGTGAAAATGAAGTTGATGATATGATTGAAGAACCTGAAAAAGAAAAAACTAAATAATAAAAGA +ATCGCACAATAAACCTTCTTCATTCGGAGGCGTATCGTGCGATTTTTTGTATTATAAATTGACATTTAAGACGAGGCAGC +TGAACCTTATATATAATTGCTAAGAGTTAGGGCTGAGCCATTTCTAACAAATATTTATAATCGTTTAAAAGATTTCACGA +ACCCAGAAACAATTAATTTGGAAATTTGGTCGGCGAATAATAAACCTAATGCGATGGCGCCTGCAATAAGTGTAACCTCT +AGCATGGTATTGATTGCTGTACTGAAATTTAATAAGACTAAATTTTTTGTAGCATCGTATGCTAAGCCACCAGGTACTAA +TGGAATGATACCCGTTACCATAAAAATGATGGCAGGTTCTTTTTGTTTACGAGCCATATAATGACTTAACAAGCCTAATG +CTAAACTACCAAAGAAACTAGAGTATATAGTGTGCACATTAAAGCCGTTGAAGAATAAGGTGTAAACCATCCATCCACAC +GTACCAACGAAACCACATGATAGATATAATTTTCTAGGTGCATCAAAAATGACGCAGAAGAACATTGAAGCTAAAAAGCT +AAAGATAAAGTTTAAGATCCAAAACATAGTCTGATACTCCTATACTAAAATTAATACGCTACCAACGCCAGCACCGATGC +CAAACGCAGTAACCAATGCTTCTAATGATTTCGTTGTGAACATCAACATGTGTCCACCAAATAAATCTTGTATTGCGTTT +GTTATTAATACACCAGGAACAATAGGCATGACTGCCGCAATGATAATAGTTGCCAAGTCACCTGTTGGAATAAGTGTATG +TCCAATAACGGCGATAATCCCAATAACTAATGAACCAATGAATTCTGGGATAAACTGTGCGTGTAACTTACGATCTAAAA +TCTCAGTGACTAGGTATCCTAGACTACCTGCTAATATCGCAGTTAAAACATCAATCAATCTACCACCTTGTAAATATAAG +AAACTCATTGCAATCATTGCTGCAGCAAAACCTTTAAAGGGAAGACTGCTGTCACGCTTAGCAACATATATTTTTTCAAG +TTGCGTTTTTGCTTCGGCTAAAGAAATTTCATTGTTTGTAATTTGACGCGAAATTTTATTAGCTTGAGAAATTTTTATTA +AGTTTGTATCTCGAGAGGTAATTCTAAATATTCTAGGAAACGATTCCGAATGTAACGTAAACTGGATGACAGTGTTTGTA +ACAAAGCTGTTACTTTCACTGTAACCAAGTTTTTTTGCAATACGTGTCATGGTATCTTCTACACGCGTACCTTCTGCACC +AGATTCTAATAGTATGCGAGCAGCAAGCATGACAACGTCTTTGATAAGTACCTCTTGTTTGTATTCTTCTGAATTTATGT +CCATTTCATCACCATTGTTTATAAGAATTTAATACTCATTATAGTTTATACACTATAAAATAACCACATGAGCTTTTTGA +TAAGTTGTTATTGATTTTAAAAAACTAATATTTGATACTATTGTAATAATAATCATCGGAATTTTTAGTAGTTAAAATTC +TCTTGAAGTATAAAAAGGAAAAATTATTGAAAAAAATTCGACAAAAATATGTTTCAAATATTTCAAATAGGTTAAAATAA +TATTAGTTAACCATTACAAAAATTGTATAGAGTAGCGACTGTATAATTTCTATTGAGGTTAACGTTTATATGTAGTGATA +GTAGTTAAAGTTCTCCCAAGGAAGACTACTCGGGTACACTTTGCTATGAGCAAAGTGTACTTTGTTATTGATAATACATT +AGCACATATATATAAGTTTAAACATACAGATTTCAACTATTTACCAAATCACGTCTTATGTATACGGGAATATACTGAAG +ATAAACGAAAAATTCCAAGCTTAAACCGATAAGCTTGGAATTTTTTTATTAATTTATAAACGTACCAATGTATTAAGAAA +TCGCAAAGAATTGATCGAATTCGTTTGTGTTAATAATATGTCCTACAAAGAAACTACCGAATTCACCGTATCGTGCTGTT +GTTTCATCAAAGCGCATTTCGTATACAATTTTTTTGAATTGTAATACGTCATCTGAGAACAATGTTACGCCCCATTCGAA +ATCATCAAACCCTACAGAACCAGTAATAAATTGTTTGATTTTGCCAGCATATTTTCTACCAATCATACCATGGTCATACA +TTAATTTTTGGCGTTCTTCCATAGTTAACATGTACCAGTTATAAGTTTCATTACGACGTTTGTTCATTGGATAGAAACAA +ATATAATCAGAATGTGGTAATTCTGGGTATAATCTTGCTTTGATATGAGGGTTCTCATAAGGATCTTCATCAGATTTACC +AGCTAAATAATTGCTCAATTCAATGACTGATACATATGAATATGTAGGGATTAGGAAGTCAGCAATGCGCAATTTGTTAA +ATTCATTTTCAATATGATTTAAAGACTTCATTTCAGGACGTAAGAACCATAATAACAAATCTGCTTTTTGACCAGTTATA +TTATAAATAGCTTGATCACCAGATTTTGATGATCTTACAGTTGCTGTATTTTCTAAAAATGATTGAAATTCAGTGACAAG +TGCATCGCGTTCGTCCTTTGGAACTATACGTAATGATGCCCAATCAACTGCATAAAATAAATGTAGACTATACCAACCAT +CTAATGTTTCGGCTGCTTGACTCATGTTTATCGCTCCTTTTCAAAAATCATCAGTTCTTTATAAATTTTATCATAAAGCA +AAGGGGTAACGATAAAATTATGATTACAAATTGGTGACGTGGCATTATGAAATAAAATGGCGTATAATTATACCGTGAAT +GATTAATAAGATTTATATTACAGGAGGACATTATGGCTGATTTATTAAATGTATTAAAAGACAAACTTTCTGGTAAAAAC +GTTAAAATCGTATTACCTGAAGGAGAGGACGAACGTGTTCTAACAGCTGCAACACAATTACAAGCAACAGATTATGTTAC +ACCAATCGTGTTAGGTGATGAGACTAAGGTTCAATCTTTAGCGCAAAAACTTGATCTTGATATTTCTAATATTGAATTAA +TTAATCCTGCGACAAGTGAATTGAAAGCTGAATTAGTTCAATCATTTGTTGAACGACGTAAAGGTAAAGCGACTGAAGAA +CAAGCACAAGAATTATTAAACAATGTGAACTACTTCGGTACAATGCTTGTTTATGCTGGTAAAGCAGATGGTTTAGTTAG +TGGTGCAGCACATTCAACAGGCGACACTGTGCGTCCAGCTTTACAAATCATCAAAACGAAACCAGGTGTATCAAGAACAT +CAGGTATCTTCTTTATGATTAAAGGTGATGAACAATACATCTTTGGTGATTGTGCAATCAATCCAGAACTTGATTCACAA +GGACTTGCAGAAATTGCAGTAGAAAGTGCAAAATCAGCATTAAGCTTTGGCATGGATCCAAAAGTTGCAATGTTAAGCTT +TTCAACAAAAGGGTCTGCTAAATCAGACGACGTGACAAAAGTTCAAGAAGCTGTCAAATTAGCACAACAAAAAGCTGAAG +AAGAAAAATTAGAAGCAATCATTGATGGCGAATTCCAATTTGATGCTGCGATTGTACCAGGTGTTGCTGAGAAAAAAGCG +CCAGGTGCTAAATTACAAGGTGATGCAAATGTCTTTGTATTCCCAAGTTTAGAAGCTGGTAATATTGGTTACAAAATTGC +ACAACGTTTAGGTGGATATGATGCAGTTGGTCCAGTATTACAAGGTTTAAATTCTCCAGTAAATGACTTATCACGTGGCT +GCTCAATTGAAGATGTATACAATCTTTCAATTATTACAGCAGCGCAAGCCTTACAATAACGATGGATTTAGCGAGTAAAT +ATTTTAATGGCGTCAACTGGCGATATATCGATCATTCTTCTGGATTAGAACCTATGCAATCTTTCGCATTCGATGATACA +TTTTGCGAAAGTGTGGGCAAAGATATATCAGATAATGTTGTGCGTACTTGGATTCATCAACATACTGTTATTCTTGGTAT +TCATGATTCAAGATTGCCGTTTTTAAAAGATGGCATTGATTATTTAACGAATGAGATTGGTTATAATGCCATTGTTAGAA +ATTCTGGTGGCTTAGGTGTCGTTCTAGATCAAGGTGTATTAAATATATCGCTGATGTTCAAAGGACAAACAGAAACAACG +ATTGATGAAGCGTTTACTGTGATGTACCTCTTAATTAGCAAAATGTTCGAAAATGAGAATGTTGATATTGATACGATGGA +AATTGAACATTCTTATTGCCCAGGAAAATTTGACTTAAGTATCGATGGTAAGAAATTTGCAGGCATATCGCAACGAAGAG +TTAGAGGCGGTATTGCTGTACAAATTTATCTTTGTGTTGAAGGCTCTGGTTCAGAACGTGCATTGATGATGCAAACATTT +TATGAACATGCTTTAAAAGGTGAAGTGACTAAATTTAAATATCCTGAAATTGAACCATCTTGTATGGCCTCATTAGAGAC +ATTGCTTAACAAAACGATTACTGTTCAAGATGTAATGTTTTTACTATTATATGCAATCAAAGATCTTGGCGGTGTATTAA +ATATGACGCCAATTACTCAAGAAGAATGGCAAAGATACGATACGTATTTTGATAAAATGATTGAAAGAAACAAGAAAATG +ATAGATCAAATGCAATAGTTAAATATTTAACAGCCCCTTTCAAGTTATCGTTTGGGGCTGTTATTTTTGAAAGTATCCAT +AAAAAGCATAAATATGTTATTGACCTAATATTTAAATTGAACATCATTAAATTAAAAGTAATGATATGATGGAAATAATA +AATTTAATAAGAAAATTTCATGTTTTACGTATTAAATAGATTGAGAATAGGGTATATATAATGCGTATGACTATGAAAGA +AACGACCTTCTATTGAAATGTTCTAATACTTTAGTAGTAAATCTATATATTCCAATTATTATTTGTAGTATTATAGTTAG +GGTGGAAAATTATAAATTGTAGGGCTATTTTATAAAAAGATAAAGAATCTGAAATATCACTCGATAAGATTTAAACTTTG +AAGTAAACGAAAGTTAGTTTTATACTAATCAAGTTAAGTCAAATATAAATTCAAAATAAAGATTATACATAATATGAATA +ATGTGAGATAAAGACTGCAATGAAATTTAACGCGAATGCGTATGCAACTTAGAATATGATGTCTTTTACTAATAAATAAA +AATAATGAATCAAGTATAGGATAAATATAAAAGGTGATATTGACATGACAAGAAAAGGATATGGGGAATCGACAGGTAAG +ATTATTTTAATAGGAGAACATGCTGTTACATTTGGAGAGCCTGCTATTGCAGTACCGTTTAACGCAGGTAAAATCAAAGT +TTTAATAGAAGCCTTAGAGAGCGGGAACTATTCGTCTATTAAAAGCGATGTTTACGATGGTATGTTATATGATGCGCCTG +ACCATCTTAAGTCTTTGGTGAACCGTTTTGTAGAATTAAATAATATTACAGAGCCGCTAGCAGTAACGATCCAAACGAAT +TTACCACCATCACGTGGATTAGGATCGAGTGCAGCTGTCGCGGTTGCTTTTGTTCGTGCAAGTTATGATTTTTTAGGGAA +ATCATTAACGAAAGAAGAACTCATTGAAAAGGCTAATTGGGCAGAGCAAATTGCACATGGTAAACCAAGTGGTATTGATA +CGCAAACGATTGTATCAGGCAAACCAGTTTGGTTCCAAAAAGGTCATGCTGAAACGTTGAAAACGTTAAGTTTAGACGGC +TATATGGTTGTTATAGATACTGGTGTGAAAGGTTCAACAAGACAAGCAGTAGAAGATGTTCATAAACTTTGTGAGGACCC +TCAGTACATGTCACATGTAAAACATATCGGTAAGTTAGTTTTACGTGCGAGTGATGTGATTGAACATCATAACTTTGAAG +CCTTAGCGGATATTTTTAATGAATGTCATGCGGATTTAAAGGCGTTGACAGTTAGTCATGATAAAATAGAACAATTAATG +AAAATTGGTAAAGAAAATGGTGCGATTGCTGGAAAACTTACTGGCGCTGGTCGTGGTGGAAGTATGTTATTGCTTGCCAA +AGATTTACCAACAGCGAAAAATATTGTAAAAGCTGTAGAAAAAGCTGGTGCAGCACATACTTGGATTGAGAATTTAGGAG +GTTAATGCGTTGATTAAAAGTGGCAAAGCACGTGCACATACGAATATTGCACTTATAAAATATTGGGGTAAAAAAGATGA +AGCACTAATCATTCCAATGAATAATAGCATATCTGTTACATTAGAAAAATTTTACACTGAAACGAAAGTCACTTTTAACG +ACCAGTTAACACAGGATCAATTTTGGTTGAATGGTGAAAAGGTTAGTGGCAAAGAATTAGAGAAAATTTCAAAATATATG +GATATTGTCAGAAATAGAGCTGGCATCGATTGGTATGCAGAAATTGAAAGCGACAATTTTGTACCAACAGCAGCAGGGTT +GGCTTCATCGGCAAGCGCATATGCAGCTTTAGCAGCAGCTTGTAATCAAGCGCTAGACATGCAGCTGTCAGATAAGGATT +TATCGAGATTGGCGCGAATTGGTTCGGGTTCTGCGTCGCGTAGTATTTATGGTGGATTTGCAGAATGGGAAAAAGGGTAT +AGTGATGAGACGTCATATGCCGTTCCACTTGAATCGAATCATTTTGAAGATGACCTTGCCATGATATTTGTTGTGATTAA +TCAACATTCTAAAAAGGTACCTAGTCGATATGGTATGTCATTGACACGAAACACATCAAGGTTTTATCAATATTGGTTAG +ATCATATTGATGAAGATTTAGCTGAAGCAAAAGCAGCGATTCAAGACAAAGATTTTAAACGCCTTGGTGAAGTAATTGAA +GAAAATGGTTTGCGTATGCATGCCACGAATCTAGGATCAACACCGCCGTTCACATATCTTGTGCAAGAAAGTTATGATGT +CATGGCGCTTGTTCACGAATGCCGAGAAGCGGGGTATCCGTGTTATTTTACAATGGATGCGGGACCTAATGTGAAAATAC +TTGTAGAAAAGAAAAACAAGCAACAGATTATAGATAAATTATTAACACAGTTTGATAATAACCAAATTATTGATAGTGAC +ATTATTGCCACAGGAATTGAAATAATTGAGTAAGGAAGAGATAAAATGATTCAGGTCAAAGCACCCGGAAAACTTTATAT +TGCTGGAGAATATGCTGTAACAGAACCAGGATATAAATCTGTACTTATTGCGTTAGATCGTTTTGTAACTGCTACTATTG +AAGAAGCAGACCAATATAAAGGTACCATTCATTCAAAAGCATTACATCATAACCCAGTTACATTTAGTAGAGATGAAGAT +AGTATTGTCATTTCAGATCCACATGCAGCAAAACAATTAAATTATGTGGTCACAGCTATTGAAATATTTGAACAATACGC +GAAAAGTTGCGATATAGCGATGAAGCATTTTCATCTGACTATTGATAGTAATTTAGATGATTCAAATGGTCATAAATATG +GATTAGGTTCAAGTGCAGCAGTACTTGTGTCAGTTATAAAAGTATTAAATGAATTTTATGATATGAAGTTATCTAATTTA +TACATTTATAAACTAGCAGTGATTGCAAATATGAAGTTACAAAGTTTAAGTTCATGCGGAGATATTGCTGTGAGTGTATA +TAGTGGATGGCTAGCGTATAGTACTTTTGATCATGAATGGGTTAAGCATCAAATTGAAGATACTACGGTTGAAGAAGTTT +TAATCAAAAACTGGCCTGGATTGCACATCGAACCATTACAAGCACCTGAAAATATGGAAGTACTTATCGGTTGGACTGGC +TCACCGGCGTCATCACCACACTTTGTTAGCGAAGTGAAACGTTTGAAATCAGATCCTTCATTTTACGGTGACTTCTTAGA +AGATTCACATCGTTGTGTTGAAAAACTTATTCATGCTTTTAAAACAAATAACATTAAAGGTGTGCAAAAGATGGTGCGTC +AGAATCGTACAATTATTCAACGTATGGATAAAGAAGCTACAGTTGATATAGAAACTGAAAAGCTAAAATATTTGTGTGAT +ATTGCTGAAAAGTATCACGGCGCATCTAAAACATCAGGCGCTGGTGGTGGAGACTGTGGTATTACAATTATCAATAAAGA +TGTAGATAAAGAAAAAATTTATGATGAATGGACAAAACATGGTATTAAACCATTAAAATTTAATATTTATCATGGGCAAT +AAATGATTTGATAGGGGAGCAATCATCTAATTTAATTAGAAGATAAATGCTCCATTTTTTATTGTCAGCCGTGTTACGTA +TCTTGAAAAAAATATTAAAAAGAGTAAAATAGATAGAGTTAACGAAAAAATTAATGAAATCGACGATATAGATATGATAA +AGAAAGGTGGGTAGCAATATGAAAAATACATTCCTTATTTGTGATGAATGTCAGGCAGTCAATATAAGAACGTTACAAAA +GAAGTTGGAAAAATTAGATCCCGATGCTGAAATCGTGATAGGTTGTCAATCTTATTGTGGACCTGGACGCCGAAAAACAT +TCACTTTTGTTAATAACCGCCCACTGGCTGCGCTTACTGAAGAAGAATTAATCGAAAAAGTTTCTCAACAATTAAAGAAA +CCACGTGATCCTGAAGAAGAAGAGCGTTTAAGAAAACGACATGAAGAACGTAAACGTCGTAAAGAAGAACAAGATAGAAA +GCTTAAAGAAAAATTAGAAAAGCGAAAAGCACAACAATAAAGCCTGATGGCAGCATCATTCAATGCGTGCCACCAGGTTT +TTATGTTTTGTCTAGAAATTAAATAAATCATTAAATGATTCGGCCATCGTAGGATGCGTATAAATATTATCTCGTAATAC +GGTATATGGAATGTTTTGATCAATCGCAAGTTTAATTATATTAATTAATTCTTCAGATTGCTTACCATATAATGTAGCAC +CTAAAATCATATTATTTTCATTATTAATGACTACTTTAAATAAACCTCTTGGATCATTGTTAATTTTATGACGAGGTATA +GCACTTACTAAAAGTTGATGTTCAGTGTAATCATAATGTTGAGCGGCAGCTTCTTTACTAGTTAATCCAACACGTGATAA +TGGTGGATCTATAAATACTGTATAAGGCACGCTGCCTCTATTGTCAGTCGTACGTGACTGATTACCATATAACGCTGATT +TGATAATTCGATAATCATCTAAAGATATATACGTAAATTGAAGTCCGCCTTTAACATCACCTGCAGCATAAATATGCGGC +ACAGTTGTTTGAAGATGAGCATTGACTTTAATTTCGCCTCTGTCGCCTAATTCGATATCAGTATTTTCTAAAGCTAAATC +CGTATTCGGTTTGCGCCCGATAGCCAAAAGTACTGCATCAGCCTCAAAGTTACCAACGTTGGTATGGACTGTTGTATGAT +GATTGTCAGATGACAATTCAGTCGTTTCAACATTTGTATGCAATGCAATGCCTTTATTTTCTAAGTCAGTAATACCATAT +GCAACGACATCTTGATCTTCGCGTGGCATAAATGATTCGCCACGTTCTAATACTGTTACCTTACTACCTAAATTCGCAAA +CATTGAAGCAAATTCTAAGGCGATATAACCGCCACCTACAATAACGAGGTGCTTAGGTTGATAGCTAATGTTTAATAAAC +CTGTCGAATCGAAGACGTGTTTAGCTTGATCAAGGCCTTTAATGTTAGGAATGACAGAGGTAGCACCGGTATTAATAATG +ATATGAGGTGCAGTAATACTATCGACGATATCGTCATGTTGATCTAATAAATTCACTTCAGTATTAGATTTAAACTGCGC +TTTAAAATCCAGTACATCAATGTTGTTATCGTCTGCTAATAAGTGGTAATTTTTATTGTTTAGCGCATTGACAACATCGT +TTTTACGGTTATAACTTGCTTCAAAAGATTTGCCTTCTAATCCATCATGTACAAGTGTCTTCGAAGGTATACATCCTATG +TTTATACAAGTGCCTCCATACATTTTCGGAGATTGTTCGATAACTGCGACGTGTTGACCTGTTGATGCAGCGTATTTCGC +TAAAGTTTTACCAGCTTTCCCAAATCCTATTACAATTAAATCATATGTTTTCATGACATAAATCCTCCTTTTGAATGTCT +TCAATGACATCTTTGATTGTTTTTCCATTATAAAAATTAATAATGATATTCTGTTCGTCTTGCTGATAATGTGACATGGT +AGTTGCAATATTACGAGCAATTTGACAGTGACTGCCTTCGTCGCCAGTAAATAGACGTGTGTGGTGTTCTTTCTCTAAGA +CAAAATGTTTATATAATGTTGCTAGAGAGACATCAGCACTTTGATCATTTGCTAAATAACCGCCATCTTTACCTCGTATT +GTGTCAATCATTTTTAAATCGACAAGTTGAGTCGTCACGCGTCGTAATTGAACAGGATTTAAACAAGTTAATTCTGCTAA +TGAACTACTATTGAATTTTTCTGAATGATGCTTAGTTAAAAAAGCTAATACATGCACGGCAATGTTAAATTCTAAATTCA +ATGTTTCTCACCTCATAATTGTAACTGTATTAGTTACAATTAAAAATCATTTTGCTCAATGTGTCAATACTTATGTTTGA +AAAGTGTTGAATTAGGTGTGGTTATTGTGATGATTTATTGTTTGAAAGTTGATGCATTAACGGTGAGATTGCACAATTGA +AATTTTAATTATGTATGAACTGATAGATATGAAATGAATATTGAAAATGAGAGCAATAAAATTTTGAATGATGATGTTGT +GTGATTGTATAAACTTATTCGTATTTTAGATACGTTTTGATATTTCGCATGATGAGGAAATACTTTTAACAACTGATGAC +ATATTTTAATAGTGAATATATTTTGAGATATTAATATATTTAATGTTAAGGATGTTGTCGCAATAAAATTAAAATATTGA +AAGATAATTTTTATTGATTAATTGCGAATATACAAAGACACCATTATATCCATATGCTAAAATAAGAAAAAAGGCGACAT +GTTATCTCAATTTACCTGCATCTAGACTCCTTTACATAAGAAATAATCATCGCCGAGTTTTAAAATTTTACAACTTTGTA +GATGTAAAAAGCATTTGATTTTAAATAGTTTGAAAATCATATATTTTTGAAAAAGGAGCATGCCGATGAGTATTGACATG +TATTTAGACAGATCTCGAAACCAAGCTTCAAGTGTGGGGAATTTGAGTCAAACAATGAATTCAAATTATGATGCGTTGGA +AAAAGCAATTACTCAATTTATTAATGATGATGCGCTTAAAGGGAAAGCGTATACGTCAGCTAAGCAATTTTTTAGTACGG +TGTTAATTCCATTATCAACAAGTATGAAAACATTGAGTGATTTAACGAAGCAAGCTTGCGATAATTTTGTGTCACGTTAT +ACGAGTGAGGTTGATAGCATATCTTTAAAAGAATCAGAGCTTGAAGAAGATATCAGATCATTAAGTCAACAAATTACGCG +ATATGAAAATTTGAATAACAATTTGAAAAAGCATGCTTCCGATAATCAGCAAGCCATTTCATCGAACCAACAAATAATAC +GAACATTAGGTCAACAAAAACATGAATTAGAAGAGAAGCTACGCAAATTGCGTGAGTTTAATCAAAAATCACCAGAAATA +TTTAAAGAAGTTGAAGAATTTCAAAAAATTGTCCAACAAGGACTTACCCAAGCGCAGAATTTTTGGAACTTTTCAACAAA +TCAATTTAATATTCCTTCAGGTAAAGAACTTGATTGGGCCAAAGCAAGTCATGAAAAATATTTGAAAGTTGCTATGGGGA +AAATTGAACATAAAGCAGAGAAAGAAACTTTAAATAAAGCAGACTTTGCTGTTATAAAGGCATATGCCAAAGAACATCCA +GAAGACGATATCCCGAAAAGTATATTGAAATATATAAATGACAATAAAGACAGTATTAAAAGAGATATAGGATTAGATAT +TACGTCAACACTTTTAGAGCAAGACGGTATAAATGCAAGTAAATTCGGTGTATTTATCAATACAGCAGGTGGAGTGAAAG +GCCCAGCAGGTCCAAATTCATTTGTGGAAGTCAAACGTACATCAGGTAATGTGTTTATAGAAAATGGTAGTAAATTTGCA +AAAGGCGGAAAATACCTAGGTAAAGGTGTTGCTGGTGTAGGATTTGGTATAGGTATGTATGATGACCTTGCAAATGATGA +TAAAACATTTGGAGAGGCGTTGTCGCATAATGGTATGACGCTTGCAGCTGGATCTGCAGGGACAGCAGTTGGAGCTGGAT +TAGCGACTTTTGTTTTAGGAAGTAATCCAGTAGGATGGGTGATTTTAGCAGGTTTGGCTATGAGTACAGTATTTGCATTA +GGAACAGATTTAATTTATCAAAATAATATTTTTGGATTAAAAGATAAAGTAGACTGGGTAGGGCATAAAATCGATAATAG +TATAGATGTTGTTAAAAAAACTACAGAAAAATCCATGGATAGTGTTGGGAATGCTGTTAGTGAAGCTAAAAATATTATTA +GCAATCATATAAATCCAATGAAATGGGCGTGGTGAGTTTTGCTTTTTAATATTAAGGTTATTAATAAAAATCCGAGATAT +AAAGTCGTTCAATATAATGATGAGTATTTGTTAATTGATTTAGTAAGCACTTGGCTTGTATACTTTTTTCCTTTTATTAA +TTGGTTCATTCCCAAAAGGTATGCAAAACTTAGTGAGAAGGAACTTGAAAATTTAAATGTTGATAAACAAAATAAAAATA +ATATTTTCTGGCCAGTTACTGGTAGTTCGTTTTTATTTGTAGTTATTTTACGGAAATATGTACATACGTTTGAAGTTCAA +TTAGATAATAAGATTTTAATATCTTTATGTTTTATAGGATTTATAGGTATTGCAGCATTTTACATTTACTTAAATAAAAA +GCTTAAACTAAAGATATATGATGATAATCTAGATAATGAAAATAGGGTTATATTAGTGCCTACTTTTAAAGATGGAAGTT +TTATAGTTTTCACATATCTATTATTAGGAGGTTGTTCTATATTATTTTTAATATGGCTAATGACTATAAAACCTCAAAAT +CTACTGGTATTTATTATGTGGATTATTATTACAATTTTTTTCTTTCTAATAAGCATGGGCTCAATTAGTAATAAAAAGGT +TTATGCCAAACTTAAAAAGCAATAGAAATTACTAAACTTAGATTGTAGTTCGTAAGTTAAGTAATAAACAGGAAAATAGT +ATGTTACTAAAATACAAAACACTTATAAAGTATCTGAAAGTGTATTAGATATAAAACCAACGAGACGGAAGTGGAGATAT +TGCTTTGCGAGGTTAGAGTTGTAAACAAAAATCCCAGATATAGAATTATTAAATATAAAAACGACTATCTGATGATTGAT +TTAGTAAGCACCTGGTTAGTTTTGTTCTTTCCGTTTATTAATTGGTTAATACCAAAAAAATATGTCCAAATCAGCAGAGA +AGATTTTGATAATTTAAATATTGTCAAACCAGTTAAAAATAAAGCTTTATGGCCAGCTATAGGTAGCATACTTTTATTTG +GCACTATGTTTAGAGATAAAATATATATACCTGATTCTCATTTAGAAAAAAATTGCGTTATTATCATTTGTTCGGTTTTA +TTATTAAGTATTCTAGTGTTTTATATATATTTAAATCAGAAAGTAAAGTTATCTATCTATAATGATCGGAGTAGCAATGG +AAAAATTATGATTTTTCCATCATTCAAGAATCTTTGCTTTGTACTATTTTCTTATTTTTTCTGTGGTGGATTATCAATCA +TGTTCTTAGATGTTTTGATTAGTTTATCTATCCAAAATATTATAGTATTTATTGCTTGGGTTATTATGACAATGCTATTT +TTCTTTATAAATATGTCTTCAATAATAGACAAAAAGATTCATGTTATATATTTAAGGTCATATAAATATTAATTTTGATA +CAAAAGGGCACAAGTGTTTTGAGAAACAGAAACGATAATATAGCTGTGTGAATTTGTTATTAATAGGAAGTGATAATTAT +TGCTTTGTGAAACCGAAATTATCAATAAAAATCCGAAATACAGAGTTATTAAGTATGATGATGAGTATTTAATGGTCGAT +GTAATAAGAACTTGGCTTGTATACTTTTTTCCATTTATTAATTGGTTCATTCCAAAAAGGTGCGCCAAAATTAGTAGAGA +AGAGTATGAAAAACTGAACACTGTTAAGCCAGTTAAAAATAAAAATTTTTGGCCTGTTGTAGGAGGAACAATCCTACTAG +GTGCTACTTCCAGAAAGTACATACACTTACTTAATATTCAATTAGAAAAAAGATCGGTTATATTTATATGCTTCGTTGTA +TTTCTATGTATTTTAATCTTTTTTGTGCTTCTGAATCGAAAATTAAAATTGAAAGTTTTCGATAATAAAAAAGAAGAACA +AAAAATAATTTTAGTACCAACATTAAAAAATGCTGTTCTTATATTATATGGCTATTTGTTGATTGGAGGCATGTCAATAT +TAGCTTTAAGCATGTTGCTGACATTAGAAAACCAGAATTTAATAACTTTTATAGCTTGGGGTATGGGGTTAATGTTGTTT +TTCTTGATGAATATAACACTCATTGTCAATAAAACAGTCAAAGTTATAAAAAGGTGAATAAATAACTAATTATATTGTAT +TGGCCGTTCTTCAAATAAAAAATAATTTGAATAAGGTGGAAGTGATAAACAGTGCTTTGCGAATCTAGAGTCATTAATCA +AAACCCTAAATATAGAATTATTAAATACAATAATGAATATTTCATGGTTGATTTAGTAAGTACTTGGATTGCTTATTTTT +TGCCCATGATTAATTGGTTTATTCCCAAAAAGTACGCGAAAATTAGTAGAGAAGAATTTGAAAGTTTAAATATTGTCAAA +CCCGCTAAAAATAATACTTTCTGGCCTGTTGCAGGATTTGCAGTGTTATTAACAACCTTAACAAGAAAATATATCTATTT +GCTTAACATCCATTTAGAAAAAGAAATAGTTATATTAACATGCTGTATGATACTTCTAGGTGTTTTCGCATTGTTTATAT +ATATAAATACAAAATTGAAGTTACATATTTTTGATAAAAATAAAAGTAATAACGAAAAGATCATATTAATACCTACATTT +AAAAATATTTGTTTATCCTTATTTGCTTATATATTATTTGGTGGATTGTCAACAATGGCTCTGAGTATGTTAGTAACTTC +ATCCCCTCAAAATATAATAGAATTTCTTGCTTTAATTGGCATGACTGCATGCTTCTTTCTACTGAATATGTCATCGGTTC +TAGATAAAAAAATTCATGTTATTTTAAAAACAAATAAGTAGTAAAATTGATTAACTTAGGTAGTATCGGATACTTAAATG +TTGGTTCATAAAAAGCAATGATTTTAAATCGAGGAGCTATCTTAGAACAGGGAAATAAAACAGCCAAAGTTATAAAAAGT +GAATTAATAACTAATTATATTATGTTAGCCACGCTTCAAATAAAAAATAATTAGAATAAGGTGGGATTGATAATCAATGC +TATGCGAATCTAAAATCATCAATAAAAACCCCAAATATAGAATTATTAAATATAATGATGAGTACTTAATGATCGATATA +ATAAGCACTTGGATTAGTTTGTTTTTTCCTTTTATTAATTGGTTCATCCCCAAAAGATACGTCAAAATTAGTAGAGAAGA +GTTTGAAAATTTAAATATTGTGAAACCTGCTAAAAAGAATGTTTTTTGGCCAGTTGCAGGTATCTCTACTTTATTCGCAG +TTACATTAAGAAAGTATACACATTTACTTGACACTCAACTTGATAGAAAATTAGTTATTGCTATATGTTGTATCACATTT +ATAGGGATTTTAACATTTTATGTACGCCTAATTAAAAAATCATCTTTAAATATTTATAATACTAAAAATAAAAGGTCAAA +AATTATCTTAATACCAACACTAAAAAATTTTTGTTTAACATTGTTTAGATATGCTTTTTTTATTTTATGGACAGTGATTT +TCTCATATGCTCTATTATCAATGAGTTATCAAAACATAATAGTATATTTTGCTTGGATTACAGCAATAATGGGTTTTTTT +CGTAGTGAATATAGCTTTAATTATAGATAAAAACATTCATGTCATACTTAAAAATTAAGTGAAAGTACTAAATTCAGATT +AAAAATATGAAAATATCAGTTAATAAAACCTTTGATGACAGTAACTTGATAGTATTTGAATGTCTTTTGAAGTATCGAAA +AATGTATTACACATAAAATTAAAGGGATGTGGAGACATTGCTTTGCGAATCTAAAGTTATTAATAAAAACCCTAAATATC +GAATAATCAAATACGATAGTGAATATTTAATGATTGATTTGGCAAGTAATTGGATTGTCTTCTTCTTTCCATTTATTAAC +TGGCTCATACCGAAAACATATGTCAAAATCACTAAGAATGATTATGAAAAATTAAATATTGTCAAACCAGTTAAAAATAA +ATCGATAGGATGGACCATATTCGCGGGTATTGTGTTACTTGGTGGTACTGTAAGAAGAAATACTTATTTATTTGATTTTC +AATTAGAAGAACTAATTGTTTGGAGCAGCTGTTTCATTGGGTTTTTAGAGATTATATTTTTTTATTGTTATCTAAATAAG +AAATTAACATTAAATATTTATAATGAAAGTAAAAATAATGAACTTAAATTAAGATTATTACCCTCCTTTAAAAATATTTG +TTTCACAATTTTTTATTACCTATTTACTGGTTTCATGTCTTATGGGGCATTTTACTTGTTGGTATTTGAAAATGTGCAAA +ATTTAATCTTATATGTTTCTTGGCTTTTCATGACTATGCTATTTATGTTTATGAATATGCATTCAATTATAGATAAAAAA +GTACATATATTCTTAAAGTCTAATAAATAGTTACAAATTTAGTTAGTTTTCAATTGTTAATTAGGGGTGGTAAACAGTGC +TTTGTGAATCTAGACAAATTTATAAAAATCCTAAATATCGAGTTATTAGATATAATAATGAATATTTCATGGTCGATTTA +GTAAGTACTTGGATTACTTATTTTTTCCCTATGATTAATTGGTTTTTGCCCAAAAAATACGCAAAAATTAGCGAAAATGA +ATTTGAAAGGTTAAATATAGTCGAGCCTGTTAAAAATAATGTTTTTTGGCCGGTTGCAGGAAGTTCAGTTCTATTTGGAA +TTATATTGAGAAAGTACGGTAACTTCTTTAATGTTCAGTTTGAAAAACAACTAGCAATCACTGTATTTTTTATCATGTTA +ATAGGGATGTTAATTTTTTATTTTTATCTAAATAAAAAATTAACATTAAAAATTTTTAATACCAACGTGGTTAATAAGAA +TAGAGTTGTATTAATACCGACTTTCAAACAAGGATTGTTAATAGTTTTTGCGTACTTTTTTTAGGAAGCGCTTCAATATT +TACTTTAACCATTCTTTTGACGACAGAGTCACAAAATATAATAATATTTTTAACTTGGGTTATTATCACGATGTTTTTCT +TTTTAGTGAATATGGCTTCGATAGGTAATAAAAATGTTCATGTTATTTTAAGAATAAACGAGTAGTAAAAATGATCGAAA +TTGGTAGTATCGCATATTTAAACGGTGGTTCAAAAAAATATAATCATATCTTAAATCAGGAAAATAGATAGTAATGTTTG +AATATACCAAGTGAAAATGCCCATTGTGATTGATAAAGGAAAAAACTGTTTTAATTTAAAAATGAAATATAGAGCGTGTT +ATTTTGTATGATAGCTCTTATAACATTGGCGTTTTTAATCCAATAGTATGGACGGTTTTAGCTGGTTTTGTTGCGGAATC +TGTTTAGACAATTCTTAAAAATTGGACTTTTCATACTAATAAATTTGGATTAAGAGATAAAACTAATTGGATAAGACATT +AAATAGACGCTGTTTAAAAATACCACAGAAAAAGCTGTAGATAGTGTTGGAACGATAGTTGGTGAAGGTACAAAAAGTAT +TGGGGATCATTTAAAACTAATGAAACGGGAGTGGTAAATGTTGCTTTGCGATGTCAGAGTCATTTATAAAAATCCGAAAT +ACAAAGTCATTCAACATAACGGTGAATACTTATTAGTCGATTTAGTAAGCACTTGGTTCGTGTACTTTTTTCCTTTCATT +AATTGGTTCATTCCAAAAAAGTACGCGATAATTAGCGAAGAAGAATTTGAAAATTTAAATGTTGTTAAACCAAATAAAAA +TAATGTTTTCTGGTCAGTTATAGGAAGTTCGGTTTTGTTTGGAGTTACTTTAAGGAAATACATACATGTTTTTGATGTTC +AATTAGATAAGCTAGTTGTAATGATATTGTGTGCTCTCGCTTTAATTTGTGTTATAGTTTTTTATTTTAACTTAAATAGA +AAGCTTAAGTTAAAAGTGTTTGATACAAATATTGAAAAAAATAAGAGAGTTATATTAATACCAACGTTTAAACTTGGCTG +TTTTTTAGTTTTCGGATATATTTTCGCTGGAAGTTTTTCAATATTTTCATTAATTGCCCTTATGACAATCGAACCTCAAA +ATATAATAATATTTATTTATTGGATTATGATGACAATGCTTTTCTTTTTGTTAAATATGACTTCGATAGGTAATGAAAAA +GTTCGCGTTATAATGAAAAATAATTGATTACATTTAAAATATTCTAAATGTTGTCGACACAATCCTTTTAAGACGCTAGT +AGAATTTAAATGACTTCTAATGTATATGAAAGTGTATCAATATAAAACCAATTGAAAAGAAGTGGAGACATTGCTTTGTG +AAACTGAAAATATTAATAAGAATCCCAAATATAGAATTATCAAATACAAAGATGAATATTTGATGATTGATTTAGTAAGT +ACATGGTTAGCACTCTTTTTCCCAATGATTAATTGGCTGATTCCAAAAAAGTACGTCAAAATCAGCGAAAAAGATTTTGA +AACTTTAAACATTGTGAAGACAGCTAAAATCAATTCTTTTTGGCCAGTGGCAGGAAGTACGGTCTTATTTGGTGTTATGT +TAAGAAGGTATTCCCATTTATTTATCGTTAAATATGAATATAGTATAGTAATTTTAATTTGTTGCATCATAATACTAGGT +ATTTTTCTGTTTTTTTTATATTTAAATCAAAAGTTAAAGTTACAAATCTATAATGAAAACAAAAATAAAAGCAATAAGAT +AATCATATTTCCCACTTTAAAGAGTCTTTGTTTATCAATAGTTTTATATATTTATTTAGGTGGTGGTTCATTCTTTACTA +TTTATATGTTATTGACGATTGAAGTTCAAAATATAATATTATTTATAACTTTGTTTGTAATATTTTTGTTTTTCTTATTT +TTAAATATGTGCTCACTATATGACAATAAAGTTCATGTATTATTTAAATCAAATGGAATTGAAAAGTTTTAATAGGAATA +ATAAACAAAGGAATGGTAGTGATAAATATTGCTATGTGAATCTAAAGTTATCAACAAAAATCCTAAGTATAGAGTTATTA +AATATGGTGATGAATATTTAATGATTGATTTAGTAAGTACCTGGTTAACTTTATTTCTTCCTATGATTAATTGGTTAATT +CCAAAAAAATATGTCAAAATCAGTAAAAAAGAATTTGACGATTTAAACATTGTCAAACCTGTTAAAAATAAAGCTTTTTG +GCCAGTTGCAGGTAGTACTATTTTGTTCGGAGTTACGTTTAGAAAATATATTCCTTCACTTAATATTCAATTAGAGAAAA +ACATGGTGATTGTAATATGTTGTGCAATATTTCTGGGTGTTTTAATACTTTTTTTATTTCTGAATCGTAAGCTAAGGTTG +GAAATTTATAATAATAACTCTAGTAAAGGGAAAATAATTTTATTTCCTTCATTAAAAAACTTTTGTTTCACAATATTTTA +TTATTTTTTATTTGGCGGTCTTTCAATAATGGCTCTAAGTATGTTATTAACTTTAAATCCTCAAAATATAATAGGCTTTA +TTGGTTGGTTGGTAATGACTGCAGGTTTCTTTCTGTTAAACATGTCATCGATTATTGACAAAAAAATTTATGTATTATCT +AAAACTAACACGGTGGAAAAATGATGGTTTAGCTGGATTTACTGCAGGTTCTATTTCGGCAATACTTGTATATTGGACCA +ATCAAAAAAATGAATTTGGAATAAAAGATAAAAACGATTGGATAGGACATAAACTAGACGTTGGTATAGATGCTGTAGAA +AAATCTGCAGAAAAAACAGTAGATGGTGTTGAAAATGTCATGGTGAAGCTTCAAAAAGTATTTCTAATCATATAAGCCCT +AAGAAATGGAGCTGGTAAATGTTGCTATGCGAATCTAAAATCATCAATAAAAACCCAAAATATAGAATTATTAAATATAA +TGATGAATACTTAATGGTCGATATAATAAGCACTTGGATTAGTTTATTTTTTCCTTTTATTAATTGGTTCATCCCAAAAG +AATACGTCAAAATTAGTAGAGAAGAGTTTGAAAACTTAAATATTGTTAAACCTGCTAAAAAGAATGTTTTTTGGCCAGTT +GCAGGTAGCTCTGCTTTGCTGGGAGTTGCATTAAGAAAATATACACATTTACTTGACATTCAACTTGATAAAAAATTAGT +TATTGCCATATGTTGCATCACATTTATAGGGATTTTAATATTTTATGTACGCCTAATTAAAAAATCATCTTTAAATATTT +ATAATACTAAAAATAAAAGGTCAAAAATTTTTTTAATACCTACACTAAAAAATGTTTGTTTCACATTATTTGGATATATT +TTATTTGGCGGATTGACTATGCTATTCCTAGATGCACTATTATCAATGAGTTATCAAAACATAATAGTATATTTTGTTTG +GATTGCAGTTATAATGGGTTTTTTTCTAGTTAATATAGCTTTAATTATAGATAAAAACATTCATGTCATACTTAAAAACC +AATAGAAATTACCAAATTAAATTTGCAATGGCTTTAATATTGTCGTTCTTAAATGTTTTTCAATTTCTGATTCAAAAGTA +CATGTAATTTAAAGGTCAACGAGCAATCGCGCTATATGCATATCAATTTAAGCAATACTTATTTTAACTGAGTTTTATAT +AACGTTTTCCTATTACACCGCTTAAATTCAAGCTCTAATTCTAACATTTGGAAGTTGTATTAAATCATGATAGTGTGATA +GTGGAAAAGTTATAAAGATAAGGAGCTGGGTCTAATGAAGCAATTTGTAAACTTAGGTAAATCTGATGTTGAAGTGTTTC +CAATCGCACTTGGGACGAACGCAGTAGGTGGGCATAATTTATATCCGAACTTAGATGAAGAACAAGGAAAAGATGTTGTT +CGTCAAGCCATTAATCATGGTATTAATTTATTAGATACGGCATATATTTATGGGCCAGAACGATCAGAAGAATTGGTTGG +AGAAGTTGTTAAAGAATATCCGCGAGAGCAAATTAAAATTGCTACGAAAGGGTCTCATGAATTTGATGAAAATCAAGAAG +TACATCAGAACAATCAACCGGAATATTTAAAACAACAAGTTGAGAATAGTTTGAAACGTCTACAAACTGATTATATCGAT +TTATATTATATTCATTTTCCGGATAACAACACTCCGAAAGATCAAGCAGTTGCAGCATTACAAGAGCTTAAGGAACAAGG +GAAGATTAAAGCAATTGGTGTATCAAATTTCACATTAGATCAACTTAAAGAAGCAAATAAAGATGGTTACGTTGATGTTG +TACAGTTAGAATATAATTTATTGCATCGCGAAAATGAGGCAGTATTACAATATTGTGTTGATCACCAAATCACATTTATT +CCATATTTCCCATTAGCATCCGGTATTTTAGCTGGAAAATATGATGAGAACACTAAATTTAGTGACCATCGTACTACACG +TCGTGATTTTAAACCAGGTGTATTTGAAGAAAATGTGCGTCGCGTAAAAGCTTTGGAAAGCATAGCTGCAGCACATCAAA +CTTCAATTGCGAACATTGTATTAGCATTTTATTTAACGAGACCAGCTATCGATGTGATTATTCCTGGTGCAAAACGTGCA +GAACAAGTCGTTGAAAATATTAAAGCTGCAGATATCGTTTTATCAGATGATGAGATTCAATATATCGATGAACTGTTTCC +GATTGAAGACTAAATGAATTGTATTTAATGGTGATATATGTATCTAATTTCATCATGAATTAGCCCCGTATCACAAACGA +CGTTATATAAGGCATGACGAAATTTCGTCAAATTTATATAAAAGTTGTAAGCGTTTTACTATTCGGTAGGCAAAGCTTAT +GATAGTATAATTAGGTAACGAAAAACACAGATATTTATTAATCAAAAGTGAGGTTAGGCTAAAATGATTACTGTTTTGTT +TGGTGGGAGTAGACCAAACGGTAATACTGCACAATTAACAAAATTCGCTTTGCAAGATTTAGAGTATCAATGGATTGACG +TGACACAACATCAGTTTAAACCGATACGTGACGTGAGACATACAGCAGAGACTATTACTTCATATGACGATGACTATTTG +CCGATTCTAGATAAAATATTGGCTAGTGATACAATTATTTTTGCATCACCAGTGTATTGGTATAGCATTTCAGCACCATT +GAAAGCATTTATCGAACATTGGTCAGAAACATTACAAGATAAACGATATCCTAATTTTAAGGCACAAATGGCCGAAAAGG +ATTTTAGAGTTATTTTAGTTGGTGGAGATTGTCCAAAAATAAAAGCGAAGCCAGCAATTACGCAAATGAAATATAGTTTA +GACTTTTTAGGTGCCACTTTAAATGGTTATATTATTGGAACTGCTGAAAAGCCTGGTGACATCATGAAAGACAACTATGC +CTTAGCACGTGCAACTGAGTGGAATAGTATATTGCAATAAGACATACCTCATACTAAACGAAGTGATTTTTTGCTTCGTT +ATTTTTTATTTTTAATTTTAACGGAAAATATAAATATATAAATTGAATAGACAATCATTGTTTATTAAGTATTGAGCAAG +TTGTGGTATAATGCCGTTATTAAAACTTGTTAATAAGGGGAGAAAGTCATGTTCAAGGTAAGACAAGCAACTGAAAAAGA +TGTTGTTCAAATTAGAGATGTCGCAACTAAAGCTTGGTTTAATACATACTTAAATATATACGCTGCGACAACAGTTAATC +ACTTGTTAGAAGCTTCATATAATGAACATCATTTAAAGAAAAGACTTCAAGAACAATTATTCTTAGTCGTTGAAGAAGGT +AATGACATCGTTGGCTTTGCTAACTTTATTTACGGTGAAGAATTATATTTATCAGCTCATTATGTTAAACCAGAATCGCA +ACATACAGGTTATGGTACAGCATTGTTAAATGAAGGATTATCACGTTTTGAAGATAAATTTGAAGGTGTTTACTTAGAAG +TAGATAATAAAAATGAAGAAGCAGTAGCTTACTATAAAGAGCAAGGTTTTACAATCTTACGCTCTTATGAGCCAGAAATG +TATGGCGAAAAGTTAGACTTAGCACTTATGTACAAAGCATTTTAAGTAAATTAAATATTATATTAAACGAAAAGGAGGGG +AATAATGACTAACGCATATGTAGATTTAAAATTAGTAGAAGAAAAAGTTTTTAAAGACCCGATACATCGATATATTCATG +TTGAAGATCAATTGATATGGGATTTAATTAAAACTAAGGAATTCCAAAGGTTACGTCGAATTAGACAACTAGGAACACTG +TACCTATCTTTTCACACAGCAGAACATAGTCGCTTTGGACATTCTTTAGGTGTGTATGAAATAGTTAGACGATTAATTGA +TGAGTCATTTATTGGTCATGATGCATGGGACAATAAAGATAGACCGTTGGCATTATGTGCTGCATTATTACATGATTTAG +GACATGGTCCATTTTCACATAGTTTTGAAAAAATATTTAATACAGACCATGAAGCATACACACAAGCGATTATTACTGGA +GATACTGAGGTGAATGCTGTATTACGTAAAGTGGCGCCTGAGTTTCCAAGAGAAGTTGCGGAAGTAATTAATAAAACGCA +TCATAATAAATTGGTCATTTCGATGATTTCGTCACAAATCGATGCGGATAGAATGGATTATTTACAACGTGATGCGTATT +TCACAGGTGTATCATATGGTGCTTTTGATATGGAGCGTATTTTAAGATTAATGCGACCTTCTAAAGATGAAGTACTAATC +AAAGAAAGTGGTATGCATGCAGTTGAAAACTTTATTATGAGTCGTTATCAAATGTATTGGCAAATTTACTTCCACCCAGT +TAGTCGTGGTGGAGAAGTGCTGCTTAATAATTGTTTGAAACGCGCAAAACAGCTTTATAATGAAGGCTATGAATTTAAGT +TGCATCCACATGATTTTATTCCATTTTTTGAAGAGACAGTTACGATTGAACAATATGTTGAACTCGATGAAGCGGTAGTT +ACGTATTATTTGGAAAAATGGACAAAAGAAGATGATGCTATTTTAAGTGATTTAGCAAGTCGATTTATTAATCGAGACTT +ATTTAAATATATTCCATTTGATGGCTCAATTATTACAATATCAGAACTGCAAGAACTGTTTGAAGCAGGTGGTATTAATC +CAGATTATTATTTTGTGAGTGAAGCATTTTCTGATTTGCCATATGACTATGATCGACCGGGGTCAAATCGCAAACCGATT +CATTTATTAAGACAAGATGGTACGATTAGAGAAATAAGCAATCAATCATTAGTCATTCATAGTATTACAGGCATTAATCG +CCAAGACTATAAATTATATTATCCTAGAGAAATGGTTGCAAAGATTAAAGATAAGACAATTAGAGAAGCTATTGAAAATT +TGATTAATGAGCTTAATTAAACAGGGCTAAAATTGTTATCGTTAAATATGGAGGTTATATCATTGTCTGAGAAAAAAGGC +TTTAATTTTAATATCATAAAAAATGACCCTCTAGATGGTCATAAAGGTACAAATATTGGTTCAATTAGCTTAGACAATAT +TGCACCAGTTTTTATCGATGTTGCTAACAAAGAAGCATTTATTGATATTGGAGGCATGCATGCTCGTGCCAAAGTTGAAA +AAGGTGTGAAATGGATTACTGATAAAGCTGCTGTTGAAGGCGATGAAGCTAAAGAATATTGGTTGTGTTGGGTAACAACA +GAACGTAATGAACAAGGACCATATTACGCTGGTTTAACAGCGTGCTATTTATTAGTGAATAAAGCAATTCGTCGTGGTTA +TAAAAGTATGCCTGAACATGTTAATATGATGGATAAATCAATGAAACATCATATTATCATAGATCAAATTGGTGACGAGA +ATAAAGCTATTTTAAAAGACTTTTTAATGAACCATGATGAAGGTATGTGGAAGCATTCTTCTGATGCTTTACATCAAGCA +TTTAATTAAATATTAGAAACTAAAATTTCCCAATTAATCTATAAAGATATGATTCATTTCTCAATGACGACGTAAATCGT +GAGGTGAAAATAATGTCTTAGATTGATTGGGAGTTTTTTTAATTTTTTTGAAATTAAATTAATCTGTAGTTAATAAAAAA +TTTGAATAACTGACACATTTTTTTGATCATAGCTATATACTTTGTGAATTAATTCACATTATAATAAGAGTGAAGATAAG +AGTATTATAAATTATCTTTAAATAAATATATGTGAAGTAAAAATTACACGTTAGCATATCGATTATGTCATTTCTTTTAA +CATATTAACTAGGGAAACGTTAAAAGTTAACGGTTGATATCTAACTAAAAACAAGGTCACAGTAGTATGTTTTAATCTGG +CGTCTATTACAAATAAAAATTACATCTATAATTATTCGTTTTCTTTTTTGAAAGTAATAGCCAATTAATATCATACATAC +TGGAGTGACTATAAGGAGGACATTATTATGAGAGCAGCAGTTGTAACGAAAGATCACAAAGTAAGTATTGAGGACAAAAA +GTTAAGAGCTTTAAAACCTGGTGAAGCGTTGGTACAAACGGAATATTGTGGCGTTTGTCATACCGATTTACATGTTAAGA +ATGCTGATTTTGGTGATGTTACAGGCGTTACTTTAGGTCATGAAGGTATTGGTAAAGTCATCGAAGTTGCGGAAGATGTA +GAATCATTAAAAATTGGAGACCGTGTGTCTATCGCTTGGATGTTCGAAAGCTGTGGAAGATGTGAATATTGTACAACAGG +TCGTGAAACACTTTGCCGTAGTGTGAAAAATGCTGGTTATACAGTAGATGGTGCAATGGCTGAACAAGTTATTGTTACTG +CAGACTATGCTGTGAAAGTACCTGAAAAATTAGATCCAGCAGCAGCGTCTTCTATTACATGCGCAGGTGTGACAACTTAT +AAAGCTGTAAAAGTAAGTAATGTAAAACCTGGACAATGGTTAGGTGTTTTTGGTATAGGTGGTTTAGGTAACCTAGCTTT +ACAATATGCTAAAAACGTTATGGGGGCTAAAATTGTTGCCTTCGACATCAATGATGATAAATTAGCATTCGCGAAAGAAT +TAGGTGCTGATGCTATTATTAATTCTAAAGATGTTGATCCAGTTGCAGAAGTTATGAAATTAACTGATAACAAAGGATTA +GATGCAACAGTGGTAACTTCAGTTGCTAAGACGCCATTTAACCAAGCGGTTGATGTTGTAAAAGCTGGTGCAAGAGTTGT +TGCCGTTGGTTTACCTGTTGATAAAATGAACTTAGATATCCCAAGATTAGTGCTTGATGGTATTGAAGTAGTAGGTTCAC +TTGTTGGTACAAGACAAGACTTACGTGAAGCGTTTGAATTTGCTGCTGAAAATAAAGTAACACCTAAAGTTCAATTAAGA +AAATTAGAAGAAATCAATGATATTTTTGAAGAAATGGAAAATGGTACTATAACTGGTAGAATGGTTATTAAATTTTAAAA +ATATCAACTGACTATATAGATAAAGAAGGTAGTGCTCTGAACACTATCATTATAATCAAACACGAGGTTTTCATGAAAGA +TAGTGAAAATCTCGTGTTTTTTGGTTTTGAGGTGTTGTTTGTATTTTATAAAATGGCTTACATATATGAAGCGTTGATTA +AGTATGGAATTGTTAATTAATTGAACCTATTTAGCTTTAAGAAGGCATAACAAGATGACCTTATTTTATGCTATAATATT +TCTATTATGCGAAGATTAAGGTGAGTAGTAAATTGGATAAAAAAGTAAGTATTCAAACAAAGCAAGTGTTGAAACAGCAC +AACGAAAAAGAAAAATTTGAATTTACTACTGAAGGAACTTGGCAACAAAGGCAATCTAACTTTATTCGGTATGTAGAACA +AATTGAGGATGCAACAGTTAATGTTACAATAAAAGTGGATGATGATAGCGTTAAGTTGATTCGTAAAGGCGACATTAATA +TGAATTTGCATTTTGTTGAAGGACAAACGACAACAACTTTTTACGATATATCGGCTGGACGAATTCCACTAGAAGTTAAA +ACATTACGCATTTTACATTTCGTAAGTGGAGACGGTGGCAAGCTAAAGATTCATTATGAATTATATCAAGATAATGAAAA +AATGGGTTCTTATCAATATGAAATTAACTATAAGGAGATAGGCGAATGAATATTATTGATCAAGTGAAACAAACATTAGT +AGAAGAAATTGCAGCAAGTATTAACAAAGCAGGATTAGCAGATGAGATTCCTGATATTAAAATTGAAGTTCCTAAAGATA +CAAAAAATGGAGATTATGCTACTAATATTGCGATGGTACTGACTAAGATTGCAAAGCGTAATCCTCGTGAAATTGCTCAA +GCGATTGTTGATAACTTAGATACTGAAAAAGCACATGTAAAACAAATTGACATTGCTGGTCCAGGATTCATTAATTTTTA +CTTAGATAATCAGTATTTAACAGCAATTATTCCTGAAGCAATTGAAAAAGGTGATCAATTTGGACATGTAAATGAATCAA +AAGGTCAAAATGTATTGCTTGAGTATGTTTCAGCTAACCCTACAGGAGATTTACATATTGGTCATGCTAGAAATGCAGCA +GTTGGTGATGCTTTAGCTAATATTTTAACTGCAGCTGGCTATAATGTAACACGTGAATATTATATTAATGATGCTGGTAA +TCAAATTACTAACTTAGCGCGTTCGATTGAAACACGTTTCTTTGAAGCTTTAGGTGACAATAGTTATTCAATGCCAGAAG +ATGGCTATAATGGAAAAGATATTATTGAAATAGGTAAAGATTTAGCAGAGAAACACCCTGAAATTAAAGATTATTCTGAA +GAAGCACGTTTGAAAGAATTTAGAAAATTAGGCGTAGAATACGAAATGGCTAAATTGAAAAATGATTTAGCAGAGTTCAA +TACGCATTTTGATAATTGGTTTAGTGAAACATCTTTATATGAAAAAGGAGAAATTCTTGAAGTTTTAGCAAAAATGAAAG +AATTAGGTTATACGTATGAAGCTGATGGCGCTACATGGTTACGTACAACTGATTTTAAAGACGACAAAGACAGAGTATTA +ATTAAAAATGACGGTACATATACGTATTTCTTACCAGATATTGCGTACCACTTCGATAAAGTAAAACGTGGTAATGACAT +TTTAATCGATTTATTTGGTGCTGATCATCATGGTTATATTAATCGTTTGAAAGCATCTCTTGAAACGTTTGGTGTAGATA +GTAATCGTTTAGAAATTCAAATCATGCAAATGGTTCGTTTAATGGAAAATGGTAAAGAAGTGAAGATGAGTAAACGTACT +GGTAATGCGATTACATTAAGAGAAATTATGGACGAAGTTGGCGTTGACGCTGCACGTTATTTCTTAACTATGCGTAGTCC +TGATAGTCACTTTGATTTTGATATGGAATTAGCGAAAGAGCAATCTCAAGACAATCCAGTTTACTATGCTCAATATGCAC +ATGCGCGTATTTGTTCAATTTTAAAACAAGCGAAAGAGCAAGGTATTGAAGTGACTGCTGCGAATGATTTTACAACGATT +ACTAATGAAAAAGCGATTGAATTGTTGAAAAAAGTAGCTGATTTCGAACCTACAATTGAAAGTGCTGCTGAGCATAGATC +GGCACATAGAATTACTAATTATATTCAAGATTTAGCTTCTCATTTCCATAAATTCTATAATGCTGAAAAAGTGTTAACAG +ATGATATTGAAAAAACAAAAGCACATGTTGCTATGATTGAAGCGGTCAGAATTACATTGAAAAATGCATTGGCAATGGTC +GGTGTAAGCGCACCTGAATCAATGTAAGAACATTTATATACACTCCAACGTAGAGTTTCTCGAAAGATACTTTGTGTTGG +AGTGTTTTTTTTAGGTATGTGACATATTGGGGAATGCTTAGTATGTGAATAAGGTTAAGAGGAACACAGTTGGATGCTCT +GCACAACTGCATAAGAGAGCCTGAGACATAAATCAATGTTCTATGCTCTACAAAGTTATAATGGCAGTAGTTGACTGAAC +GAAAATTCGCTTGTAACAAGCTTTTTTCAATTCTAGTCAACCTTGCCGGCGGGGCCCCAACAAAGAGAAATTGGATTCCC +AATTTCTACAGACAATGCAAGTTGGGGTGGGACGACGAAATAAATTTTACGATAATATCATTTCTGTCCCACTCCCTCTA +AAATGGAGGGTGTAAATGTTAGGAACTGATGAATTATATAAAGTTTTATATGAACATCTCGGACCACAATTTTGGTGGCC +TGCTGATAATGACATTGAAATGATGTTAGGTGCAATTTTAGTTCAAAATACTAGATGGCGAAATGCAGAAATTGCATTGA +ATCAGATTAAAGAACATACGCATTTTAATCCAAATCATATATTAGAACTACCTATTGAAACGTTACAATCATTGATACAT +TCAAGTGGCTTTTATAAAAGTAAATCACTGACGATTAAAACATTATTAACATGGTTAGCACGACATCATTTCAATTATCA +AGAGATTAATGAGCGATATAAAGGTGGATTAAGAAAAGAATTATTATCTTTGAAAGGTATTGGAAGTGAAACAGCAGATG +TCTTACTTGTTTATATATTCGGACGTATTGAATTTATTCCAGATAGCTATACAAGAAAAATATATGATAAATTAGGATAT +GAAAACACTAAAAATTATGATCAATTAAAAAAAGTAGTCACATTACCAAATCATTTTACAAATCAAGATGCTAATGAATT +TCATGCTCTGTTAGATGTATTTGGTAAACATTACTTTAGAGACAAAGATATAAAGAATTATGATTTTTTAGAACCTTACT +TTAAAAAGTAAACGCTGTGAAGTTAGATAGATGAGTTTATATGAAATATAAAAAATAATTTACTATTTTCTTTTAGTATG +TGGACTTATATAATAAATAGAAGCATATAAAGAAAAAAACAGTTGTTTGTTTGTGCAGCAACTGCATAAGAGCCCCTAAT +CGCTAAAGCTCAAGGGGAGTAAAGGAATACAGTTGTTTGTGCAGCAACTGCATAAAAGCCTCTAATCACTAAAGGTGAAG +AGGAACGCAGTTGGATGCTAAGGCACAACTGCATAAAAGCCTCTAATCGCTAAAGATGAAGAGGAACGCAGTTGGATGCT +ACCGCACAACTGCATAAATCCCTCTAATCGCTAAAGCGAAAAGTGGGATTAAAAAGGAGATGTGATAGTGTGAAGAAATC +GTTAATTGCTTTTATTTTGATTTTTATGCTTGTCCTGAGTGGCTGTGGTATGAAAGATAATGATAAACAAGGTAGCAATG +ATAATGGCTCGTCTAAATCGCCGTACCATAGAATTGTTTCGTTAATGCCTAGTAATACTGAAATTTTATATGAATTAGGA +TTAGGTAAATACATAGTTGGTGTTTCAACGGTTGATGATTATCCAAAAGATGTGAAAAAGGGTAAGAAACAATTTGATGC +TTTGAATCTAAATAAAGAGGAACTTTTAAAGGCAAAGCCAGATCTAATTCTTGCGCATGAGTCGCAAAAGGCAACTGCTA +ATAAAGTATTGTCATCATTAGAGAAACAAGGCATCAAAGTAGTGTATGTTAAAGATGCACAATCAATTGATGAAACTTAC +AACACATTTAAGCAAATTGGGAAATTAACGCATCATGATAAGCAGGCTGAACAACTTGTTGAGGAAACTAAAGATAATAT +CGATAAAGTCATAGATTCAATTCCTGCTCATCATAAAAAATCAAAAGTATTTATTGAGGTTTCATCAAAGCCTGAAATAT +ATACAGCAGGGAAGCATACATTTTTTAATGATATGTTAGAAAAATTAGAAGCCCAAAATGTGTATAGTGACATTAATGGT +TGGAACCCTGTAACGAAGGAAAGTATTATTAAAAAGAACCCAGATATATTAATTTCGACGGAAGCTAAGACAAGATCAGA +TTATATGGATATCATCAAAAAAAGAGGTGGATTCAATAAAATTAATGCTGTCAAGAATACACGTATTGAAGTTGTAAATG +GTGATGAAGTATCAAGACCAGGTCCACGTATTGATGAAGGATTAAAAGAATTAAGAGATGCAATTTATAGAAAATAAACC +ATTCTAATTATGCCCCTTATTGCTACATGTAAAAAATACATGTTTGAGATAAGGGGTTTTTAAAATATATTTAGTGAATG +ATAGCAACGCGAGTATGTGATTGCTATAATGAATGTAATTATCGATGAAAGAAAAGAGAATGCTATGACATTTAATAAAG +TATTATTGAGCTGGATAGTCATATTGATTATAACAACTAGCATATATCTATTTTGGCAGTTGGGCGATATCAATGATGTA +TTTAACCAGTCTATTTTAATCAATGTTAGATTACCGAGATTATTAGAAGCATTGTTGACAGGTATGATATTAACTGTTGC +AGGCCTTATATTTCAAACAGTTTTAAATAATGCATTGGCAGATAGCTTTACATTAGGATTGGCAAGCGGCGCTACATTTG +GTTCAGGATTAGCATTATTTTTAGGTTTAACAACGTTATGGATTCCTGTATTTTCAATAACATTTAGTTTGATAACATTA +ATAACTGTATTAGTCATTACGTCGGTATTGAGCCAAGGCTATCCAGTTAGAATCTTAATATTAAGTGGTTTAATGATTGG +TGCGTTATTCAATTCACTTCTATATTTTTTGATTTTATTAAAACCTCGCAAATTAAATACAATTGCCAATTATCTGTTTG +GTGGTTTTGGTGATGCAGAATACTCAAATGTATCTATAATAGCAATCACATTTATCATTGCATTGTTTGGTATATTTATC +ATTCTTAATCAACTAAAGTTATTGCAATTAGGAGAACTAAAAAGTCAGTCACTAGGCTTAAATGTTCAATTGATTACATA +TATCGCGTTATGTATAGCTTCTATGATAACGGCGATAAATGTCGCATATGTTGGCATCATTGGATTCATTGGTATGGTGA +TACCGCAACTCATTAGAAAATGGCAGTGGAAACAATCATTAGGAAGACAATTGGCTTTAAATATTGTAACTGGAGGACAA +ATAATGGTTATGGCAGATTTTATTGGTAGCCATATATTGTCACCAGTACAAATACCGGCAAGTATTATCATTGCATTAAT +TGGTATACCAGTGTTATTTTACATGCTAATATCTCAGTCGAAACGGTTACACTAGCACACGACATTTGCTAAAATAAAAA +TAACTATAAACATAAAGAGGGCATAAGCGATGGATTTGAATCAAATTAAAGCAGTTGTATTTGATTTAGAAGGTACGTTG +TTGGACAGAGTTAAATCTCGAGAGAAATTTATCGAAGAGCAATATGAACGATTTCATGACTACTTAATTCATGTTCAACT +GGCAGATTTTAAAAAAGCATTTATTGAGCTAGATGACGATGAAGATAATGATAAACCTGATTTATATAAAGAAATCATTA +AACGTTTCCATGTAGATAGGTTAACTTGGAAAGACTTATTTAATGATTTTGAAATGCATTTTTATCGTTATGTATTTCCT +TATTACGATACTTTGTATACACTAGAAAAGCTATCGCAAAAAGGCTTTCAAATTGGTGTTATCGCAAATGGTAAATCTAA +GATTAAACAATTTCGATTACATTCACTTGGTTTGATGCATGTTATTAATTATTTATCAACATCAGAAACAGTTGGTTTTC +GTAAACCACATCCTAAAATTTTTGAAGATATGATTGATCAACTAGGGGTATTACCTGAGCAAATTATGTATGTTGGCGAT +GATGCGTTAAATGATGTAGCTCCAGCACGAGCTATGGGCATGGTTAGTGTATGGTATAAACAAGAAGATGCTGAAATTGA +ACCACTCGAAGAAGAAGTTGATTTTACAATTACAACAGTGGAAGAATTATTAACCATTTTACCAATAAAAAATGATAATA +AAGGAGAAAATTATGGATCTATTTACTAGAAAAGATGGAACATCGATACATTACAGTACATTAGGTGAAGGCTATCCTAT +CGTATTGATTCATACTGTACTTGATAATTATTCTGTGTTTAATAAATTAGCAGCAGAATTAGCAAAATCATTTCAAGTTG +TGTTAATTGATTTACGTGGACATGGCTATTCTGATAAACCTCGTCACATTGAAATAAAAGATTTTTCTGATGACATTGTT +GAATTACTTAAATATTTATATATTGAAGAAGTTGCATTTGTATGCCATGAAATGGGTGGAATCATTGGTGCGGATATTTC +AGTACGTTATCCTGAATTTACATCATCACTTACGTTGGTAAATCCAACATCTATTGAAGGTGAATTACCGGAAGAACGTT +TATTTAGAAAATATGCCCATATTATTCGAAACTGGGATCCTGAAAAACAAGATAAATTTTTAAATAAGCGTAAGTATTAT +CGTCCGAGAAAAATGAATCGATTCCTCAAACATGTCGTAGATACAAATGAAATATCAACTAAAGAAGAAATTCAAGCAGT +TAAAGAGGTATTCAAAAACGCTGATATTTCTCAAACTTATAGAAATGTCGTAGTACCGACAAAAATTATTGCAGGAGAAT +TCGGTGAAAGAACAACAAGATTAGAAGCTAAAGAAGTAGCTGATTTAATCCAAAATGCGGACTTTGAAGTATATCAAGAA +TCAAGTGCATTCCCATTTGTTGAAGAGCAAGAAAGATTCGTCGAAGATACAGCTGCATTTATCAACAAACATCACGATGA +AAAGCATGTTTAATTTTTAGTGATAAGTGAAATATAAAAGTAGTAATTATTATTTGTAATATAATTGTAATATGACTGTT +GTTTTAGAAATGATTGTTGTATAATGGTTGTGAGCACAAGACGATATAAATAAAAGGAGAGAGTGCTTATGAAAAAGCTA +CTAACGGCAAGTATAATTGCATGTTCTGTTGTAATGGGAGTAGGCTTAGTGAACACTAGTGCCGAAGCAGCAAGTGGCAA +CTCTATTGATACTGTTAAACAATTAATTAAGGGTGATCAGTCATTAGAAAATGTGAAAATTGGCGAATCTATTAAAGATG +TTTTAACTAAGTACAAAAATCCGATGTATTCTTACAATGAAGATGGAACTGAACATTATTACGAATTCCATACTAAAAAA +GGTATGTTATTAGTAACTACTGATGGTAAGAAAAACAATGGTAAAGTAACTCATATTTCAATGATGTATAATGATGCTAA +TGGTCCAACATATCAAGCTGTTAAAAATTATGTTGGCAAAGCAGTAACACATACGGAATATAGCAAAGTTGCTGGTAATT +TTGGATATATTGAAAAAGGCAAAACGACTTATCAATTTGCCTCAGCACCAAAAGATAAAAACATAAAATTATATCGTATT +GATTTGGAAAAATAATAATTAAATTAATCGTGCTTAACCGACAAGTTAAATCGCAATACTTGTCGGTTATTTTATTACAT +GGCATAATACAGTGAATATTAATTTTTATGATTTGCGAAGGGGAAATTAAACCAATACAATAAATAAAGATGTTTCATTT +TAAAGGAGTACTACCAATGCGTATTAACGATAAAATTTTACTAGAAAATATAGAAGATTACTTTAATCATAAAGGTTTAT +CACCGCATTTGATTGATGATATTAAAGAGAAAGTAATTACTGATATAAAAAATTCTGAAAAGAAAGATCAAGATTATATT +GAATATAAAAGAAAATCTCCAGCACAAATCATATTAATGATTCAAAGGAATTTGTTTGCTTTACAAATGAATCCAGTTAT +TTTCTTTATTATAAACTTCATTCTCATATCCTATTTATATGATAAACAGTATGTTCAGTTTCAAGCTATTACTGGAATGA +GTCTATTTTATTGTTTAGTGATTTTTCCAATGACTATTGTTGTATACCTAAGGGTGTCTCAAAAGAATTACTTGCGTAGT +AATAAAATAGAAATGATTATGGGTACAATTATCGCTATCATATCCTTGTTATTAATTATATTACAAGCATTTAATATTAC +TTGGGGCGTTATACCAATTACAAATTTTGGACATCAATTTTTCTTTTTCATTGGTATTATTTTAGTAATTGCCGGCATAT +TTTATAAGCGACTTGAGTTTTCGGGAATCGGGTTATTATTTTGTCAAAAAACCGTCGATGCAATGATTCATAATCCACAA +TCAGCCCAGATTTTTTCATTAATTATATGGATATTATTAGTAGTTCTAGTTATATATTTCACAATTAGATTATCTTCACG +TACAAGATTATAAATATGATAAAACTATTCACTTGATTAATTGTATTAATTGAGATGAATAGTTTTTTTATTGTTGGAAT +AACTTTTGGTAATTTATAAATAATTTAAAAAAATTGTTTATAAAATGGAAGCGTATATAGAATGAAGGTTGGGTATATAG +TTTATTGAGGGAGGTGTCACAATGAATAAAGTCACAATTAATCCTCAAATCCAATTAACTTATCAAATTGAAGGTAAAGG +GGATCCTATAATATTACTTCATGGATTGGATGGTAATTTAGCTGGATTTGAAGATTTGCAACATCAACTAGCATCATCAT +ATAAAGTACTTACTTACGATTTAAGAGGTCATGGCAAGTCTTCTAAAAGTGAATCATACGATTTAAACGATCACGTTGAG +GATTTAAAAATTCTAATGGAGAAGTTAAATATTCATGAGGCACATATTCTAGGACATGATTTAGGTGGGGTAGTTGCTAA +GTTATTTACAGATAAATATGCTTATCGTGTAAAATCATTAACTACCATTGCATCGAAGAAAGATGACTTAATACACAGCT +TTACTCAATTGTTAATACAATATCAAGATGATATAGCGGGTTTTAATAAGTCTGAAGCGTATATTCTTTTATTTTCTAAA +TTGTTTAGAAATCAAGAGAAGACGATGAAATGGTATCAAAAACAAAGAATATATAGCATTAAGTCTGAGGATGATAGTGC +GGTGGCAATTCGTTCATTAATTTTGCATAAAGATGAACCTATGTATTTAAAAAAACGTACATGTGTACCTACTTTGTTAA +TTAATGGGGAACATGATCCTTTGATTAAAGATAAGAATCATTTTAAATTGGAAGCGCATTTTTTAAATGTTACGAAAAAA +ATCTTCGAACATTCAGGACATGCACCGCATATTGAAGAACCAGAAGCATTTATGAATTATTATTTAAAATTTTTAAAAAG +CGTATCATAATATGTGATATATAAACCTAGGGCATAAAGTCCTTAGGCAATGTGAAAAAGCTGATTACTATTCATTATTT +GATAGAAATCAGCTTTTTTTGAAATGTATTTGATATATACTGCTCGTTATGCGGCTATCTTCCTTATATTAAGTGCCATT +AGTGCAAAACCTCTTAACAATTAGGTAAAAAGAGCATAAAAAAAGGAAGTTTAATAGAATGTATCATCTATCAAACTTCA +CCAAATTGCGCTAAACAAAATTATAGTTCAATTTCGTTGTTTGCTTCAGTGATTCGTTTATTTACTCGACTCAATAATGA +TTCGATTTTTTTACGTTGTTGTGCATTAACAAGAATTAATACAGTTCTTTCATCATGCTCATTACGTTTTTTATCGAAGT +AATCTTCTTGAGATAAAATTTTAACTGCTTTAACAACTTGTGGTTGTTTGTAGTTTAAATGATTAATAATATCTTTAAGA +TAGTATTCTTTCTCTTTGTTTTCGCTGATGTATGTCAATACAGCGAATTCTTCAAAGCTAATTGAAAATTCCTTTTTAAT +TAAACTTTTTAATTTGTCAGCATAAGTGACCATTGATAACAACTCAAAGCAATCATTGATTTTTGTAATTGCCATGTTTA +AAACCTCCCTATTTGATGCATCTTGCTCGATACATTTGCCCGATAATATATTGATATCTAATCTTTATTTATTATAGATA +TGTTAGTCATAATTTTGCATTAAATAAGTTTTATTAAATATATTTAATGCTCTATTATTTAGTTAATTATAACTAATTAA +AAATGAGAAGTAAACAAAAAAGTGTTTATAAAACAAATTATTCATTAGACACAGTGATTGTATTTCTGGGTTAGCATTTG +GTTTAGTCAAAAATATCAGCATTTTTTATAATTTACCTTAATTTAATCGACAATACAAGGTAATTTATTTAATGAAACGA +TTTAGCGCAATTAAATGTTTCGATATTCAATGTGTGTCTAAAGAGATTCTTTGTTATAGCTTAACTCTTCCAAGTTAACT +TCTTCTACTATTTATACCCATTTTTCAGAATTTTATCACTAAAAATGAAAAATTTTTATAATTTTTTTAATTTAGTAATT +CTCAGTGCAGTTTTAATGATAAGAAAAACACATTAATTTAGTGATTAAAACCAATCTATAGCACGTTAAAGTGTCTATAC +GTTATTCCGATTGATAACGGGTAACAAATAATAGAAAGTACGATAATTGATCATGCATTAAAGCACTTTTATTTTTCAAA +CTCGACTCAATGAATTATAAAAAGTTGTGCATTTTGACTTATTTTTAATTAATTGAATAACCAACTATGAAATATTAGCA +CGAATAACATAATTTTTGCTATTAATTTGGTGTAAATTATTAAAAAATAACATTAATTTAGGTCTTAAGAATGAGTTGAC +TATCGAATTTGCTAAATTAAAATTTGTTGTGTCAAAGCATACTACCCAAATCAACGCTTTTCTAAAGATTTTTAGACCAC +AAAAAAATAGCTTATCACTTTTAAAAATAATTACAAGTTAAAATGATAAGCTAAAAGTTAATTTAAAATAATCTTAAAAG +TATAATGCCTGTCAAAATACATAATAGACCAATAGTTTTTCTGGATGTCATTGCTATTTTAGGTGAACCAAATAATCCAA +AGTGATCTATCAATATGCCCATTAGAATCTGGCCAAACATCCCAATAAGTGTTGTTAATGCTGCACCCATATGAGGCATT +AAGATAATGTTAGCTGTTACAAAAGCCATGCCAAGTATACCGCCAGTAAAATAGATAGGCTTTAATTTACCGAATTTTAA +ATGACTTGTTTTTAGTTTTAAAGAACGATTAAAAATAGCGGTTAAAATCAATAGCGCTATTGACCCAATTGTAAATGATA +CTAATGATGCAAAGGCTGGTGAATGAGTATGACTAGCTAAAGCACTATTAATTGTCGTTTGAATAGGTGGAAAGAAACCA +AAAATAAATCCTAATAGAAGCCAAAACAGTAAATACTTTTGATCAGTTAGTAATAAATTATTCTTGTTAAATTGATTCAT +TATGACGATGCCGACAATGAGTAACAATACTCCAATTGCTTTAATTAAATTAAAATCATGAATTGTAGCGCCAAATAATC +CAAATGTATCAATAATGACACCCATAATAATTTGACCCGCAACTGTTGCAATTACAGTTAATGTTGCACCTAATTTTGGC +AATAACAATAAATTGCCAGTTAAAAAGCTAACCCCAAGCAAACCACCGACTACCCATGTGTAGTTAAATGATTGATTATT +GTAAAAGTGAATAGTAAATACTTCTGGATTGATAATGATATTTAAAATAATTAAACAAATTGTTCCAACTGAAAATGAAA +TGAATGAAGTATAGAAAGGCGATTTAGTATACAGTGATAGTCTTGAATTGACAGATGTTTGGATAGGAATAAGCATTCCA +ACTAGGACACCAATGATATAGAAAAGAAGCATATGAATAAAGTCTCACTTTCCAAAGTTAATAATAAAATCATATATTTA +CATTATCATTATAGAGAAAATATACAACTCATTTACAATAATGTTGAAAAAAACAGGACATTATTACAACAAAATCACCC +TAGACAATCATGATGAAAGTCTAGGGTGTTAGTATTTATACTAAATTAATTGCGCTTTCTTTATATCTATTTCTTATCTT +CAAATAAAACAGTTGTTGTTTTATCAATAATTTGAGCAGAATGTACTTTGGTGATAGTACCACTATTTGACTTTATAATA +TTTAAATCTACTAATGCATTCATACTATCACGTGCAACTTGCTCGGTCACTACGTTGTTTAATTGTGGTAATTGCAGTTT +TACAGGCTTGTCTAGTGAAGATTTAAAACTTAATTCAAGTGTTTTAGTCATGAATATGTCCTCCTGATTAAATTGATAAA +GATTTGATGAGTTCGATATTGCTATATGTTTCTCCAGTAAGTCGCTCAATAAGTTTGCTGAATGTTTTAATTTGGTCGTT +TGATGCATCAGGGTTAATGTTAGCGAATCGACGCTTAAATTCTGTTTGTTTGCCGTTAGCGTCTACTTTAGTAAATGATA +ATACAATAGTGATGTGGTTTATTTTACTCATATTTTAAAACCTCCTTTCACACTATATATCGAAACAAAATAATAAAATG +GCTAATTTTATTTTCTATGTTTAAAATCTATAAAAAAGGCAATAGATATATGTAACTAAAATATAATATAGCTATGATTA +AGACACTTAAAGCAAGGGGAGGTTGAGTAATGAATAAAGTAGAAGCGATTAAATTTAATGATGATATTGTTAAAATGTAT +GAAGCGCTCAAGATAAAATCTGAACGTGACTATTTATTCTTTAAGTTAGCTATACATAGTGGATTGAAAGTATCAGAATT +ATTAACAATTACAGTCTCTCAAGTTAAGAGACTAATTGAAAAGTGTACGTTATCAGAAATGTGTAAAGCACATTTTCATT +CGTTGATTAAAATTAGGTTACCAGAAACATTATCGAAAGAACTACTTCAATATATAGAGGACAGGAGTCTTTCGAATGAA +GACGTTCTTTTTCAATCACTACGAACAAATCAAGTATTATCTAGACAGCAAGCATATCGAATAATTCACCAAGCATCAAT +TGAAGCTGGTATAGATAATGTAGGACTAACGACATTGCGTAAGACATTTGCATATCATGCTTATCAAAAAGGTATACTTA +TACCAGTCATTCAAAAGTATTTAGGGCATCAATCTGCTATTGAAACACTAAATTTTATCGGTTTAGAAAATGAGTGTGAA +CATAGTATTTATATTTCATTACAATTATAGAAACAAAGGAGGCTAATAATGAGTTTGGTTTATTTATTAATTGCTATACT +TGTGATTATGGCGATGATACTTCTAATGTCTAAACGTAGAGCATTGGCTAAATATGCCGGGTACATAGCGTTGGTTGCAC +CTGTAATTTCATCTATCTATTTTTTGATTCAAATACCATCAGTAGCTAAACTGCAATATCTTTCTACCTCTATTCCATGG +ATTAAGACATTAGATATTAATTTAGATTTACGTTTAGATGGTTTAAGTTTAATGTTTTCTCTTATTATTTCACTTATTGG +AATTGCAGTATTCTTCTATGCAACTCAATATTTATCCTCTCGAAAAGACAATTTACCAAGGTTTTATTTTTATTTAACGT +TATTTATGTTCAGTATGATTGGTATTGTATTATCAGACAATACGATATTGATGTACATTTTTTGGGAATTAACGAGTGTA +TCATCATTTTTATTGATTTCATATTGGTATAACAATGGTGACAGTCAATTTGGTGCGATTCAATCATTTATGATTACAGT +ATTTGGTGGATTGGCGTTATTAGTTGGTTTTATTATGCTGTATATCATGACAGGAACGAATAACATCACAGAGATATTAG +GACAAGCAGATCATATTAAGAATCATGGATTGTTTATCCCTATGATTTTTATGTTTTTATTAGGTGCATTTACAAAATCA +GCACAATTTCCATTTCATATTTGGCTACCTAGAGCAATGGCTGCACCTACACCTGTAAGTGCTTATTTACATTCAGCCAC +GATGGTAAAAGCTGGTATCTTTTTATTACTTCGATTTACACCATTATTAGGTCTTAGCAATATGTACGTATATATCGTTA +CGTTTGTTGGTTTAATAACAATGTTATTTGGTTCAATTACAGCTTTAAAACAATGGGATTTAAAAGGTATCCTAGCGTAC +TCTACAATCAGTCAACTTGGGATGATTATGGCTATGGTGGGTATAGGTGGCGGATATGCTCAACACCAACAAGACGCAAT +AGCATCTATTTATGTATTTGTATTATTTGGTGCGCTATTTCATCTAATGAATCATGCCATCTTTAAATGTGCGCTTTTCA +TGGGAGTAGGTATTTTAGATCATGAAGCAGGTTCAAGGGATATACGAATTTTAAGTGGAATGCGTCAACTATTTCCTAAA +ATGAATCTAGTCATGACGATAGCGGCTCTATCTATGGCTGGAGTACCATTTTTAAATGGATTTTTAAGTAAAGAAATGTT +TTTAGATGCATTAACACAAACTGGACAATTATCCCAATTTAGTTTGATTTCAATGATAGCTATCGTGTTTGTTGGTGTTA +TTGCGAGTGTTTTTACATTCACATATGCACTATACATGGTAAAAGAAGTATTTTGGACAAAATATGATTCTAAGGTTTTT +ACTAAAAAAAATATCCACGAACCATGGTTGTTTAGTTTACCATCTCTTATATTAATGGTGCTAGTACCTGTAATCTTTTT +TGTACCAAATATATTTGGGAAGGGGATTATCGTTCTAGCATTAAGAGCTGTATCAGGTGGTAATCATCAAATTGATCAAT +TGGCACCACATGTTTCGCAATGGCATGGATTTAACATACCGCTTCTTTTAACCATCATCATTATTTTATTGGGTAGTGTA +CTAGCAATCAAAGTAGATTGGAAAAAAGTGTTCACAGGTAAAATTAGACAGATTTCAGTTTCAAAAAGCTATGAGATGGT +ATATCGACATTTTGAAAAGTTTGCTACGAAGCGATTTAAACGTGTTATGCAAGATCGTTTAAACCAATACATTATTATGA +CCTTAGGCATATTTATGATTATCATTGGATATGGTTATATTCGAATTGGACTTCCTAAAGTACATCAGTTACATGTTTCT +GAATTTGGGGCATTAGAAATTATATTAGCAATCGTAACTGTCACAATTGGTATTTCTTTAATTTTTATACGTCAACGACT +GACAATGGTCATTTTAAATGGAGTCATCGGATTTGTTGTGACCTTATTCTTTATAGCAATGAAAGCCCCTGATCTAGCAT +TGACTCAGCTAGTAGTTGAAACAATAACGACGATACTATTTATTGTCAGTTTTTCAAGATTACCAAACGTGCCAAGATCT +AACGCTAACAAAAAAAGAGAAATAATTAAAATTTCTGTATCACTCTTGATGGCACTTATTGTTGTATCATTAATTTTTAT +TACACAACAAACAGATGGTTTATCATCAATATCAGACTTTTATTTAAAAGCTGACAAACTAACAGGTGGTAAAAATATTG +TAAATGCGATACTTGGTGACTTTAGAGCATTAGATACATTATTTGAAGGATTAGTGTTAATTATTACTGGGCTAGGTATT +TACACATTATTAAATTATCAAGATCGGAGGGGACAAGATGAAAGAGAATGATGTCGTGTTAAGAACGGTCACGAAACTTG +TTGTATTTATTTTATTGACTTTCGGATTCTATGTCTTCTTCGCAGGTCATAATAATCCTGGTGGTGGGTTTATTGGTGGT +TTAATATTTAGTTCAGCGTTTATTTTAATGTTTCTGGCTTTTAATGTTGAAGAGGTTTTAGAAAGTTTACCGATTGATTT +TAGAATTTTAATGATTATTGGAGCATTGGTATCATCTATTACTGCGATAATACCTATGTTTTTTGGAAAACCATTTTTGT +CTCAATATGAAACAACTTGGATACTTCCAATTTTAGGACAAATTCATGTAAGTACAATAACACTTTTTGAATTAGGTATT +TTATTCTCAGTTGTTGGTGTTATTGTCACAGTGATGTTGTCGCTTAGCGGAGGTCGATCATGAATTTAATATTATTACTA +GTTATAGGATTTTTAGTGTTTATAGGAACATATATGATTTTATCAATCAATTTAATTCGTATTGTAATCGGAATTTCAAT +ATATACTCATGCTGGTAATCTCATTATTATGAGTATGGGAACGTATGGTTCTAGTAGATCAGAACCACTAATAACTGGTG +GAAACCAATTGTTTGTTGATCCCTTGTTACAAGCTATTGTACTAACTGCAATAGTTATAGGGTTTGGGATGACTGCGTTT +TTACTTGTACTTGTTTATAGAACTTATAAAGTAACAAAAGAAGATGAAATTGAAGGCCTAAGGGGGGAAGATGATGCTAA +GTAACTTATTGATTTTACCAATGTTATTACCATTCCTTTGTGCCTTAATCCTTGTATTTTTAAAAAATAATGATCGTATT +TCTAAATATTTATACTTAGGTACAATGACTATCACCACAATTATTTCATTAATGCTATTAATTTATGTTCAGCGTCACCG +TCCAATTACGCTAGACTTTGGAGGATGGTCAGCGCCCTTTGGTATACAGTTTTTAGGAGATTCTTTAAGTTTAATTATGG +TTACAACCGCTTCGTTTGTGATTACTTTAATTATGGCATACGGATTTGGGCGTGGCGAACATAAAGCAAATCGTTATCAC +TTGCCATCGTTCATATTATTTTTAAGTGTTGGCGTGATAGGCTCTTTTCTAACATCAGATTTATTTAATTTATACGTCAT +GTTTGAAATTATGTTACTAGCGTCATTTGTACTCATTACACTTGGACAATCTGTAGAACAATTACGTGCTGCAATTATTT +ATGTTGTCTTGAATATTATTGGTTCATGGCTATTCTTATTAGGTATAGGTTTACTTTATAAAACAGTAGGTACATTAAAC +TTTTCACATATTGCAATGCGTTTGAATGACATGGGAGATAATCGCACTGTTACAATGATTTCATTAATCTTCTTAGTCGC +ATTTAGTGCGAAAGCAGCGCTGGTCCTTTTTATGTGGCTACCCAAAGCCTACGCTGTGTTAAATACTGAGCTTGCAGCAT +TATTTGCAGCGTTAATGACCAAAGTAGGGGCCTATGCATTAATTCGATTCTTCACTTTACTATTTGATCAACATAATGAT +CTCATACATCCATTGCTAGCAACTATGGCTGCTATAACTATGGTCATCGGCGCTATAGGTGTCATTGCTTATAAAGATAT +TAAAAAGATTGCAGCTTACCAAGTCATAATCTCAATAGGATTTATCATTTTAGGTTTAGGAACAAACACGTTTGCAGGTA +TTAATGGTGCAATATTTTATTTGGTAAATGACATTGTTGTAAAAACATTGCTATTTTTTATTATTGGTAGTTTAGTTTAC +ATTACAGGCTATCGACAATATCAATATTTGAATGGCTTAGCTAAAAAAGAACCTTTATTTGGAGTTGCGTTTATTATAAT +GATTTTTGCTATTGGCGGCGTGCCTCCATTTAGTGGCTTTCCGGGGAAAGTACTTATTTTCCAAGGTGCATTGCAAAATG +GCAATTATATTGGACTAGCGTTAATGATTATTACTAGTCTAATTGCAATGTACAGTTTATTTAGGATACTTTTTTATATG +TATTTTGGAGATAAAGATGGGGAGGAAGTTAATTTTAAGAAAATCCCGCTATATCGAAAAAGAATTTTAAGTATTTTAGT +AGTTGTGGTTATCGCAATCGGAATTGCTGCACCTGTTGTGTTAAATGTTACAAGTGATGCAACTGAGTTGAACACGAGTG +ATCAATTATATCAAAAACTTGTAAATCCGCATTTGAAAGGAGAGGACTAAATGAATCAAATAGTTTTAAATATTATCATT +GCATTCTTATGGGTATTATTTCAAGATGAAGATCATTTTAAATTCTCGACTTTCTTTTCTGGATATCTAATTGGTTTAAT +TGTCATTTATATATTACACAGGTTTTTCAGCGATGATTTTTATGTTAGAAAAATATGGGTAGCTATTAAATTTTTAGGTG +TTTATTTATATCAATTAATAACATCTAGCATTAGCACGATTAATTATATTCTTTTTAAAACAAAAGATATGAACCCTGGA +TTACTTTCATATGAAACAAGACTAACAAGTGATTGGTCAATAACATTTTTAACAATTTTAATTATTATAACTCCAGGGTC +TACAGTAATACGAATTTCTCAAGACTCTAAAAAGTTTTTTATTCATAGTATCGACGTGTCAGAAAAAGAAAAAGATAGTT +TGTTAAGAAGTATTAAGCATTATGAAGACTTAATATTGGAGGTGTCGCGATGATACAAACAATAACACATATTATGATTA +TTAGTTCACTCATTATTTTTGGAATTGCATTAATCATCTGTTTATTTAGATTAATCAAGGGACCTACAACAGCAGATCGT +GTCGTTACATTTGATACAACAAGTGCTGTCGTAATGTCAATTGTGGGTGTGTTAAGTGTACTTATGGGCACCGTTTCTTT +CTTAGATTCAATCATGCTCATTGCCATTATATCTTTTGTAAGTTCTGTTTCAATATCACGCTTTATTGGTGGGGGGCATG +TGTTTAATGGAAATAACAAAAGAAATCTTTAGTCTTATTGCTGCTGTGATGTTGTTGTTAGGTAGTTTTATTGCTCTTAT +TAGTGCAATAGGTATCGTGAAATTCCAAGATGTTTTCTTAAGAAGTCACGCTGCGACAAAAAGTTCAACTTTATCCGTGT +TATTAACTTTAATCGGTGTATTAATTTATTTTATTGTGAATACAGGATTTTTCAGTGTGCGTTTATTACTGTCACTTGTT +TTTATTAATTTAACTTCACCAGTCGGCATGCACTTAGTCGCTCGCGCTGCTTATCGCAACGGCGCTTATATGTATCGAAA +AAATGATGCTCACACACATGCATCAATATTATTAAGTTCAAATGAACAAAACTCTACAGAAGCATTACAATTACGTGCTG +AAAAACGAGAAGAGCATCGTAAGAAATGGTATCAAAACGATTGAAGTGTTGGCTAAATTGTTAAGCATAACAATGCTTTT +GAAAATCTGTTTTCAAAATTAATCATTAAAAGAGTACTTTGAAATTGGTTAGTCGATTTTAAGGTGCTCATTTTTTGTAT +TATAAGTTAATGTTGTTATGTGAAAGCAAGTTGTTTATTTATACAAACAATTAGTGATAAAATAAAATTAAATAATTGCA +AATTTAAGTACTGCAATCGTAGACATATATCTATATTAAAGTGCTTTTGTGGTCGCTAACTCAATTTATCCTCTAAAGTA +TAATTACAACGAGATGTGATACTTAATTTTCAATTAAACAATACTTTTTAGAGAGGTGAAAGTTTGGAAATATTTGAAAC +AATTCTTATATTTATAGCTGTTGTGATACTAAGTTCGTTTGTCCATACTTTCATACCTAAAGTACCCCTAGCATTTATAC +AAATTTTCTTGGGCATGTTACTATTTATTACCCCAATCCCTGTTCAATTTAATTTTGATTCTGAATTGTTTATGGTAACA +ATGATTGCGCCTTTGTTATTTGTAGAAGGTGTTAATGTTTCTAGAGTCCATTTAAGGAAATATATTAAGCCAGTGATGAT +GATGGCATTAGGATTAGTCATTACTACTGTGATAGGTGTAGGTTTATTTATTCATTGGATTTGGCCAGATTTACCTATTG +GAGCAGCATTTGCAATTGCTGCCATTCTTTGTCCTACTGATGCAGTAGCAGTGCAAGCAATCACTAAAGGAAAGGTCTTG +CCAAAAGGAGCAATGACAATTCTTGAAGGTGAGTCATTATTGAATGATGCTGCTGGTATTATTTCATTTAAAATAGCTGT +TGGAGTATTAGTTACAGGTGCTTTTTCACTTGTTGATGCTGTTCAGTTGTTTTTAATTGCATCAATTGGTGGCGCAGTGG +TTGGTTTACTTATAGGTATGGCATTAGTAAGGTTCCGATTAACATTGATGCGTCGAGGATATGAAAACATTAATATGTTT +ACAATTATTCAATTGTTAACACCATTTGTTACGTATTTAATTGCTGAATTGTTTCACGCATCAGGAATCATTGCAGCAGT +AGTTGCAGGACTTGTACATGGTTTCGAACGTGACAGAATTATGCAAGTACGTACACAACTGCAAATGAGTTACAATCATA +CATGGAATATACTAGGTTATGTTTTAAATGGCTTTGTTTTTTCAATATTAGGATTTTTAGTACCTGAAGTTATTATTAAA +ATTATCAAAACAGAACCGCACAATTTAATCTTTTTAATAGGCATCACTATTGTTGTTGCTTTAGCTGTCTATCTATTTAG +ATTTGTTTGGGTTTATGTCTTATATCCTTATTTTTATTTAGCCATCAGTCCATTCCAAAAAATGATGACTAAAAATGATG +ATGATAATCCAACGACTGAGAAACCACCAAAGCGAAGTTTATACGCTTTAATTATGACGTTATGTGGTGTGCATGGAACA +ATTTCTTTAGCAATCGCATTAACGTTACCGTATTTTTTAGCAGGGCATCATGCTTTTACGTATAGAAACGACTTATTATT +TATTGCATCTGGTATGGTTATTATTAGTTTGGTAGTTGCGCAAGTATTATTGCCATTATTAACGAAACCTGCACCTAAAA +CAGTAATTGGCAATATGTCGTTTAAAGTTGCTAGAATTTATATATTAGAACAAGTTATTGATTATCTAAATCAAAAATCT +ACTTTCGAAACAAGTTTTAAATATGGTAACGTGATTAAAGAATATCATGATAAATTAGCATTTTTAAAAACTGTAGAGAA +AGATGATGAAAACTCTAAAGAATTAGAACGTCTACAAAAAATTGCTTTTAATGTAGAAACAAAAACATTAGAGTCTTTAG +TAGATGAAGGACAAATAACGAATAGTGTACTTGAAAACTATATGCGTTATGCTGAAAGAACACAGGTATATAGACAAGCA +TCATTAATAAGAAGAATGATTGTATTATTACGAGGTGCTTTATTAAAACGAAGAGTACAAACGAGAGTGAACTCCGCATC +TTCACTTAGTGTTACGGATAACTTAATGGAATTAAATAAAATTAATAAATTAGTCCATTATAATGTGGTTAGTCGTTTGT +CTAAGGAAACAACAAAAGATAATACACTTGAAGTTGGAATGGTTTGTGACGGTTATTTAATGCGAATTGAAAACTTAACA +CCATCAAATTTCTTCAACTCAGCAAGTGAAGATACGATTACTAAAATTAAATTAAATGCATTGAGAGAACAACGTCGCAT +TTTACGTGAGTTGATTGATACAGATGAAGTATCAGAAGGTACAGCGTTAAAACTAAGAGAAGCCATCAATTATGATGAAA +TGGTTATTGTAGATAGTATGACGTAGTTCCTAATTATGCTAAAAGGGATTGATGAAAAACTGAAGGGCTTTTCATCAATC +CCTTTTATTTTAGGGGAATTGAATAGATAGTTTTAAACTATACGAATTATTAATATTTGAGATTTAATTGAAATAAGTTT +TAAAAATTGGAGGAGATAGATTAAGCGAAGTCATTTAAAGGTGAAGTTAAGTGTATTCACAAAAAATAGCCACACTCATA +TGACATCGGATGAGTGTGGCTTAAGGATCTATGGGGGGAGGAAACCATAGATGTTTACTTTGATAGGCCAGATTAAATAT +CAAAGTATGCGATTATTTATAGCTTGATGCAAAAGTGGTATGCCTATTAAAAGTTACTGCACATAGCTTTTAATATTCCG +TTCAAAGGAAAGGGGCATACAATTGAACAATCTGTAATAGTACTTTTAACCAGCTATGCTAAAAGTCTAGTAGGGAGAAC +AGTTGTCCAATCACATAAGAACCTCTAACTTCGTTAGTACGATTAAGAAAAGCTTTTTAGTTAGTATGTAATACAATTTA +TTGACGCGCGTGAATCTCTTTTATAAGAGTGTGTAGGGAATGGCGTTGTATAAATTGTATTAGAAGAACTTCTAACGCAT +CTCTGTGGTTAAAAGAGATGAAGGGAACGACAGTTTAATTAAAACTGCATAAGAACTTCTAGCTTTTCTCTCTCGTTCAA +AGAGAAGCAGCTGTTCGCAGTTTAATCAAAACCACATAAAGCTTTTAACTTTACTCTTTGATTTAAAGAGTGATAAATGT +TTACAGTTTAATTAAAACTGCATAAGAACTTCTAGCTTTTCTCTTTCGTTCAAAGAGAAGCAGCTGTTCGCAGTTTAATC +AAAACCACATAAAGCTTTTAACTTTACTCTTTGATTTAAAGAGTGATAAATGTTTACAGTTTAATTAAAACTGCATAAGA +ACTTCTAGCTTTTCTCTTTCGTTCAAAGAGAAGCAGCTGTTCGCAGTTTAATCAAAACCACATAAAGCTTTTAACTTTAC +TCTTTGATTTAAAGAGTGATAAATGTTTACAGTTTAATTAAAACTGCATAAGAACTTCTAGCTTTTCTCTTTCGTTCAAA +GAGAAGCAGCTGTTCGCAGTTTAATCAAAACCACATAAAGCTTTTAACTTTACTCTTTGATTTAAAGAGTGACAAATGTT +TACAGTTTAATTAAAACTGCATAAGAACTTCTAGCTTTTCTCTTTCGTTCAAAGAGAAGTTCTAATACCACCATATCGTG +CGATCGGGAACGGTATATATATTAATAGGAGGGTAATATATATTTAACGCACGATATGGGACTATTAGCCTTCGACTTTG +TTATGTTGATGTGTGGCCTAAAATATTGGAGATACCAATATTTTAGGTTGCATCAACATCAATTCATCTTACTTCATTAA +AACACAGCGTGTTATTTCATGCTTCCGTGTACAGTTTCAATATTTGATTTCATCATTTTGTAGTAAGAGTCACCTTTAGT +GCCTTCTTTACCGATTGAATCTGTGTACACTTCACCAAAGATATCTTTCTTCGTTTCTTCAGATAAACTTTCCATTGCTT +TCTTATCAACACTTGTTTCTACTAATAAGTGTTTTAATTTGTGCTTTTTAACAAACTCAATAGCTTGTCTCATTTGTTCA +GGTGTACCTTGTTTTTCAGTGTTAATTTCCCAAATATAACCTGGTGTAATACCGTATTGTTTTGAGAAGTACTTGAAGGC +ACCTTCACTTGTAATCATGGCACGTTGTTCTTTTGGAATGTCATTAAATTTGTCTTTACTGTCTTTACTGTCATTATTTA +ATTTTTCCAATTGAGCAATGTATTTGTTACCTTGCTTTTCATAATCTGCTTTATGTTTTTTGTCGTTATCGATAAATGTT +TGTTGAATTGTTTTTACGTATTTAATACCGTTATCTAAACTTAACCATGCGTGTGGATCTTGTTTATCTTTGTTGCCTTC +TTCACCGTTTAAATAGATAGGTTTAACATCTTTTGATACTGCGATAACTTTTTTATCTTTTAATGATTTACCAGCCTGTT +CTAAGGCTTTTTCAAACCAACCGTTACCAGTCTCTAAATTTAATCCGTTGTATAAAATAACGTCAGCGTCAGTTAACTTT +TTAATATCTTTAGGTTTAACTTCATATTCATGAGGATCTTGACCAACAGGTACAATACTATGAATATCGACGTTGTCTCC +ACCAACATTTTTAGCCATATCATATAAAATTGAATTCGTCGTTACTACTTTTAATTTGCCATTTGACTTATCACTGCTTT +GTTTACCACCAGTACCACATGCAGCAACTAGAAGTAATAAGGCTAATAATAAAGGTACTAATTTTTTCATGTTAAACTTC +CTCGTTTCTTTCTATTCGTAAATTTTGTGAAAAATAATGTGATGATATAAATTACAAACGTACAAAGTACGATTGTCGCA +CCACTAGGAATGTTGTAAATATAGCTGTAATAAAGTCCGACAATTGAACTTATGACACTTATTAAACTTGCTATAATCAT +CATTGAGTATAGTTTTTTACTAATTAAAAATGCTGTAGATGCAGGTGTAATTAATAATGCAACTACAAGAATAATACCTA +CCGTTTGAATACTTGCTACTGTTACTAATGAGAGTAACAACATCACAAAGTAATGTAATAACGTCGTATTTAGACCACTC +ATTCTACTAAACGTTGGATCGAATGTAGAAATCATTAATGGACGATAGAAAATAATGATTAGAATAAGGACGATTGAACC +AATCACAATAGTTGTTAAAAATGCACTATTTGTGATTGCCAGTAAATTACCAAACAGAATATGGTACAAATCTGTCGTAG +TGTTTATTAAGCTAATAATAATAATCCCCGAAGCTAAGAAAGCGGTAAAACTAATTCCAATAGCGGCGTCAGGTTTCGTT +TTACTACTAGATGTGATATAACCGATAAAAATACTTGCGATCATACCAGTTATAAGTGCGCCTACAAACATTGGAATACC +AAATAAGAATGATAGGGCAACACCAGGTAATACTGCGTGACTCATTGCATCTCCCATTAATGAAAGACCACGTAATACAA +TTAAACTACCAACTGTACCACAAACTATCCCTACAATAATTGAAGTTATCAATGCTCGATTCAAGAATTGATATGTAAAT +AAATGTTCGACAAACTCTAACATGTTATATTGCTCCTTTGACTAGGGTCACTACAGTCAGTGCTACTCATAAATGTTTCG +TTTAAGCGAGTGACACTCATAGCCTCTTCACTATCACCAAAGTATCGTAATGTTTGATTTAATAGAATAATGCGATCAAA +GTATTGCTTTGCTTTTGATAGATCATGGTGGATGATAAGAATAAGTTTTCCTTGTTGTTTTAAGTTCTCGATTTTTGTCA +TGATTAATTTTTCGCTACTAAAATCAATTCCGACAAACGGCTCATCTAGAAAATAAACTTCACTTTCGGACATCAATGCT +CTTGCTACTAGCACACGTTGTAATTGTCCACCACTTAATTCTGAAATTTGTCGATGACGTAAAGATTCTAATTCTAAATC +GCTTAATAACTGTTTGAGTTTATCCCTTGCTGATTTATTAGGTCGTCTAAACCATCCAATTTCTTTGTAGCAACCTGATA +AAATCACTTGTTCCACACTTATAGGAAAATCTAAATCAATATGTGCTTTTTGTGGAATATATGTAATATGTTGCAGTTGT +TGTTGTATAGGTTTGTTATATAACAATTTAGTACCGGTAGCATTAAATTCACCAATTAAAGACTTGATAAGGGAAGATTT +ACCAGCACCATTCGGGCCCATGATACCAATTATTTCGCCGCGTACTGGTATCGATAAGGAAATGTTTTTAAGTACATGCT +TATTACCTAAAAACAGATTTAAATCTTTTGTTTCTAACAAACGTTTATACCTCCTAATTAAAAGTTTAGGCTAACCTAAT +TAATTGTATAATAAACTGAGAATATTTATCATGTCAAGTAAATTCGTGATATAATATAGACAATGTATGTGAGGTGAAAG +TATGTTAACTGAAGAAAAAGAGGACTATTTAAAGGCAATCCTTACGAATAATGGCGATAAAAACTTTGTGACAAATAAAA +TCTTATCTCAATTTTTAAATATTAAGCCTCCATCTGTAAGTGAAATGGTAGGACGTCTTGAAAAAGCAGGCTATGTTGAA +ACAAAACCATACAAAGGTGTTAGATTAACAGAGGATGGTTTAACGCATACGCTTGATATCATTAAGAGACATCGACTATT +AGAATTATTTTTAATAGAAATATTGAAATATAATTGGGAAGAAGTACATCAAGAAGCAGAAATTTTAGAACATCGAATTT +CAGATTTATTTGTTGAAAGGCTGGATAGCCTGTTAAATTTCCCAGAAACTTGCCCGCACGGCGGTGTGATTCCTAGAAAT +AATGAATATAAAGAGAAATATATAACAACGATTTTGAATTATGAACCTGGTGATATCGTTACAATCAAACGTGTGAGAGA +TAAGACCGATTTGCTAATATATTTGTCTAGTAAAGATATTTCTATTGGTAATGAAGTGGAAATTGTATCGAAAGATGAAA +TGAATAAAGTAATTATCATTAAACGTAATGATAATGTAATTATTGTCAGTTACGAAAATGCAATGAACATGTTTGCTGAA +AAATAAAATAAAGAAGCCATAAAGATATCCATGATTGAACTGATAAAGACATATGGATAATTGCTTTAGGCTTCTTTTTT +ATTAGTTAATTTATCAAGTGAGTATATTTGAGTAAAATATTCACTGCATAAAGATTGAAGATAATCCAGATTGTACTATA +AATGAAGATAGGTACATGACTGAGTTCTTTAAGTGCACTACCATCCCACTGTGGACTCGGACGCTGGAAAGTCAATTTAG +CAATCGTCCAACTAGATTGTAGAACTTCGCCTAATAATACACCTAAAATATATTGATAACTCATTGTGACAAGTAGTTGA +ATTTCTACTATATTTTCATCTTTTAATATAAAATACAACATGATAGAAATTAAAGTTATAACAACAATGGGTGAGCCTTT +TCTAGATGTTAAAATTAAAAAATAAATAAATATCAATAAATAGGTAAATATAAAGAAACTAGGTATCTGATAATGGCTCG +ACGCTAAACCTATCAATAACATAATAGGTGGCATAAAATAACCACCAATCGTTGTAAGCCATTGGCCTGCTAGATGTCTA +GATTGTGTAATTGCGAATCCTTGTTGTAATGTCTGTTGTCGCTCTCGTGGACTTGTTACAATGACTAAATCTTTTGCACG +GCCACCAGCGAGTTTATTAAACAGTACATGACCAAATTCATGTGTTAAAACAGGGATATAGTTTAAAATGACATCTAAAT +AGTTCAAAACAGGCTTATGTCTATATTGATGAATAGCAATATAACAAGCTGCAACAATAACGATAATGTATATATTAAGT +TGAATTGTCGTATTAAAAAAGTTTGATAAATAATTCATTGTTAACCTCATATAAGATATTAATTTAAAGTTTGCTTATCA +CTTATTATAAATGATATTGGCATCAATAGCGTTAGACTTTAGACTTACCTTAGTTAAACTAATTTTAATTTTTGAAAAGG +TGAATATGTGTTAAAATAAAGCAAAATCATTTCGATATAAATAGGATGAATATAAATACTGTTAATATTGATTACACTAA +CATAATAATGAAATAAGATAGGAGATTCCTGTTATGACTGTTGAAGAAAGATCCAATACAGCCAAAGTTGACATTTTAGG +GGTCGATTTTGATAATACAACAATGTTGCAAATGGTTGAAAATATTAAAACCTTTTTTGCAAATCAATCAACGAATAATC +TTTTTATAGTAACAGCCAACCCTGAAATAGTGAATTACGCGACGACACATCAAGCGTATTTAGAGTTAATAAATCAAGCG +AGCTATATTGTTGCTGATGGGACAGGAGTAGTCAAAGCTTCGCATCGTTTAAAGCAACCTCTAGCGCATCGTATACCTGG +TATTGAGTTGATGGATGAATGTTTGAAAATTGCTCATGTAAATCATCAAAAAGTATTTTTGCTAGGGGCAACTAATGAAG +TTGTAGAAGCGGCACAATATGCATTGCAACAAAGATATCCAAACATATCGTTTGCACATCATCACGGTTATATTGATTTA +GAAGATGAGACAGTAGTGAAACGAATTAAACTGTTTAAACCTGATTACATATTTGTAGGTATGGGATTCCCTAAACAAGA +AGAATGGATTATGACACATGAAAACCAATTTGAATCTACAGTGATGATGGGCGTAGGTGGTTCTCTTGAAGTATTTGCTG +GGGCTAAAAAGAGAGCGCCTTATATCTTTAGAAAATTAAACATTGAATGGATATATAGAGCATTAATAGATTGGAAACGT +ATTGGTAGATTAAAGAGTATTCCAATATTTATGTATAAAATAGCCAAAGCAAAAAGAAAAATAAAAAAGGCGAAATAATC +ATGATGACAAAAATAAAACCGAGGAAATCCTTAAATGGAGATTCTCGGTTTTTTCGGTTTATTTAATAACGAAGCGGGAC +TCATCGAGTTTGTTTCTAAATTCTTTTTGTTCGGCTTTGGATTTCTTTTTAAAATCGTTAAGGAAAGCTTCATATTTAGG +TAATACATCATCAAGTTCACCGTAATCTTTTAACTTTCCGCCTTCAATCCAAGCAATCTTAGTACAAAATTGTCTCACTT +GTCCTAAGTTATGACTAACGAAAAAGATGGTTTTGTTTTGCTCTTTAAACTCGTAAATTTTATCTAAACATTTTTGTGCA +AAAGTTTGGTCACCTACAGATAAAGCTTCGTCAATGACTAAGATATCTGGATTAACTGTGATATTAATTGAAAAACCAAG +TTTTGCACGCATACCACTTGAATACTTTTTAACTGGTTGATAAATAAACTCACCAAGTTCACTAAATTCAATAATCTTAG +GTGTCATCGCTTTAATTTCTTTTCGCTTAAAGCCCATACATAACATTTTAAATTCGATATTTTCAATCCCTGTAAGTTGT +CCACTCAAGCCAGCACTAATTGCGATAACGCTGACTTCACCATTACGATCCACTTTGCCAACAGTAGGCGACAAAGAACC +GCCAATGATATTGCTCAACGTTGATTTGCCGGAACCATTGATGCCAACAAGCCCTATGACGTCACCTTCATATGCTTTTA +AACTAATGTCATCTAAAGCGAAAAATGTTTTGTTTTTATGTTTGGGAATGAGCGCATCTTTCATACGTTCTTTATTTGTA +CGATAAATACGATATTCTTTTGTTACATTTTTAATGTTTACCGAAACGTTCATTTGTAGACCTTCCTTATTCACATTTAT +CTAGATTATAATATACTACTCAACAGTTGTTAAATTTTAAAACCTGTTGTAAAGTGTATAGAAGATTTTGTTATTATCAG +AGTGGGTGTTTTGACACAAAATGTTAATCATCAATGATAACAATGATATTTAAAAACTAAACTTATTTCAACTTACATGA +TTGTATACTATAATGTATTTGTAATAAACTAATATTTTAAAGAACTAGACAATAATTTTGATAGCATCCATGTATAGTGA +TAGTATTTACAACAATTATTATAATACTATTTAGTTAAGTAGAGAAATAGTTAAACATTTGAAAGTGTGGTTTAATGGAA +TGTCAGCAATAGGAACAGTTTTTAAAGAACATGTAAAGAACTTTTATTTAATTCAAAGACTGGCTCAGTTTCAAGTTAAA +ATTATCAATCATAGTAACTATTTAGGTGTGGCTTGGGAATTAATTAACCCTGTTATGCAAATTATGGTTTACTGGATGGT +TTTTGGATTAGGAATAAGAAGTAATGCACCAATTCATGGTGTACCTTTTGTTTATTGGTTATTGGTTGGTATCAGTATGT +GGTTCTTCATCAACCAAGGTATTTTAGAAGGTACTAAAGCAATTACACAAAAGTTTAATCAAGTATCGAAAATGAACTTC +CCGTTATCGATAATACCGACATATATTGTGACAAGTAGATTTTATGGACATTTAGGCTTACTTTTACTTGTGATAATTGC +ATGTATGTTTACTGGTATTTATCCATCAATACATATCATTCAATTATTGATATATGTACCGTTTTGTTTTTTCTTAACTG +CCTCGGTGACGTTATTAACATCAACACTCGGTGTGTTAGTTAGAGATACACAAATGTTAATGCAAGCAATATTAAGAATA +TTATTTTACTTTTCACCAATTTTGTGGCTACCAAAGAACCATGGTATCAGTGGTTTAATTCATGAAATGATGAAATATAA +TCCAGTTTACTTTATTGCTGAATCATACCGTGCAGCAATTTTATATCACGAATGGTATTTCATGGATCATTGGAAATTAA +TGTTATACAATTTCGGTATTGTTGCCATTTTCTTTGCAATTGGTGCGTACTTACACATGAAATATAGAGATCAATTTGCA +GACTTCTTGTAATATATTTATATGACGAAACCCCGCTAACCATTAATAAATGGAAGTGGGGTTCATTTTTGTTTATAATT +TAAGTAAATAACATATTAAGTTGGTGTATTATGAACGTTTTAATAAAGAAATTTTATCATTTGGTAGTTCGAATACTTTC +TAAAATGATTACGCCTCAAGTGATTGATAAACCGCATATCGTATTTATGATGACTTTTCCAGAAGATATTAAGCCTATCA +TCAAAGCATTAAATAATTCGTCGTATCAGAAAACTGTTTTAACAACACCAAAACAAGCGCCTTATTTATCTGAACTTAGC +GACGATGTTGATGTGATAGAAATGACTAATCGAACATTGGTAAAACAAATTAAGGCTTTGAAAAGCGCGCAGATGATTAT +TATCGATAATTATTACCTATTGCTAGGTGGATATAATAAGACTTCTAATCAACACATTGTTCAAACGTGGCATGCAAGTG +GTGCATTAAAAAACTTTGGCTTAACAGATCATCAAGTCGATGTGTCTGACAAGGCAATGGTTCAGCAGTACCGTAAAGTT +TATCAAGCGACGGATTTTTACTTAGTGGGTTGTGAACAAATGTCACAATGTTTTAAACAGTCTTTAGGTGCAACAGAAGA +GCAAATGCTGTATTTTGGGCTTCCGAGAATTAATAAATATTACACAGCTGATAGAGCAACGGTTAAGGCAGAGTTAAAGG +ATAAATATGGAATTACAAATAAGTTGGTATTATATGTACCAACATATAGAGAAGATAAAGCAGATAATAGGGCTATTGAT +AAAGCTTATTTTGAAAAATGTTTACCAGGATATACACTGATTAATAAATTACATCCATCAATTGAAGATTCAGACATTGA +TGACGTATCTTCAATCGACACGTCTACATTAATGCTAATGTCAGATATAATTATTAGCGACTATAGTTCGCTGCCAATAG +AAGCTAGCTTGTTAGATATTCCAACTATATTTTATGTGTATGATGAAGGAACATATGATCAGGTGAGAGGCCTGAATCAA +TTTTACAAAGCAATACCGGATAGCTACAAAGTGTATACTGAAGAAGATTTAATAATGACGATACAAGAAAAAGAACATCT +ATTAAGTCCGTTATTTAAAGATTGGCATAAGTATAATACTGATAAAAGTTTACATCAGCTCACAGAATATATAGATAAGA +TGGTGACAAAATGAGGTTTACGATAATCATACCTACATGTAATAATGAGGCAACAATTCGACAATTGTTAATATCTATTG +AGAGTAAAGAACACTATAGAATCCTTTGTATTGATGGTGGTTCTACTGATCAAACAATTCCTATGATTGAACGGTTACAA +AGAGAACTCAAGCATATTTCATTAATACAATTACAAAATGCTTCGATAGCTACGTGTATTAATAAAGGTTTGATGGATAT +CAAAATGACAGATCCACATGATAGTGACGCATTTATGGTCATAAAACCAACATCAATCGTATTGCCAGGTAAATTAGATA +GGTTAACTGCTGCTTTCAAAAATAATGATAATATTGATATGGTAATAGGGCAGCGAGCTTACAATTACCATGGTGAATGG +AAATTGAAAAGTGCTGATGAGTTTATTAAAGACAATCGAATCGTTACATTAACGGAACAACCAGATTTGTTATCAATGAT +GTCTTTTGACGGAAAGTTATTCAGTGCTAAATTTGCTGAATTACAGTGTGACGAAACTTTAGCTAACACATACAATCACG +CAATACTTGTCAAGGCGATGCAAAAAGCTACGGATATACATTTAGTTTCACAGATGATTGTCGGAGATAACGATATAGAT +ACACATGCTACAAGTAACGATGAAGATTTTAATAGATATATCACAGAAATTATGAAAATAAGACAACGAGTCATGGAAAT +GTTACTATTACCTGAACAAAGGCTATTATATAGTGATATGGTTGATCGTATTTTATTCAATAATTCATTAAAATATTATA +TGAACGAACACCCAGCAGTAACGCACACGACAATTCAACTCGTAAAAGACTATATTATGTCTATGCAGCATTCTGATTAT +GTATCGCAAAACATGTTTGACATTATAAATACAGTTGAATTTATTGGTGAGAATTGGGATAGAGAAATATACGAATTGTG +GCGACAAACATTAATTCAAGTGGGCATTAATAGGCCGACTTATAAAAAATTCTTGATACAACTTAAAGGGAGAAAGTTTG +CACATCGAACAAAATCAATGTTAAAACGATAACGTGTACATTGATGACCATAAACTGCAATCCTATGATGTGACAATATG +AGGAGGATAACTTAATGAAACGTGTAATAACATATGGCACATATGACTTACTTCACTATGGTCATATCGAATTGCTTCGT +CGTGCAAGAGAGATGGGCGATTATTTAATAGTAGCATTATCAACAGATGAATTTAATCAAATTAAACATAAAAAATCTTA +TTATGATTATGAACAACGAAAAATGATGCTTGAATCAATACGCTATGTCGATTTAGTCATTCCAGAAAAGGGCTGGGGAC +AAAAAGAAGACGATGTCGAAAAATTTGATGTAGATGTTTTTGTTATGGGACATGACTGGGAAGGTGAATTCGACTTCTTA +AAGGATAAATGTGAAGTCATTTATTTAAAACGTACAGAAGGCATTTCGACGACTAAAATCAAACAAGAATTATATGGTAA +AGATGCTAAATAAATTATATAGAACTATCGATACTAAACGATAAATTAACTTAGGTTATTATAAAATAAATATAAAACGG +ACAAGTTTCGCAGCTTTATAATGTGCAACTTGTCCGTTTTTAGTATGTTTTATTTTCTTTTTCTAAATAAACGATTGATT +ATCATATGAACAATAAGTGCTAATCCAGCGACAAGGCATGTACCACCAATGATAGTGAATAATGGATGTTCTTCCCACAT +ACTTTTAGCAACAGTATTTGCCTTTTGAATAATTGGCTGATGAACTTCTACAGTTGGAGGTCCATAATCTTTATTAATAA +ATTCTCTTGGATAGTCCGCGTGTACTTTACCATCTTCGACTACAAGTTTATAATCTTTTTTACTAAAATCACTTGGTAAA +ACATCGTAAAGATCATTTTCAACATAATATTTCTTACCATTTATCCTTTGCTCACCTTTAGACAATATTTTTACATATTT +ATACTGATCAAATGAGCGTTCCATTAATGCATTCCCCATCATATTACGTTGCTTCTCGCCACCAAGGTTTTTATAGTCTC +CTGCACCCATGATAACTTGATTAATTCTAAATTTACCTCGTTTGGTAGTAATCGTATGGTTGTAATTTGCTGTATCACTT +GATCCAGTTTTTAAACCATCTGTACCCGGCAAACTCATTTTTGCACCTTCCAATGAAAAGTTGAATGTGTAATACGTAAC +TGCATGCGTTGTTGGTGCTAACTGCTTTGTAAAGTCTAATATTTTAGGTGTCTCTTTAATCACGTGTAAATCTAAAATGG +CATAGTCTCTAGCAGTCGTTACAGTACGTTCTTGGTCTTTATACTTTGTTGGTGCAAATGTACGTAATCTTGAATTTTCA +GCACCCGTTGGATTGACGAAATGTGTATTTTTCATTCCGATAGCTTTAGCTTTGTTATTCATTAAATCAACGAAATCGCT +GGTGTTTTTTGAAACCTTCTTAGCTAAAATTAATGCCGCGGCATTACTAGAATTAGATACTGTAATTTGTAATAGGTCTG +CGATTGTCCATACTTGTCCAGGATATAGTTTCGTATTACTCAACTCAGGTAGTGTAGACATAATATATTCTTTGTTCGTC +ATTGTGACTGTGTCATCAAGTGAAAGCTGCCCCTTATTTACAGCTTCCAATGTTAAGTACATTGTCATTAATTTAGTCAT +AGACGCTGGATTCCACTTAGTATCGATATTGTATTGATACAGTAATTGTCCAGTTTGACTTACATTAACAGCACTCGTCG +GTTCGTATGCAGCCGACAAACCTGCATAACCATATTGATTTGCTGCTTGTACAGGGGTTACGTCACTGTTAGTAGCTTGT +GCATATGGTGTCATAATACTTAATGTTAAACATAAAATGATGATAATAGATATTAAATTTTTCATAAAGCGTTAATCTTC +CCTTTTCCAATTCTTAAATATTCCCTAAAAGCAATGGTTATTCCTACTTACGGAAATCATTGCTAATTCACTTCACCTTA +ATTAAATTGTTGAAAATAAAGTTTTCTGCAGTTAATTTGAAAAATAATGCAAATATATTACGTGTGTAGCTAAAGGTGTT +ATAATGTTTGTACGAAGAGCAAACTTACTCAAAAGCGATTAATTTTCATGTTTTAATATAAAGACTTTGAGAAGTTATTA +CAAAAAATGCAATAGAAATATTCTATCATATAAATGTTATGAGCGGTATTTTGGGGCAACACTTTATTTGATTTTTAAAG +TTTTGTTGGGAGAAAGTATATGATAGAAATGCATGTATCTATCTAAATGAATTAACTATAAATTTCAAACAGAAGAGGTA +AAACTATGAAACGAGAAAATCCATTGTTTTTCTTATTTAAAAAACTATCATGGCCAGTGGGTCTTATCGTTGCAGCTATC +ACTATTTCATCACTAGGGAGCTTAAGTGGACTATTAGTGCCACTGTTTACTGGACGAATTGTAGATAAATTTTCCGTGAG +CCATATCAATTGGAATCTAATCGCATTATTTGGTGGTATCTTTGTCATCAATGCTTTATTAAGCGGATTAGGTTTATATT +TATTAAGTAAAATTGGTGAAAAGATTATTTATGCGATACGCTCAGTTTTATGGGAGCATATCATACAATTAAAAATGCCA +TTCTTTGACAAAAATGAAAGTGGTCAATTAATGAGTCGATTAACTGACGATACGAAAGTGATAAATGAATTTATTTCACA +AAAGCTACCTAACTTATTACCATCAATCGTTACATTAGTTGGGTCACTAATCATGTTATTTATTTTAGATTGGAAAATGA +CATTATTAACATTTATAACGATACCGATATTCGTTTTAATTATGATTCCTCTAGGTCGTATTATGCAAAAGATATCGACA +AGTACACAATCTGAAATTGCAAACTTCAGTGGTTTGTTAGGGCGTGTCCTAACTGAAATGCGTCTTGTTAAAATATCAAA +TACAGAGCGTCTTGAATTAGATAATGCACATAAAAATTTGAATGAAATATATAAATTAGGTTTAAAACAGGCTAAAATTG +CGGCAGTTGTACAACCAATTTCAGGTATAGTTATGTTGCTAACAATTGCAATTATTTTAGGTTTTGGTGCATTAGAAATT +GCGACTGGTGCAATCACTGCAGGTACATTAATTGCAATGATATTTTATGTTATTCAGTTATCTATGCCTTTAATCAATCT +TTCCACGTTAGTTACAGATTATAAAAAGGCAGTCGGTGCAAGTAGTAGAATATACGAAATCATGCAAGAACCTATTGAAC +CGACAGAAGCTCTTGAAGATTCTGAAAATGTATTAATTGATGACGGTGTATTGTCATTTGAACATGTAGACTTTAAATAT +GATGTGAAGAAAATATTAGATGATGTGTCGTTCCAAATCCCACAAGGTCAAGTGAGTGCTTTTGTAGGCCCTTCTGGGTC +TGGTAAAAGTACGATATTTAATCTGATAGAACGTATGTATGAAATTGAGTCAGGTGATATTAAATATGGCCTTGAAAGTG +TCTATGATATCCCGTTATCTAAGTGGCGACGCAAAATTGGATATGTTATGCAATCAAATTCGATGATGAGTGGTACAATT +AGAGACAATATTTTATACGGAATTAATCGTCATGTTTCAGATGAAGAACTTATTAATTATGCTAAATTAGCGAACTGTCA +TGATTTTATCATGCAATTTGATGAAGGATATGACACGCTTGTAGGTGAACGAGGATTGAAACTGTCTGGCGGACAACGTC +AACGTATTGATATTGCTAGAAGTTTTGTTAAAAATCCTGATATTTTGTTACTTGATGAAGCAACAGCTAATCTCGATAGT +GAAAGTGAATTGAAAATTCAAGAAGCTTTAGAAACATTGATGGAAGGTAGAACAACGATTGTCATTGCGCATCGTTTGTC +TACAATTAAAAAAGCCGGTCAAATTATATTCTTAGACAAAGGACAGGTAACAGGTAAAGGTACGCATTCAGAACTGATGG +CATCACATGCGAAGTATAAAAACTTTGTAGTGTCTCAAAAATTAACAGATTAATTTTATATATATAAGTAAGCTTGGAGC +AAATACACATATACCATCGAGGAAATTAAAGTGTGGCACATTGATGGATATAGATGTTAATAAATTGCTTCAAGCTTTTG +TCTATTTTAAATCATTTGAGAAGTTACGACATAATAATTCTTAAATTAATGAAATCGATATTTTAAGAAAAAAATGCTCA +TGGTATAATACAAGTTATAAGCAAACATACATATATTAAATACTGTAGCCACGAGTCATAATTCTTCATATTTTACATAG +CAATTTAACTGATTTTAGAGTCCACGGTACAGAAGTTTGATATTTCAATGTTTCTAAATTTTTAAAAAATTAAATCATAG +GTGGGTGCCAAATGTTTTTATTAATCAACATTATTGGTCTAATTGTATTTCTTGGTATTGCGGTATTATTTTCAAGAGAT +CGCAAAAATATCCAATGGCAATCAATTGGGATCTTAGTTGTTTTAAACCTGTTTTTAGCATGGTTCTTTATTTATTTTGA +TTGGGGTCAAAAAGCAGTAAGAGGAGCAGCCAATGGTATCGCTTGGGTAGTTCAGTCAGCGCATGCTGGTACAGGTTTTG +CATTTGCAAGTTTGACAAATGTTAAAATGATGGATATGGCTGTTGCAGCCTTATTCCCAATATTATTAATAGTGCCATTA +TTTGATATCTTAATGTACTTTAATATTTTACCGAAAATTATTGGAGGTATTGGTTGGTTACTAGCTAAAGTAACAAGACA +ACCTAAATTCGAGTCATTCTTTGGGATAGAAATGATGTTCTTAGGAAATACTGAAGCATTAGCCGTATCAAGTGAGCAAC +TAAAACGTATGAATGAAATGCGTGTATTAACAATCGCAATGATGTCAATGAGCTCTGTATCGGGAGCTATTGTAGGTGCG +TATGTACAAATGGTACCAGGAGAACTGGTACTAACGGCAATTCCACTAAATATCGTTAACGCGATTATTGTGTCATGCTT +GTTGAATCCAGTAAGTGTTGAAGAGAAAGAAGATATTATTTACAGTCTTAAAAACAATGAAGTTGAACGTCAACCATTCT +TCTCATTCCTTGGAGATTCTGTATTAGCAGCAGGTAAATTAGTATTAATCATCATCGCATTTGTTATTAGTTTTGTAGCG +TTAGCTGATCTATTTGATCGTTTTATCAATTTGATTACAGGATTGATAGCAGGATGGATAGGCATAAAAGGTAGTTTCGG +TTTAAACCAAATTTTAGGTGTGTTTATGTATCCATTTGCGCTATTACTCGGTTTACCTTATGATGAAGCGTGGTTGGTAG +CACAACAAATGGCTAAGAAAATTGTTACAAATGAATTTGTTGTTATGGGTGAAATTTCTAAAGATATTGCATCTTATACA +CCACACCATCGTGCGGTTATTACAACATTCTTAATTTCATTTGCAAACTTCTCAACGATTGGTATGATTATCGGTACATT +GAAAGGCATTGTTGATAAAAAGACATCAGACTTTGTATCTAAATATGTACCTATGATGCTATTATCAGGTATCCTAGTTT +CATTATTAACAGCAGCTTTCGTTGGTTTATTTGCATGGTAATATGTCGAAGAGTGACTATGATAATACATTTTAACTAAT +AAATATGTCCAGGCATGTCGTCTATTGATATAGGTGAGATGCTTGGACTTTTTTATTATTGATATAAAGGTATTTAAATA +TTTTTAAAGTTACCGAAATTGAAGCATTATAAAAACCAAGTGCACATGGTAATACACTTGGCTTTTATGGGAAATGAATA +TTATTGTACATATGACAGTAAGGACTAGGTACAGTCATAGTACTTCGAGCAAAATTTGTTTTGTTATTATAAACAACACA +AAGGAGATAACTTCTCTATTGAAGAAGTTAAAAACATTATAGCAGACAATGAAATGAAAGTAAATTAAAAATTCAGAATA +TTTTTAATTATTATATTGTGAGTGATATTTATTAGGGAAAGCTATTCTTCATATAAATTAGTTAAATAGTAAATCTTTGT +TAGAACTCTTTCCATAGAGTTGTGACAGCATTTTTGTTGTGCTACTATTTTTATAGTCTAATAAATATATAAAGGGGATG +GTTTCGTGAATAAAACGGTTAAAGATTTAATACTAGTTGTCTTAGGTTCATTTATCTTTGCTGCAGGTGTAAATGCATTT +ATTATTTCTGGTAACTTAGGTGAAGGCGGGGTTACAGGTTTAGCAATTATTTTATATTATGCGTTTCATATTTCACCAGC +CATCACTAACTTCTTGGTCAACGCAGTATTGATTGCCATAGGTTATAAATTTTTGAGTAAGAGAAGTATGTACTTAACTA +TTCTTGTAACAATTCTTATTTCAATATTTTTGAGTTTAACAGAATCATGGCAAGTAGAAACTGGAAACAGCATTGTGAAT +GCCATTTTTGGTGGTGTAAGCGTTGGACTAGGAATCGGAGTAATTATCCTTGCAGGCGGTACAACAGCAGGTACAACAAT +TTTGGCGAGAATTGCAACGAAATACCTCGATGTAAGCACGCCATATGCTTTGCTTTTCTTCGATATGATCGTTGTTGCAA +TTTCACTTACAGTTATTCCACTTGATAAAGTATTAGTAACAGTAATATCACTTTATATAGGAACAAAAGTGATGGAATAT +GTCATAGAAGGTTTAAACACTAAAAAAGCTATGACGATTATTTCAACTAATCCCGACAAACTTGCCAAAGCAATAGACGA +GCAAATTGGAAGAGGTTTAACCATTTTAAACGGACATGGCTATTATACGCGTGAAGAAAAAGATGTCTTATACGTTGTTA +TTTCTAAAACACAAGTTTCAAAAGCAAAGCGATTAATTAAACAAATCGATAAAGATGCATTCCTCGTAATTCATGATGTA +AGAGATGTCTATGGTAATGGCTTTCTTGCAGATGAATAAATAAATGGTATGAGCACACATACTTAAATAGAAGTCCACGG +ACAAGTTTTTGAACTATGAAGACTTATCTGTGGGCGTTTTTTATTTTATAAAAGTAATATACAAGACATGACAAATCGAG +CTATCCAATTTAAAAAGTAATGTTAGTCAATAAGATTGAAAAATGTTATAATGATGTTCATGATAATCATTATCAATTGG +GATGTCTTTGAAAATTGATAATTTAAAAATAGAAATTATTTTTTATAAACAGAAAGAATTTTATTGAAAGTAGGGAAATT +ATGAATCGTTTGCATGGACAACAAGTTAAAATTGGTTACGGGGATAACACGATTATAAATAAATTAGATGTTGAAATACC +AGATGGCAAAGTGACGTCAATCATTGGTCCTAACGGCTGCGGGAAATCTACTTTGCTAAAGGCATTGTCACGTTTATTGG +CAGTTAAAGAAGGCGAAGTATTTTTAGATGGTGAAAATATTCATACACAATCTACGAAAGAGATTGCAAAAAAAATAGCC +ATTTTACCTCAATCACCTGAAGTAGCAGATGGCTTAACTGTTGGGGAATTAGTTTCATATGGTCGTTTTCCACATCAAAA +AGGATTTGGTAGATTAACTGCTGAGGATAAGAAAGAAATTGATTGGGCAATGGAAGTTACAGGAACTGATACATTCCGAC +ACCGTTCAATCAATGATTTAAGTGGTGGTCAAAGACAACGTGTTTGGATTGCAATGGCATTAGCACAAAGAACTGATATT +ATCTTTTTAGACGAACCAACAACATATTTAGATATCTGTCATCAATTAGAAATACTAGAATTAGTTCAGAAGCTAAATCA +GGAACAAGGTTGTACAATTGTCATGGTTCTTCATGATATCAACCAAGCGATTCGTTTCTCAGATCATCTTATTGCGATGA +AAGAAGGGGATATCATCGCTACAGGTTCAACAGAAGACGTATTAACACAGGAAATATTAGAAAAAGTTTTTAATATTGAT +GTTGTTTTAAGTAAAGATCCTAAAACTGGAAAACCTTTACTGGTAACTTATGACTTATGTCGCAGAGCTTATTCTTAATT +AAGTAAGTTAATATGATAAAAAGGACAATTAACATGACAAATAGAGAGAACCCAACGCCATTGAAGTTTTTATCCTATAT +TATAGGTTTAAGTATGATACTACTAATCACACTATTTATTTCTACATTAATAGGTGACGCCAAAATTCAAGCCTCTACAA +TTATAGAGGCTATTTTTAATTATAATCCTAGCAATCAACAGCAAAACATCATCAATGAGATTAGGATTCCCAGAAATATA +GCAGCAGTAATTGTAGGTATGGCGCTTGCAGTTTCTGGTGCGATTATACAAGGTGTTACTCGTAATGGTCTTGCTGATCC +GGCGCTCATAGGTTTAAATTCAGGTGCTTCATTTGCTTTAGCATTAACATATGCAGTTTTACCAAACACTTCATTTTTAA +TATTGATGTTTGCTGGATTTTTAGGTGCTATTCTAGGAGGTGCTATTGTATTAATGATAGGCCGATCTAGACGTGATGGA +TTTAATCCGATGCGTATTATTTTAGCGGGTGCAGCAGTAAGTGCTATGTTAACAGCGCTAAGTCAAGGTATTGCATTAGC +TTTTAGACTAAATCAAACAGTAACATTTTGGACTGCTGGAGGCGTTTCAGGCACAACATGGTCACACCTTAAGTGGGCAA +TTCCATTAATTGGTATTGCGTTATTCATTATATTAACAATTAGTAAACAACTTACCATTTTAAATCTTGGTGAATCATTA +GCTAAAGGTTTAGGTCAAAATGTAACAATGATCAGAGGCATATGTTTAATTATTGCTATGATTCTAGCAGGTATTGCAGT +TGCTATCGCTGGACAAGTTGCATTTGTAGGTTTGATGGTACCTCATATAGCAAGATTTTTAATTGGAACTGATTATGCTA +AAATTCTACCATTAACAGCCTTGTTAGGTGGGATACTCGTGCTTGTTGCCGATGTGATAGCACGATATTTAGGAGAAGCG +CCTGTTGGTGCAATCATTTCATTTATCGGTGTTCCTTACTTTTTATATTTAGTTAAAAAAGGAGGACGCTCAATATGATT +AGTTCAAATAATAAACGCAGACAATTGATAGCACTGGCTGTTTTTAGCATTCTACTATTTCTAGGTTGTACTTGGAGTAT +TACCTCAGGTGAATACAACATACCTGTTGAAAGATTTTTCAAAACTTTAATTGGACAAGGTGATGCCATTGATGAGTTAA +TCTTATTAGATTTCAGGTTACCTCGGATGATGATTACTATTTTGGCTGGCGCAGCGCTTAGTATTAGTGGTGCAATAGTG +CAAAGTGTCACAAAAAATCCAATAGCTGAACCAGGTATATTAGGTATTAACGCAGGTGGCGGATTTGCAATCGCATTATT +TATTGCAATTGGTAAAATTAATGCTGACAACTTTGTTTATGTACTGCCGTTAATAAGTATACTAGGTGGTATCACCACTG +CATTGATTATTTTTATTTTCAGTTTTAATAAAAATGAAGGTGTTACACCTGCGAGTATGGTATTAATAGGTGTAGGTTTA +CAAACAGCATTATATGGTGGCTCAATTACAATTATGTCAAAATTTGATGATAAGCAATCTGATTTCATCGCTGCTTGGTT +TGCAGGTAATATTTGGGGTGACGAATGGCCATTTGTCATTGCATTTTTACCGTGGGTGTTGATTATTATTCCTTACTTAC +TATTTAAATCGAATACACTAAATATTATTCATACGGGTGATAATATTGCACGAGGTCTAGGTGTAAGGTTAAGCAGAGAA +CGTTTAATATTATTCTTTATCGCAGTGATGTTATCATCTGCTGCTGTAGCAGTAGCAGGTTCAATTTCGTTTATCGGATT +AATGGGTCCGCATATTGCCAAACGTATCGTTGGACCACGTCACCAGTTGTTTTTACCAATTGCCATTTTAGTAGGGGCAT +GTTTACTTGTTATAGCTGATACAATTGGCAAAATTGTATTACAACCAGGTGGGGTTCCAGCAGGTATTGTCGTAGCAATT +ATTGGTGCACCGTATTTCTTATATTTAATGTACAAAACGAAAAATGTATAGTGTCAATGGACACAACTTATTGCTATGAA +AGGCACTTTATTATAAGGCTTTTCATAGCATTTTTTATTTAATGAGCCACTCAAGACTATTTATTTTTTCAATAATGAAC +CATTAAGTTATCAAGAGGATCTTATCAAAAATATATTTGATAACGGTATCAGGTTAATTCTTTATGATAGCGCATTCATT +TATTCTGTTTTATACTATGACTGATAATACCAAGGAGGTACAACATGATGAAAAAGTTAATCAATAAAAAAGAAACATTT +TTAACTGATATGCTTGAAGGATTGTTAATTGCGCACCCAGAGTTAGATCTGATTGCTAATACAGTTATTGTAAAAAAAGC +TAAGAAAGAACATGGTGTAGCAATAGTCTCTGGAGGTGGAAGCGGACATGAACCTGCGCATGCCGGTTTTGTTGCAGAAG +GTATGCTAGATGCAGCGGTTTGTGGCGAAGTATTTACATCACCTACACCTGATAAAATATTAGAAGCTATTAAAGCAGTA +GATACTGGTGATGGTGTATTACTAGTTGTAAAAAACTATGCAGGTGACGTGATGAATTTCGAAATGGCACAAGAGCTTGC +AGAAATGGAAGGTATAAATGTTCAAACTGTTATTGTTCGTGACGACATTGCTGTGACAAACGAAGTACAACGTCGTGGTG +TTGCAGGAACAGTGTTTGTTCATAAGCTTGCCGGTTATCTTGCTGAAAAAGGTTATTCATTAACAGAGATAAAATCGCGT +GTAGAAGCGTTGTTACCTGAAATTAAAAGTATTGGTATGGCAATTGAGCCACCGCTTGTTCCAACTACTGGAAAATATGG +CTTTGATATTGAAGACGACAAAATGGAAATCGGTATTGGTATACATGGTGAAAAAGGTATTCATAGGGAAGAAGTAAAGG +ATATTGATCATATTGTTGGAACATTGTTAGACGAATTGTATAAAGAAGTTACTGCCAATGATGTCATATTAATGGTAAAT +GGTATGGGTGGTACGCCGTTATCTGAATTAAATATCGTAACTAAATATATTCAACAAAATTTAGCTGCAAGAACGGTTAA +TGTTGCTAAATGGTTTGTTGGTGATTATATGACATCTTTAGACATGCAAGGTTTTTCTATAACTATCGTGCCTAATAAAC +CAGAATATTTGGAAGCATTTTTAGCACCAACAACAAGTCAATACTTTAAATAAGAAGTGAATATGAATAAATATACATTT +ATGAGGTGGCACAAATGAAAGTGAATGATATGAAAGCACGTTTATTAAATTTAGAAGAAACGTTTAAAAAACATGAATCT +GAATTAACTGAATTAGATCGAGCAATTGGTGATGGTGACCACGGGGTTAACATGGTTCGTGGGTTTAGTAGTCTTAAAGA +CAAACTTGATGATAGCTCAATGCAATCATTGTTCAAATCAACTGGTATGGCATTGATGTCAAATGTTGGGGGTGCATCAG +GACCACTGTATGGCTTTAGCTTTGTTAAAATGTCTGCAGTCACCAAAGATGATATGGATAATCAAGATTTCATTACACTA +ATTCAGGCATTTGCCGAAGCGGTTGAATCACGTGGTAAAGTTACTTTAAATGAAAAGACAATGTATGATGTAGTAGCGCG +AGCAGCAGAGAAGCTTAAAAATGGTGAAACTTTAACATTCAATGATTTACAGCAATTAGCAGATAATACAAAAGATATGG +TAGCAACGAAAGGTAGAGCTGCATATTTTGGAGAAGAATCAAAAGGTTATATTGATCCAGGTGCTCAAAGTATGGTTTAT +ATTTTAAACGCTTTGATTGGAGATGAAGATAATGCCTAAAATTATACTTGTTAGCCACAGTAAAGAAATTGCAAGTGGTA +CAAAATCTTTGTTAAAGCAAATGGCAGGTGACGTTGATATTATACCAATCGGGGGATTACCAGATGGTTCAATTGGAACT +TCATTTGATATCATCCAAGAAGTTTTGACTAAATTAGAGGATGATGCATTGTGTTTTTACGATATTGGATCTTCAGAAAT +GAATGTAGATATGGCAATTGAAATGTATGATGGTAATCATCGTGTGTTAAAAGTTGATGCACCAATTGTTGAAGGCAGTT +TTATCGCAGCAGTAAAGCTATCAATCGGCGGTTCAATTGATGATGCATTAGCAGAAATCAAACAATCATTTTAGTTAAAA +TTTACTAATAATGAAAAATGTAAACCTTTTTCAAATGAAACTTTATAAAAAATATGATAGTATATATGTAAATGTTTAAT +AAAATCTGGAGAAATAGGAGGACATTGCCATGCAACACCTTATAAAAAAACATGTATTGAATGGCGAGTTTGATTTAGTA +CGACAATTGATGTCCGAAACAGATTTTATGGAATTTGAAGAAGCATATATTTCAAGTGCGCATGAAGTAGAAAGTATGAT +GTTTTATACATGTATTTTAGATATGATTAAGTACGAAGAATCATCTGAAATGCATGACTTAGCATTTTTATTGCTTGTGT +ATCCACTAAGTGAATATGAAGGTGCTTTGGATTCTGCTTATTATCATGCAGACGCTTCCATAAAACTTACTGACGGCAAA +GAAGTTAAAAGTTTGTTACAAATGTTATTATTGCATGCGATACCAACACCTGTTATTTCAGATAAGAAGGCTTTTGATAT +CGCCAAGCAAATTTTAAAATTAGATCCTAATAATAATGTTGCTCGTAACGTCTTAAAAGACACTGCCAAACGTATGGACA +ACGTTGTTGTTGATATAAATGAATTACACCAACGTAATGCACGTTAATTACATTTCAATTATATTAGCTTAATAATAGTT +TTAACATTTGGTTGGGTTGGGCATATGTTCCAGCCTTTTTTAATACTTAAAAACTAACGAAGTATACTTGTGTGCACAAA +TGGTTTTTATACAACATTTTATAAATTTATACATTTTAATAAAGAACATACGATAGATGGTTTAAACCTTGTTAACTGAG +AAATTTTGATATGTATTCTTCGAAATTTAACTAAATATACGAAATTCAAGAAGCACAATAATTAATCATTTTTCCTATAC +AAAAGTTCGTATGACTGCATTATAAAAGCATAAATTTATAATTTTTTTAAATGTCATTGAACGTGATAATGTGAATGGAT +TGAGCAATTTTGAAAAAGTGAAAAATAACCTATGCGACTTGCAATTAATTTTCAGTACGTTATAATGCACACTGTGCAAA +ATTAAGGAGGTCTATTATTCACATGATGATGAATAAAGAAGCAACAAAAATTGGATTTGCCTACGTCGGCATTGTAGTGG +GCGCAGGATTTTCAACTGGACAAGAAGTTATGCAATTTTTCACTAAATATGGCTTGTGGGCTTATTTAGGTGTTATTATA +TCTGGTTTTATTTTAGCTTTTATTGGGCGCCAAGTAGCAAAAATTGGTACTGCCTTTGAAGCGACAAATCATGAATCAAC +ATTACAATACGTATTCGGTGAAAAGTTTAGTAAAGTCTTTGATTATATTTTAATCTTCTTCTTATTTGGTATAGCTGTAA +CCATGATAGCTGGTGCAGGCGCAACATTTGAAGAAAGTTATAACATACCTACATGGCTAGGTGCTTTAATTATGACATTA +GCGATTTATATTACGTTGCTATTAGACTTTAATAAAATAGTACGTGCACTAGGTATCGTTACACCATTTTTAATTGTTTT +AGTTGTATTAATCGCTGGCGTTTATTTATTTAAAGGTCATGTTTCATTAGCAGAAGTTAACCAAGTAGTGCCTGAAGCAA +GTATTTGGAAGGGAATCTGGTTTGGTACAATATATGGTGGATTAGCTTTTTCTGTAGGTTTTAGTACCATCGTAGCAATC +GGTGGGGATACTGAAAAGCGTACAGTGTCAGGTGCAGGCGCGATGTATGGTGGTATTATCTATACTGTATTACTAGCATT +GATCAACTTTGCATTGCAAAGTGAATATCCAACTATTAAAAATGCCTCAATTCCTACATTGACGTTAGCAAATAATATCC +ATCCTTTAATAGCAACAGTGTTATCTGTTATTATGCTGGCGGTTATGTATAATACTATTCTAGGACTAATGTATTCATTT +GCAGCACGTTTTACAGAACCATACAGTAAAAATTATCATATCTTTATTATTATAATGATGGTAGCAGGTTATTTATTAAG +TTTCGTAGGATTTGCTGAATTAATTAATAAGTTATATACAATTATGGGATATGTAGGCTTATTTATTGTAGTAGCTGTAA +TTATTAAATATTTCAAACGTAAAAATGCGGATAAAAAACATATTGCTTAATATCATATGAGGGATATCCGAAACTTTACA +ATTGAATCACTTTGTTTTAACCTTAAAAGCAATTCGTCTCTACTCTTATCGGGCGAATTGCTTTTTATATTTATTCAGTC +TATTAATATGAGCGTCTAACAAATAGAGAGGTACGATGTAATGAATAAAGATAATAAATGGACGATGATAACTGCGCTTT +TTATAACTGTAATCAGTGTATTGTTAGCATTTCATCTGAAACAACATTATGACCAAATTACAAATGAGAACCATGCTAAT +AAAGACAAAATTAATATTAAAAATAAAAATGTGCGCATTTATCAAAACCTTACATACAATAGAGTTTTCCCTAACAGTAA +ATTAGATATTATTACACCTGTTGATATGTCTTCTAATGCCAAACTGCCAGTTATTTTTTGGATGCACGGTGGTGGTTATA +TTGCGGGTGATAAGCAGTATAAAAACCCATTATTAGCGAAAATTGCTGAACAAGGGTACATTGTTGTGAATGTAAATTAT +GCATTGGCGCCACAATATAAATATCCCACACCATTAATTCAAATGAATCAAGCAACTCAATTCATTAAAGAAAATAAAAT +GAATTTACCTATTGATTTTAATCAAGTAATTATTGGCGGTGATTCTGCAGGTGCTCAATTAGCTAGCCAATTTACGGCAA +TACAGACGAATGATCGCTTAAGAGAAGCCATGAAATTTGATCAGTCATTCAAACCATCGCAAATTAAAGGTGCTATACTA +TTTGGGGGTTTTTATAATATGCAAACAGTTAGAGAAACTGAGTTTCCAAGAATACAGTTATTTATGAAAAGTTATACTGG +CGAAGAAGATTGGGAAAAGAGTTTTAAAAACATTTCACAAATGTCGACAGTAAAACAATCGACAAAAAATTATCCACCAA +CATTTTTATCTGTTGGAGATAGCGATCCATTCGAAAGTCAAAATATAGAATTCAGTAAGAAATTACAAGAATTGAATGTA +CCAGTAGATACTTTGTTTTATGATGGTACGCATCATTTACATCATCAGTATCAATTTCACCTTAATAAACCTGAATCGAT +AGATAATATCAAAAAAGTGTTACTTTTCTTAAGTCGTAATACATCCTCTAGTGGTATTCAAACTGAAGAGAAACCACAAA +TAGAAAATCCGAGTAATGAATTACCGTTAAATCCTTTAAACTAATGATAAACAGTAGTAATTTATTACTTAAGCAACATT +TAAGATTTTCAAATTAAAAACGAAGAATTTAAAACATGTGGTGCTAATGTGTAAGAGTCTGAGATATAATAAATTGTTAG +CGGTTCTTTATCATTTCTATCTCACTCTATTATGATGTGACATTTATTTTACAAACTAATTTGTTTTGAACCTGAAAATA +ACTTTTTACAACTAAATTGTCGAAAAACAGTGTGTACATGATATAATAAATTTATAAATTGAAAAGAATTCAAAAGAAAA +ATTAAAATATAGATTGAGCACGAAGTGATTTGAAATAAGGTTGTGAAAGGGAATGACAAGGTCAGCATTAAAACCATTTA +AAAATAAACGCGTTATGGTTACTGGACGTATACAACGTGTTTTGTTTAAAAATTATTTAGATAGACATAGCACATTTAAG +CCGAATGTAAGGATATTATTAAAAGATGTATTTGTTTCAGGTGTATCAATAGATCATTTATGGTTATATGAGACAAATAA +ATACTATGCATTGGCAATGGAACTTATTCATCAACGAGTAAAATTTAGTGCGAATGTTGTACCGTATTACAAAATAAATA +GAAATAATAATTTATTCGTACAAGATTATGGAATTAAGCGTAAAGGTAGGTTAATTACTGAAGAAGCTTACAATCAAAAC +AATCAGTATCAGGATAAGATATATGAAAAATTACCGGATATAGATTTTAGACTCGAAGATTTTTATAGTAAGGAAAACTA +AACAACATATATAAAGCACCACTAATCGTAGTTGATAGTTAATCAATTACACATTAGTGGTGTTTTATTTTGAATTTAAA +TTTGATAAATGAATAAGAGTGTACGTATTTATTTGAGATTAGATAACATTGATAAGTATGAGTAATGTATTAGGTAACCA +TTAAATTCGTTGTTATACGATTCTGATACAAGATTATGATAAAATAGCTTTAATTAAGAATTTTAGCCATCATATAGTCA +TCATAATATTTACCATCGATAAATAACTTATCTTTTAAAACGCCTTCGATTTGAAAATCGGCACTTTTAAAAAGCTCGAG +GGCAGGTTGGTTATTGAGTGGTACATTTGCTTCAATTCGGTGTATTTGATTGTTTAAACACCAAGCCATAATGGCATCAA +GAAGTGCTTGGCCAATTCCACGATGTTGATATAATTTCTTTACACCTAAATCAATTTTAGCAACATGTTTAATGCGTTGA +AATGGTGTCGTATTAACAAAGGCAAAGCCAACGAGTTGTTCATCACTTTCAGCAACGAAGATGACTTTATGCGGAGAAGT +GATATATTCTTCTAATTGTTTACTAGCCGATGTGACGCTAGGATCATATTCTCCTGGTGTGTAGAACATATACGGAGATT +CGTCGTATATGTTCGCTAACATTGAAATGAAATTTTCTACATCTTTGATACTAACTCTACGTATAATATGGGCCATAAAA +AGCCTCCAGTATTTTGAAATTTTAAAACATTTGCTACTATTATAATATATATGTAACTAAAAGGTGGAGTAATATGTTTT +TTGGTTACAGTTTAAAGGAGTATTTTAGATGAAACCTAAAGTTTTATTAGCAGGTGGAACAGGATATATTGGTAAGTATT +TAAGTGAAGTGATTGAAAATGATGCTGAACTTTTTGCTATATCAAAATATCCAGACAATAAAAAAACAGATGATGTTGAA +ATGACTTGGATTCAGTGTGATATATTTCATTACGAACAGGTTGTTGCAGCAATGAATCAAATAGATATTGCTGTATTCTT +TATCGACCCAACAAAGAATTCTGCCAAAATAACACAATCATCAGCAAGAGATTTAACATTAATCGCAGCAGATAATTTTG +GTCGAGCAGCGGCTATCAATCAAGTAAAAAAAGTAATCTACATACCTGGGAGTCGTTATGATAATGAAACAATTGAACGC +CTAGGTGCATATGGTACACCTGTAGAAACAACAAATTTAGTTTTTAAACGTTCTTTAGTTAATGTAGAATTACAAGTTTC +AAAGTATGATGATGTTAGATCAACGATGAAGGTAGTTTTACCAAAGGGATGGACATTAAAGAACGTTGTAAACCATTTTA +TTGCATGGATGGGTTACACTAAAGGAACTTTTGTGAAAACAGAAAAATCACATGATCAATTTAAGATATATATTAAGAAT +AAGGTGCGACCGCTCGCAGTATTTAAAATAGAAGAAACAGCTGATGGAATAATAACTTTAATTTTATTGAGTGGAAGTTT +AGTGAAAAAATATACAGTTAATCAAGGGAAGTTAGAATTTAGATTAATCAAAGAGTCGGCAGTCGTTTATATACATCTAT +ACGATTATATCCCTCGATTATTTTGGCCGATTTATTACTTTATACAAGCACCAATGCAAAAAATGATGATTCATGGCTTT +GAAGTTGACTGCCGGATTAAAGATTTTCAAAGTCGATTAAAATCAGGAGAAAATATGAAATATACTAAATGATATTGGGT +GATATGGATGCAAATACTACTAGTAGAAGATGACAATACTTTGTTTCAAGAATTGAAAAAAGAATTAGAACAATGGGATT +TTAATGTTGCTGGTATTGAAGATTTCGGCAAAGTAATGGATACATTTGAAAGTTTTAATCCTGAAATTGTTATATTGGAT +GTTCAATTACCTAAATATGATGGGTTTTATTGGTGCAGAAAAATGAGAGAAGTTTCCAACGTACCAATATTATTTTTATC +ATCTCGTGATAATCCAATGGATCAAGTGATGAGTATGGAACTTGGCGCAGATGATTATATGCAAAAACCGTTCTATACCA +ATGTATTAATTGCTAAATTACAAGCGATTTATCGTCGTGTCTATGAGTTTACAGCTGAAGAAAAACGTACATTGACTTGG +CAAGATGCTGTCGTTGATCTATCAAAAGATAGTATACAAAAAGGTGATCAGACGATTTTCCTGTCCAAAACAGAAATGAT +TATATTAGAAATTCTTATTACCAAAAAAAATCAAATCGTTTCGAGAGATACAATTATCACTGCATTATGGGATGATGAAG +CATTTGTTAGTGATAATACGTTAACAGTAAATGTGAATCGTTTACGAAAAAAATTATCTGAAATTAGTATGGATAGTGCA +ATCGAAACAAAAGTAGGAAAAGGATATATGGCTCATGAATAATTTGAAATGGGTAGCTTATTTTTTGAAATCTCGCATGA +ACTGGATATTTTGGATATTGTTTTTAAACTTCCTTATGTTAGGCATTAGTCTAATCGATTATGATTTTCCAATAGACAGT +TTATTTTATATTGTTTCTTTGAATTTAAGTTTAACAATGATTTTTCTTTTATTGACATATTTTAAAGAAGTAAAATTATA +TAAGCATTTTGACAAAGATAAAGAAATAGAAGAAATTAAACATAAAGATTTAGCGGAAACGCCATTTCAACGTCATACAG +TTGATTATTTATATCGTCAAATCTCAGCGCACAAAGAAAAGGTTGTTGAGCAACAGTTGCAATTGAACATGCATGAACAA +ACCATTACAGAATTTGTGCACGACATAAAAACACCTGTGACAGCTATGAAATTATTAATTGATCAAGAAAAAAATCAAGA +AAGAAAACAAGCATTACTATATGAATGGTCTCGTATAAACTCGATGCTAGATACACAGCTGTATATTACTAGATTAGAAT +CTCAACGTAAAGATATGTATTTTGATTACGTGTCACTTAAACGCATGGTCATTGATGAAATACAATTAACAAGACATATT +AGTCAGGTTAAAGGTATTGGTTTTGATGTTGACTTTAAAGTGGATGATTATGTTTATACAGATATAAAATGGTGTCGTAT +GATTATTAGACAAATTTTGTCAAACGCATTGAAATATAGTGAGAATTTTAATATTGAAATTGGGACAGAATTAAATGATC +AACATGTTTCGTTATATATTAAAGACTATGGCAGAGGTATTAGTAAAAAAGATATGCCGCGAATATTTGAACGAGGATTT +ACGTCAACGGCTAACAGAAATGAAACGACGTCTTCAGGTATGGGTCTATATTTAGTAAATAGTGTAAAGGATCAATTAGG +TATTCACCTGCAAGTCACGTCGACTGTTGGTAAGGGGACAACTGTCAGATTGATTTTCCCATTACAAAATGAAATTGTTG +AACGCATGTCGGAAGTGACAAATTTGTCATTTTAAACATGCGTTTTGTTACTTAGAATTGATACATCAATGCAGCTTCAA +CGTTATAATAAGATAGATGTTAGTCATATGTTAAATTGAAGATACAAGTGCCAAAGCCTAAAGGAAATGAAGTTAAGATA +AATTATAGGAGTGTTAAAGTGGCAATTTTAGAAGTAAAACAATTAACAAAAATATATGGAACTAAAAAAATGGCACAAGA +AGTGTTGCGAGATATCAATATGTCTATTGAAGAAGGCGAGTTTATTGCTATTATGGGTCCCTCTGGATCTGGGAAAACGA +CATTATTAAATGTTTTAAGTTCAATTGATTATATTTCACAAGGTTCTATTACATTAAAAGGAAAAAAATTAGAAAAGCTT +TCAAACAAGGAATTATCTGATATACGCAAGCATGATATTGGTTTTATTTTTCAAGAGTATAATTTACTGCATACATTGAC +TGTTAAAGAAAACATAATGTTACCACTAACGGTACAGAAGTTAGATAAAGAACATATGTTAAATCGTTATGAAAAAGTAG +CAGAAGCATTAAATATATTGGATATTAGTGATAAATATCCCTCTGAATTGTCTGGTGGACAAAGGCAACGAACATCAGCT +GCCAGAGCATTTATAACATTGCCTTCTATTATATTTGCTGACGAACCAACAGGTGCACTGGATTCTAAAAGTACTCAAGA +TTTATTAAAACGATTAACAAGAATGAATGAAGCATTTAAGTCTACAATTATTATGGTAACGCATGATCCTGTTGCAGCAA +GCTATGCAAATCGAGTAGTGATGCTAAAAGATGGTCAAATTTTCACTGAATTATACCAAGGGGATGACGATAAACATACC +TTTTTCAAAGAAATAATACGTGTACAAAGTGTTTTAGGTGGCGTTAATTATGACCTTTAACGAGATAATATTTAAAAATT +TCCGTCAAAATTTATCACATTATGCCATCTATCTTTTTTCGTTAATTACGAGTGTAGTATTGTATTTTAGCTTTGTAGCA +TTAAAATACGCTCATAAACTAAACATGACAGAGTCATATCCAATTATAAAGGAAGGCTCACAAGTCGGAAGCTACTTTCT +ATTTTTCATCATAATTGCATTTTTGTTATATGCCAATGTGTTATTTATTAAACGACGAAGTTATGAGCTTGCATTATATC +AAACATTAGGTTTATCTAAATTCAACATTATTTATATACTAATGCTCGAACAATTACTAATATTTATAATTACGGCAATA +TTAGGTATTATTATTGGTATTTTTGGTTCGAAACTGTTATTAATGATTGTCTTTACATTATTAGGAATTAAAGAAAAGGT +TCCAATTATTTTTAGTTTGAGGGCGGTATTTGAAACATTAATGTTAATCGGTGTCGCTTATTTTTTAACATCTGCTCAAA +ATTTTATATTAGTGTTCAAACAATCTATTTCACAGATGTCAAAGAATAACCAGGTTAAAGAAACAAATCATAATAAAATT +ACATTTGAAGAGGTTGTTTTAGGCATCTTAGGTATAGTATTGATTACCACAGGATACTATCTATCTTTGAACATTGTTCA +ATATTATGATTCTATCGGTACACTTATGTTTATTTTATTGTCAACTGTGATTGGGGCATACTTATTTTTTAAAAGCTCTG +TTTCTCTAGTTTTTAAAATGGTGAAGAAGTTTAGAAAAGGTGTTATAAGTGTAAATGATGTCATGTTCTCATCATCTATT +ATGTATCGTATTAAGAAAAATGCTTTTTCACTTACGGTCATGGCAATCATTTCAGCGATTACTGTTTCAGTTCTTTGCTT +TGCTGCTATAAGTAGAGCGTCCTTATCAAGTGAAATAAAATATACTGCACCACACGACGTTACAATTAAAGACCAACAAA +AAGCTAATCAATTAGCAAGTGAATTAAACAATCAAAAAATTCCTCATTTTTATAATTATAAAGAAGTAATTCATACGAAA +TTGTATAAAGATAATTTATTTGATGTAAAAGCGAAAGAACCATACAATGTAACAATTACTAGTGATAAATACATCCCTAA +TACTGATTTGAAACGTGGGCAAGCTGATTTATTTGTAGCGGAAGGTTCTATCAAAGATTTAGTGAAACATAAGAAGCATG +GTAAGGCAATTATAGGAACGAAAAAACATCATGTTAATATTAAGTTACGTAAAGATATTAATAAAATCTATTTTATGACA +GATGTTGATTTAGGTGGACCAACGTTTGTCTTAAATGACAAAGACTATCAAGAAATAAGAAAGTATACAAAGGCAAAGCA +TATCGTCTCTCAATTTGGATTCGATTTGAAACATAAAAAAGATGCTTTAGCATTAGAAAAAGCGAAAAATAAAGTTGATA +AATCTATTGAAACAAGAAGTGAAGCGATAAGCTCAATATCAAGTTTAACCGGAATATTATTATTTGTAACATCATTTTTA +GGTATTACATTCTTGATTGCTGTATGTTGCATTATATACATAAAGCAAATAGATGAAACCGAAGATGAGTTAGAGAATTA +TAGTATTTTGAGAAAGCTTGGATTTACACAAAAAGATATGGCAAGGGGACTAAAGTTTAAAATTATGTTTAATTTTGGGT +TACCTTTAGTTATTGCACTATCACATGCATATTTTACATCATTAGCATATATGAAATTAATGGGTACAACGAATCAAATA +CCGGTTTTCATAGTAATGGGATTATACATTTGTATGTATGCTGTTTTTGCAGTGACGGCTTATAATCATTCCAAGCGAAC +AATTAGACATTCCATATAAAATATACAGATGGCTTTCAGTAGAGTAGTGGATTCGGATTCACGAACTATACTGGAAGCTT +TTTATTATAAATGAAGAGAAGTTATATTTTTAGCATGTATAGTTGAATACTGGGTTAAAATACCATATTAATAATGAAGT +AAAGGTATGAGTGATTATGAAAGTGTTTTGAATGAAATATATTTAATTGGTGATGCTTTTAATTGAAAAGATTAACAGGA +TTCAACTTTGTAAATTGTATTAAATGTGAGAAAATAAAAGTATATTCATTGAGAGATATATGAGTCAATGATCGTTTTAA +ACAAGATAAGTGTATTTTAATATGTAAAAGTTATGTAATAAATATTGTATCGTTGCAAATTTCCCAACTATATCCATTTA +CAATTTTTAGAGTTGTATAGATAAAAAATTGCCTATACAATAACTTTTTCAAAACAAATGCCTACTACTATTTAAATTTT +ATGTAAAAGTTAAGTTTCGGAATTGTTAAAAATTTGTAAATTGAATTGTGGGATTGTAAATTTTGTGCTATTTTCATATA +AGGGCAAATACAGTAAATTGTTTACTGTGAGGCATTTTTGAAATTAATATCAGTACACTAAAATTATACTGACGATTGAT +AATAACAAATTTGTATCATTAGTTTGTAAATTCACTTGTCTTAATTTGAAACAAATTATAACTGAATGTGATTGGTGACA +ATCGCTTAAATGGAGGATTTTAAATGTTTAGTAAGAAAAAAGATAAGTTTATGGTTCAATTAGAAGAGATGGTTTTCAAT +CTGGATCGTGCTGCTATTGAATTCGGTAAAATGGATTTCAATACACATTTAGATTTAAAAGCATACTCAGACAACATTAA +AACTTATGAGTCACATGGTGACGAATTAGTACATCAAGTAATTACTGATTTAAATCAAACATTTATCACACCAATTGAAC +GTGAAGATATTTTATCATTATGTGATGCAATTGATGATGTTTTAGATGCAATTGAAGAAACGGCAGCTATGTTTGAAATG +TATTCAATCGAATACACAGATGAATATATGGCTGAGTTTGTTGATAACATTCAAAAAGCAGTTGCAGAAATGAAACTTGC +TGTCGGCTTATTAGTCGATAAAAAATTATCACATATGCGTATTCATTCAATTAATATTAAAGAATTTGAAACAAACTGTG +ATGGTATTTTAAGACAGTCAATTAAACATATTTTCAATAGCGAAACAGATCCAATCACTTTAATTAAAATAAAAGATATT +TATGAAAGCATGGAAGAAATCGCTGATAAATGTCAAATCGTAGCAAATAATTTTGAAACTATTATTATGAAAAATAGCTA +AGGGGAGTATATATTTATGTCATATATAATCATCGTCACTATAGCTGTAGTTATTTTCTCGCTGATATTTGACTTTATCA +ATGGATTCCATGATACAGCCAATGCAGTAGCTACTGCTGTATCTACTAGAGCGTTAACGCCTAAAACGGCAATTTTAATG +GCAGCAGTGATGAACTTTATAGGTGCTTTAACATTTACGGGCGTTGCAGGCACCATTACTAAAGACATTGTCGATCCATT +TAAATTGGAAAATGGATTAGTTGTTGTGTTAGCTGCAATACTTGCGGCTATTATTTGGAATTTAGCTACTTGGTTTTACG +GAATTCCAAGTTCGTCTTCACATGCACTTATAGGTTCAATTGCGGGTGCAGCAATCGCATCTGAAGGCTCATTTGGAGTG +TTACATTACCAAGGTTTCACAAAAATTATTATTGTATTAATCGTTTCACCGATTATCGCATTTTGTGTTGGTTTCTTGAT +GTATTCAATTTTTAAAGTTATCTTTAAAAATGCAAATTTAACAAGAGCGAATCGTAACTTTAGATTTTTCCAAATTTTCA +CAGCAGCGTTACAATCATTCTCTCACGGTACGAATGATGCGCAAAAATCAATGGGTATTATTACGTTGGCATTGATTGTC +GCTAATGTACAGAATGATGGCAGTGTTGAACCACAGTTATGGGTAAAATTTGCCTGTGCGACAGCAATGGGGCTTGGTAC +TGCAATTGGTGGCTGGAAAATTATCAAAACTGTAGGTGGTAATATTATGAAAATACGTCCAGCAAATGGTGCTGCGGCCG +ATTTATCATCTGCATTAACAATTTTTGTTGCATCATCGCTACATTTCCCATTATCAACAACTCACGTTGTGTCATCATCA +ATCTTAGGTGTTGGTGCTTCTAACCGAGCTAAAGGTGTAAAATGGAGCACTGCGCAACGAATGATCATTACATGGGTGAT +TACATTACCTATTTCAGCATTGTTAGCAGGTTTACTATTCTATATACTTAACTTATTTTTCTAATTGAAAATAAAACTAA +ACTGAACTTCAGTATCACAAACATATGGTGGTATTGAAGTTCAGTTTTTTATGTAAGTAGATAACATATTTTATAGAATT +TACGTAGAATGACCAAGACAAAGCATTTTAATTAGGAGGCTATATTATAAATGTATTCCATGGAACATAATCAGTTAGGT +CATTAAAATATATGTAAGAGCAAAATAATGATTTAATAGTTTATATTCTTCAATGAAGGAATAATTAAATAGTTATCAAC +TTGTTTAATTAACCTAAATTGTATTGGGCTTTATTTAATATAAATTTGTATATGTAGACGAAAAAGCGTAAAATACTGGT +ATAACAAATTAAATCATTATATAGTCAACTTAGTTGCATTATTTTGATTTTTAATATATTATGCATTTAATTTAATCAAA +TAGGTAGGTTGTTAATTTTTACCTAACTACCATTCATACAAAATATAAATATGTATGATAGGGAAGGGTGAGATAATTTT +TTGTACCAAAATGAAAATTTTTTTCAAAAATAAAAAACTAGCAATCTCACATGATGTGAATTGCTAGTATATATCAGTAC +AATTTATTTAATTAATGGATGAATGCATAGCTAGAAACTTCTGAAGCTGGAATTGTACGGTAGTTCATATTGTATGGACC +ATATGTGTAATTCATTTCAGAAATCAAGATACTACCATCACCATTGACACGTTCAACATAAGCAACATGACCATATGGAC +CAGGTGTGCTTTGCATAATTGAACCAACTGATGGTGTGTTGTTTACTTGGTAACCATCATTAGCTGCGTTACCAGCCCAA +TACTTAGCGTCTGACCAATATGTGCTAATTGGACTACCAGCTTGAGCACGACGGTCAAATACGTACCATGTACATTGACC +AGCAGTGTATAAATTTTGGTGATTAAAAGATGATGCATTGCCATTGCTACCTGTTGTAGCTGTTGGTGTTGTACCACCTG +ATCCACCATTAGGAATTTGTAATGTTTGGTTAGGCATAATTAAATAACCACGTAAGTTATTGGCTGCCATTAATTGATCA +ACTGAAACACCATATCTGCTAGCAATGATATTTAATGATTCACCAGCTTGTACAGTATGAGATGATGCTGAACCAGCTTG +TGGAGAAGTGTTTGACGTATTTTGTGCATCACTTCCACCTACTGAGATAACTTGACCAGGGAATACCAAGTTGTTATCTA +ATTGGTTATTTTGTTTAATACTCTCTACTGAAGTGTTGTATTTTTGAGCAATACTCCATAATGATTCACCAGATTGTACT +GTATGTTGTGTAGAAGCTTGTGCATCATGATGCGTTAAAAATGCAGCTGCACCAGATGTTGCTGTTATTGCAAATGCTAA +TTTTTTCAAAGGGACTCCTCCTTAAAATTATGTTTTACTACTTTTAAAAATTATTAAGACAATTTATATAGTAAATTAAG +TTTTCGTTTTATATTACGATTTCCATATTATCAAAAATAAGTAGCTTTGTGTGGTTTGTTATTATCTTGTAATATTATTA +ACATTTTAGGATATCGGTTCATATTTTTATAAATGCTGATTTGATAGTGTATAAAAGCGTTTTAGTGATTTTAACTAATT +ATAAAAAATTATATTTTGTAACATTGTAAAATGTAAAATTACTATACTTGTTAAAGGTTATTTCTTTTGATGAAGCAAAA +TTTACTGTAAAATTTGACGCATAAAACGACGGTAGAAGGAGGAATTATTTTGTCGCAAAATACAAATCATTCATATTATC +ATCAAAACCAGCATGCTCAATCAATAAGTAAAGTGTGGCTTTATTTTATGTATTATTGGATTATATTTGGCATAGGATGC +TATCTAGGTCAGTTTTTACCATTAAGTTGGCGACAACCCTTGTCATTTGGATTACTGATTATTATTTTAGCAACACTTGT +TTTTGAAAGAGCGAGACGGTTCGGTTTAATTATTTCACATATTTACGCTGTAGTGATTGGCTTATTGTCATACGCAACGT +TTACCACGTATTTACAAAATTTAGGACCAGATATTTTCTATAAAAATATCGCATTAGCAATTTTTGCATTTATAGCATTT +GGTATTATTGGTTATTTCTTCGTTGGAGATGCATCGAGTATAGGCAAATATTTATTCGTTACATTAATAACATTAATTAT +TGCGAGTCTAATTGGTATTTTTCTTCAAAATCCTATTTTTTACACTATTATTACCGTCGTTAGTCTGTTGTTATTTCTAC +TTTATACTTTGTATGATTTTAATCGTTTAAAAAGAGGTGACTATTCACCAAGAGAAATGGGATTTAATCTATTTATTAAT +TTGTTGAATATTATTAAGGATATACTTTACCTTGCTAATATGTTCAGAAGATAAACAGTTAAATTAATAAAGAAATTTGT +ATAAAGTTTTGGAATAATCAATAATGAATATCAACCTTAAACTGGAGCGATTGTCATAAGTTAGAAACTTAGACTGAGAT +TTTACTGATAAAGCAATAAAAATATATTACCTTAATTGTAAAGATATTATTAATCATTGAAATGATTAACTGCTATAGTT +CTAAAGTAAACAAATCTTAGAAGATAAACTTTAAACAGGTGTACTTGCCTTTCTAATTACACTTTTATTAGAAAAGTAAA +GTATAGCTACGTATTAATTTTAAAATCGAAACATTAATAGGAAGAAGTATAGATAATATAAACAATTTAATGTTAGTTTG +AGCTCATCTCAAAAGTAGAATGTGATTTAAATAAGTGTTTATTTTTAAATTGAAAATGAATATATAACTAACAAAAGATA +ATAGTAATACGTTGTAGATGTGGTCATTTAAAATTATTTTGAAAATACTAAAAAGTAGTGTGTTTTCAATTGGATAGTTT +AATTAAACTGACTACATCTATTTTTGTAATATTTAAATGATTGATATATAAGTTTAAAAATTAACCATTATATAGTTTTA +TTTATTTTTGATTTGGTATTCGTATTGTGTTAAATTTACGTTTAATTAAATAAGTTAAATATTATAGGGAATGATAAAAG +TGGTACTTAAATATAGAAAGAGGTAAAGTTATGGCAAAATCATGCTTGCATATACTTACTAATAATGAATATGCGACAAC +GCGTTGCCAAGATGGCATAGTCTTATTTTGGCCAATTGACGGGGAAATCGAACTACAAAAATTTCGTAAAAGTAAAATAA +TTGAAGATGATATATATATTATTAATCATCTGGATGTATTTAGTATTAAGAATAATAAAAAAACGATCATGTTGTATTTG +AGTAGCGATTGGTTTGCGGAATTAGGCTTTACTTTCTTTAATTACCACTATACAGCAAAGTTGATTAAATCATCCTATAA +TTTGAAATGTCTACTATTAAAATTGACATATCGATACCTTGATAATCAGCCTCTTAATGACGCTGATATTAGAAAATTAC +AGGATATTATTAAAATCATTGCAAAAGAAGCAAGTATGGATAAAAAGATTGCACAAAATCAATATCGATATGCGTATTAT +GGTGATTTGCGTGATGAGCTCGAATATATTTATCAAAATGTAAATCAACGATTGACATTAAAAAGTGTCGCTGATAAATT +ATTTGTCTCAAAGTCAAATTTGTCATCACAATTCCACTTACTTATGGGCATGGGTTTTAAAAAATATATTGATACTTTGA +AAATTGGTAAATCGATTGAAATTCTACTTACTACTGATAGTACTATTAGCAACATAAGTGAACATTTAGGTTTTAGTAGT +AGCTCCACTTACTCTAAAATGTTTAAAAGTTATATGGATATAACACCGAATGAATATCGTAATTTATCAAAATATAATAA +ATGTTTAATGCTAAAGCCAGAACCACTAGTAGGCAAAATGGTGCAAGAAGTAAAAGAAATCATATTGAATTATATTGAAC +ATTATAAAAACCACCTAACTGATGTTATACATATTGATGAAGACAAATTTGAAACACCTAAATTGTTTCAAACGGTTATT +CAAATAAATACTTATACAGAAATGAAATTAGTTTTCTTAGAAGGAATCTTTAAAACCTTATTGAATAAGAACAGTCAAGT +TGTCTTTTTCATCATGCCATCGATTCTAAAAAGTAAAAATACCATGTCCGAAGAAGAAAAATTCACAATCATTAAAACAA +TAATTGAAAGTGATCTAAAGATAGCATTTAATATAAATAATATTGAAACAACTTATTTTGTTGAAGAAGCTTTTATGAGT +GTTTTCAGACAAATATCTCCAAACGAATTAAGTAATCATAATAATTACGAAGTGCATTTTGTTTTTGATTTATCATTGAT +GGAAATTAGAACAATTTATCGAATGATATTAAAATTACATAACATCATGTTGAATGTGAAATTAGGATTGAACATTACCT +GTTTATTTGAAAAACCTTCAGTTTTTAAATCACTAGTATCACAAATAAAGCGACTTAAATTCGATTCGTTAATAATAGAT +AATGCAAATTTAAGTAGCCCTTATTTGATGGGGGAAAGTGATGAGTTACTATTGAAAAATATTTTGCATTTTAAAAATTT +AAAACAAGTAATTAATGAATTGGATATTGAACAAGAAAAGCTGATTTTTCTAAATGTTGAAAATCATAAACTGCTTAATA +ATAAAGAACGAGATCTAAGTAATAGTGCTCCATTAATTTATAAGACATTAAGTGCGCTGTATCACAACTTTGATGGCTTT +GGATTAAACATTTTTGACAATCATCAAGCATTTAATGCGATGCATCTATATGACAAAAATGGATTTAAAACAACACTAGG +TCTTATATTAGAAAAGTTTATCGAATATGTCTCGAAACCAAAATACGAAAACAGTTATTATTCTATTTTTGATATAGAGA +ATTATTATTGTCTTGTTATTTATGATTGGCGAGTGATAGAGAGCGAGACAATTATGAGTAATTTTGAGGATAGTCAAGTT +TATATAAATTTTAAAAACAATGTTTTAAACGATAAATATCTAATTGTAATAGAAACATTGGACGAAAATAGTGGCAACAT +TAATCATTTGATTTCTAAAGAATTAAGAGATAAATATGAATGGAACCCTAGTTTACTATCTAAAATTGACAACTACCTTA +AACCAGCAATAGAGATTAAAGAGCATAATTTTAGTGATAATTCCTTGAATATTAACGTTACTTTTAATGCGTTATACATA +ATTAAAATAGGAAAAAAATAACACCTTGATATGTATTGCAAAATTTAATTTGCATTTGTTGGTAATTTTGACAACGTATT +AAAAACAAATGAGAAAGACAATGGCTGAAAAATTTAAGATAATAATGACAGAAGCATTGTCTTTATATATTTGGGGGTGC +AACATTTTGAATACTGAGAAATTAGAAACATTGCTTGGCTTCTATAAACAATATAAAGCATTATCTGAATATATTGATAA +AAAATATAAGTTGTCGCTAAATGATTTAGCAGTCTTAGATTTAACGATGAAGCATTGCAAAGATGAAAAAGTACTTATGC +AATCATTTTTAAAAACTGCAATGGATGAGCTAGATTTAAGTAGGACAAAATTATTAGTTTCTATAAGAAGACTAATTGAA +AAAGAAAGACTTAGTAAAGTTAGATCATCTAAAGATGAGCGTAAAATTTATATTTATTTAAATAATGATGATATATCTAA +ATTTAATGCTTTATTTGAAGATGTAGAACAATTTTTAAATATTTAATTGAAATTGAGTGTCGAAAGCATAGAATTTGCTT +ATCGGCACATTTTTAATTTATACATATTTTAAAACTAAGTAACAGTTTGAAGAAATCGTAGTTCAATAATGTTAATTGTG +AAAATGTATATAAACATAAAAAAATCATGTATAATATATGTTGTTAATTAAACAGTTCGAAAGCGAGATGACATTATGGG +ACGTAAATGGAATAACATTAAAGAAAAAAAGGCCCAAAAAGATAAAAACACAAGTAGAATATATGCGAAATTTGGTAAGG +AGATTTATGTTGCAGCAAAATCTGGTGAACCCAATCCAGAATCTAACCAAGCTTTAAGGTTGGTGCTTGAACGCGCTAAG +ACATATTCAGTGCCGAATCATATTATTGAAAAAGCAATAGATAAAGCTAAGGGTGCTGGAGACGAAAACTTTGATCACCT +AAGATATGAAGGATTTGGCCCAAGCGGATCAATGCTAATTGTTGATGCGTTAACAAATAATGTAAATCGTACTGCCTCTG +ATGTGCGAGCTGCTTTTGGTAAAAACGGCGGTAATATGGGTGTATCTGGATCAGTTGCTTATATGTTTGATCATGTGGCA +ACATTTGGTATTGAAGGAAAGTCTGTTGACGAAATACTTGAAACATTAATGGAACAAGATGTAGATGTAAATGATGTGAT +TGACGATAATGGATTGACAATAGTCTATGCTGAACCAGATCAATTTGCAGTCGTTCAAGATGCGCTTCGTGCAGCAGGTG +TTGAAGAATTTAAAGTTGCTGAATTTGAAATGTTACCTCAAACAGATATTGAACTTTCTGAAGCGGACCAAGTAACATTT +GAAAAATTAATCGATGCATTAGAAGATTTAGAAGATGTACAAAACGTATTCCATAATGTGGATTTGAAATAATGAAATCA +GCAGAACAATGGATTGATGAATTGCAACTTGAATCACATCCTGAAGGTGGTTTCTATAGAGAGACAATTCGAGAAGTATT +GAAAGATGGACGCAGAGCGCCGTTTAGTAGTATTTATTTTTTACTTACAGATGACAATATTTCGCATTTTCATCGAATTG +ATGCTGATGAAGTATGGTACTATCATGCTGGCGATTCTCTAACAATTCATATGATAAATCCGGATGGGGAATATACGACT +GCAACATTGGGTACTGATATCCAAAATGGAGATGTATTGCAATATGTAGTGCCTAAAGGAACAATTTTTGCTTCTTCAAT +CGAAATATCAAATACTTTTAGTTTAGTAGGTTGTATGTGTCAACCGGCATTTGAGTTTAAGCAGTTTGAATTGTTTAAGC +AATCTGAATTAATTACGCAATATCCGCATCTTAAATCAGTAATCGAAAAATATGCTTTAAAATAAAAGTGATCAATGAAG +TGGTTTGAAGGTTGTTAATAAACCTTTGAGTCACTTCATTTTTATATGTATTCTTGATTGAATCAGAATAGATTTGATGC +TTCAGCTGTTTTTAATGAAAATAGCATTAAATGATTTTGAAAACGATAAGAGTGTGTTATTTATATTTTTGAAAAATCAC +TTTTATGAAGAAATGTGTGTACTATCTAATTAATATTGTCTATTTTAAGTAAATTGAAGCGAGTTGTAGTAAAATAATAA +TAAATATTTTCATATGATTAACAAAAACTATAAAACTGTATCATGACATGACATCATTTATGTAGTTTTATATAATAATG +ACAAGGAGTTGGTGACTGTGAAAGTAAAGTATATAGATAAACGTCACTGGCGTCGCCTAATTGATAGGGAATACACAGAG +GTAAAAGTTAATAATAATAGGTTTAAGGGTATTATAGGCTTAGTCACGATGAAAAAGGTTCGTGATCCTTTAGAGGTGAC +GGTAGTTGGACAAAATATCATTGTCGCAGATGACAATTATAAATGGTTGCAAATACTACCTGAAAAGAAACGTTATAGTA +TAACTGTAATGTTTGATAATAAAGGCAATCCATTAGAATATTATTTTGATATAAATATCAAAAATATAACGCAAAAAGGT +AATGCGCGTACAGTAGATTTATGTTTAGATGTTTTAGCATTACCAAGTGGTGAATATGAGTTGGTAGATGAAGATGATTT +AATGTTTGCATTAGAAAGTGAGCAAATTACAAAAAAGCAATTTCATGAAGCATATATGATTGCACATCAAATTATGGCAG +AGTTAGAAAATGATTTTAAAGGATTCCAAAAGAAAATCATGTACTGCTTTAATAAAATTAATGCAAAGGCTCAAAAAAAT +CATCAAAAGCCACAAAATAAAACTAATATTGAAAAAAGCAAACAAATAAAGCCTAAGCAATATAATCAAACTAAAAATCA +CCAACAACAAAAGAAAAACTAAGTAATTCAAGCTGCAGCCATACCAATAAAATTGGTGAAATTATTCGTTAGTAATGAAC +TCATGAATGCTTGTAGCCATGAAAGTTCAATAATTGAATAATTTATGGGGGGAAATTATGAAGATTGAAGACTATCGTTT +ACTAATAACATTAGACGAAACGAAAACGTTACGTAAAGCGGCTGAAATTTTATATATATCTCAACCTGCTGTTACACAAA +GACTAAAAGCTATTGAAAATGCTTTTGGAGTAGATATTTTTATCAGAACAAAAAAACAATTGATTACAACAACTGAAGGA +ACAATGATTATTGAGCATGCTCGTGACATGTTGAAAAGAGAGCGATTATTTTTTGACAAAATGCAGGCACATATTGGTGA +AGTGAATGGAACAATATCAATCGGGTGTTCTTCTTTGATTGGACAAACCTTACTTCCTGAAGTTTTGAGCCTATATAATG +CCCAATTTCCTAATGTTGAAATACAAGTGCAAGTTGGTTCAACTGAACAAATTAAAGCAAATCATAGAGATTATCATGTT +ATGATAACTCGTGGAAATAAGGTAATGAATTTAGCTAACACACATTTATTTAATGATGATCATTATTTTATTTTTCCAAA +AAATAGACGAGATGATGTTACAAAGTTACCATTTATAGAGTTTCAAGCTGATCCGATTTATATAAATCAAATAAAAGAAT +GGTATAACGATAATTTAGAACAAGATTACCATGCAACTATTACAGTGGATCAAGTAGCAACTTGCAAAGAAATGTTGATT +AGTGGTGTAGGTGTTACAATCTTGCCAGAAATTATGATGAAAAATATCAGCAAAGAACAATTTGAGTTTGAAAAAGTAGA +AATTGATAATGAACCGCTGATTCGTTCGACATTTATGAGTTATGATCCGAGCATGTTGCAATTGCCACAAGTTGATTCTT +TTGTAAATCTCATGGCGAGCTTTGTTGAACAACCAAAGGCGTAGTTTTAGACTAATTTAAGGTTTGTATTTAATTTTAAA +CTATTCGGTTAAATTGAACGTAGTTGGTTGCTAATGCACCAACAGCAAAAGAGCCCCTAATTAATAAATTAAAAGGGGAC +AAAGGAATACAGTTGGTTGCTAATGCACCAACTGCATAAGAGCCCCTAATTAATAAATTAAAAGGGGCTCTAAGAAGCGG +GGTTATGGATATGTTTGCAGCGTTATTACAAATAAAGAATTATAAACTCTTTGTTGCTAATATGTTTCTACTAGGTATGG +GTATTGCGGTTACGGTCCCATATCTTGTTCTTTTTGCAACTAAAGATTTAGGTATGACAACAAATCAGTATGGATTACTT +CTAGCATCTGCAGCGATTAGCCAGTTTACAGTAAATTCAATTATTGCTAGATTTTCGGATACGCATCACTTTAATAGAAA +AATTATTATTATTCTCGCATTATTAATGGGTGCGCTTGGTTTTTCAATATACTTTTTTGTAGATACAATCTGGTTATTCA +TATTACTATATGCGATTTTCCAAGGATTATTTGCACCAGCAATGCCCCAACTTTACGCATCTGCTAGAGAATCTATCAAT +GTTTCAAGCTCTAAAGATAGAGCTCAATTTGCCAACACAGTATTACGTTCAATGTTCTCATTGGGCTTTTTATTTGGTCC +ATTTATTGGTGCCCAATTAATCGGATTAAAAGGCTATGCTGGATTGTTTGGTGGAACAATAAGTATCATTTTATTTACTT +TAGTACTTCAAGTGTTTTTCTATAAGGATTTAAACATTAAACACCCTATTAGTACGCAACAACATGTTGAAAAAATTGCT +CCTAATATGTTTAAAGACAAAACGCTTTTATTACCATTTATTGCATTTATTTTATTACACATTGGACAATGGATGTATAC +GATGAATATGCCTTTATTTGTTACTGATTATTTAAAAGAAAATGAACAACATGTCGGTTATTTAGCTAGTTTATGTGCTG +GTTTAGAAGTGCCATTTATGATCATTCTTGGCGTTTTATCATCTAGATTACAGACTCGAACATTGTTGATTTATGGAGCG +ATTTTTGGTGGTTTATTCTACTTCAGCATTGGGGTATTTAAAAACTTCTATATGATGTTAGCAGGACAGGTGTTTTTAGC +TATTTTCTTAGCGGTTCTTTTAGGAATTGGTATTAGTTATTTCCAAGATATCTTACCAGATTTTCCAGGATACGCTTCAA +CACTATTTTCTAATGCAATGGTTATTGGACAGTTAGGCGGTAACCTATTAGGTGGTGCTATGAGTCACTGGGTAGGTTTG +GAAAATGTATTTTTTGTATCAGCAGCATCAATCATGTTAGGTATGATACTTATATTCTTTACTAAAAATCAAAAAATTAC +AAAAGAGGATGTGATATCAACATGACAATTATTTTATGGCTACTTATCATCGCTGCCTTCATGTTAGCATTTGTTGGGTT +GATTAAGCCGATTATTCCTTCTGTTTTAGTATTATGGGTTGGCTTTTTAATCTATCAATTTGGCTTTCATAATCAGCATT +TATCATGGGTGTTTTATGTATCTATGGCATTGCTAACAATATTAATTTTATGTGCCGACTTTTTAGCTAATAAATATTTT +GTGAATCGCTTCGGTGGTTCTAAGTTTGGAGAGTATGCAGCTTTAATTGGTGTGGTTATTGGATGTTTTGTTTTACCGCC +ATTTGGAATTATTATTATACCTTTTATTTTGGTATTCATAGTTGAATTAATACAAGGCTATTCATTTGAAAGAGCAGTTA +AAGTAAGTATAGGTTCAATCGTAGCATTTTTAACAAGTAGTATAGCTCAAGCAATCATTATGTTTATAATGATTGTATGG +TTCTTTATAGATGCTTTATTGATTAATTAATAAAAAGCTTATTGCAAAATATGTTTTTCGGTAACTGTAATTTAGTGATT +TTATCATTAACAGTACCAAATTCGCATATGATGTAATAAGCTTTTTGTTTATAAAAATGTATGAGAATGTTTTTTCGAAA +TATTTCTTTCAATGCGTAATCCAATAGGCATAACTATAAATGAAAATATAATAAATACCCAATTGCTTACCTGAATATAT +GGTCCAAAGGCAAGTAATGATTGTGGAATAAAGAATACATAGAAAAATCCTACAATTAGTAGAATGATTGCTAAATAGGT +GAATATCCATATCCAATTTGAATTGTTAAAACACTGCAGTTGTATTGTTTTAAAACTAAACCATATAAATAAAAAGACAA +TAAAGAATCCAACGACTACTGAAAACGGGAATGAAACAAAATATAAATTACTTCCATTTTTTTCCATGAAAAATCCTAAA +AATCCTTTGAGAAAACTAACAATCCCAATTAATAGAACGATGTGTTGATAGATATATTTAAAAATATTTTTAAATGTTTC +ATTAGGCATCGCTTTTAGTTCTTTTATTGCATGTGCTTTTGGGTCGTGATTGAAAAAATCTAAGGCTAATAAACCATGTT +GTTCTGCGCTTAATAATTGTTTGAGTATACGGTTAATAATTAACTCTGTATCATGAGGGTTGACGCGAAAGTCAGAGCGC +ATATAAGTCATATAATTCTCGAAGATTTCTCTATCAGTATTGCTTAATCTTAATGATTTAACATTATTTTCTTTTGTTAA +TTGCGCAGTACTTTTCATTGTTACTTAAGCGCTCCTTTAAAAATGTTTAATTCCAAATTAAAATGGAAATGATTTTATAG +TATTAATAAGGTCAATCATATCATATTAAACGCATAAATATAACGATTAATATTGGAGAGGAAAATGAGGACACTTAATA +AAGATGAACATAATTATATCAAGCAAATAGCTAATATACATGAGACATTATTGTCGCAAGTAGAATCCAACTATAAATGT +ACTAAACTGAGTATTGCTCTTAGGTACGAGATGATATGTTCAAGATTAGAACATACAAATGATAAAATTTATATATATGA +AAATGAAGGTCAATTAATAGCGTTTATTTGGGGACATTTTAGTAATGAAAAAAGTATGGTTAACATTGAACTGCTATATG +TTGAACCACAATTTCGCAAACTGGGAATAGCTACGCAACTGAAGATTGCGCTTGAAAAATGGGCAAAAACTATGAATGCA +AAGCGAATAAGCAATACAATTCATAAAAATAATTTGCCAATGATATCTTTGAATAAAGATTTAGGTTATCAAGTGAGTCA +TGTGAAAATGTATAAAGATATTGATTAGAATTAGGATTATGTTGCTAATTCATGTTAAAATTAAAAAAGATTTAATGACG +TTAAGGAGTTTTATATGAAGAAATTAATCATCAGTATTATGGCGGTCATGCTATTTTTAACAGGTTGTGGTAAAAGTCAA +GAGAAAGCCACTCTGGAAAAGGATATCGATAATTTACAAAAAGAAAATAAAGAATTAAAAGACAAAAAAGAAAAGCTTCA +ACAAGAAAAAGAAAAATTAGCAGATAAGCAAAAAGACCTTGAAAAAGAAGTGAAAGATTTAAAACCTTCAAAAGAAGATA +ACAAGGATGATAAAAAAGACGAAGACAAAAATAAAGACAAAGATAAAGATAAAGAGGCATCACAAGATAAGCAATCAAAA +GATCAAACTAAGTCATCGGATAAAGATAATCACAAAAAGCCTACATCAGCAGATAAAGATCAAAAAGCTAATGACAAACA +CCAATCATAATCGAATTGCTTACTTGTTATAGATGAAAGGTACAGCGTTTTAAACCTTATTTTAAGGGTATGTATTAATT +AAAATGTGGTCATGATTGAAACAGAATGTAAAAATAGACAACATAATTAATAAAGGAGAGAAACGGCATGCATGAACAAG +ATTTTAGAATTTTAGAGGGTCAAGATATTACTTTGCCAGAATTAGGTAGAGAATTAGAAAATATTACAGGACATACGATT +GCTGATTCTACTGGCGAAATTAAGCGTGTAATTGCACATTTACCAAACTTTGAGTCCGATACAGATACTTTTGTTGCTAC +ATATCGTTTAAACCATCAACAAGATTTTATAGATGCAACTTTTACTGCGCTGAAATCAGATAGAGCACGTTTAAAAGAAG +TGCCAGTTCATGTTGAACTTATAAGTTATATTTCTAAATCAAAATAAACTGCTATCTAAAACGCAAAGTTGATCAAAATA +TCGATTTTGTGTTTTTTATTGAGAAATTATATAGGAGTGTCAATCGATGATTTATTGTGAAACAGAGCGTTTAATATTAA +GAGACTGGCATGAAGATGATCTGTTACCTTTTCAAAAAATGAATGCGAATTATGACGTACGTAAATATTTTCCAAGTTTA +TTGAGTTATCGTCGTTCAGAATTAGATATGAGAACTATGGATGCGGTTATTAAAGATTATGGCATTGGATTATTTGCTGT +AGAAGATAAAGAGTCCCATCAATGGATAGGCTTTATAGGTTTGAATTATATTCCAGAAACAAGCGATTATCCATTTAAAG +AATTACCGCTTTATGAAATAGGTTGGCGCTTGTTGCCAGAATTTTGGGGAAAAGGATTAGCAACTGAAGGCGCAAAGGCA +ACATTGAAGTTAGCAGAAGAACATCAAATATACGATGTCTATAGTTTTACAGCAGAAGCAAATAAAGCTTCACAACGTGT +AATGGAAAAAATTGGCATGACAGTGTATGATCATTTCGAATTACCCAATCTAAGTAAGTATCATTTATTAAAAAGGCAAG +TGCGCTATTACATTAATCTTCGAAAGTGAAAAATTTATACATAAGCGTAACAAACACCCCTAACATTGTTTAGCTGATGA +TAACAATATTAGGGGTGTTTGTTTATTTTTTAACCTTAGAATGATTAATCGTATGAACGAGTACCCAGAGGTTTGAAATT +TAATATTGATTCAATTAATGATTCCTTAGTGTCGCATAACGGTGCAAGAGCACGATACTTAGGATCAATAAAACCTTCCT +CAATCATATGGTCAATCATTGTTTGTAGTGGATTGAAAAAGCCATTAATATTATAAATGGCAATAGGCTTTTCATGGATA +CCTATTTGAGCCCAACTATACATTTCGAAAAATTCTTCTAGTGAACCTGCGCCACCAGGAGCCATGACAAATGCATCTGC +AAGTTCTGCCATTTTATTTTTACGTTCATGCATAGAATCAACTAAAATTAATTCAGTTAAACGTTGGCTTGTGATTTCAT +GTTCATCTAACATTTTAGGCATGACGCCAATAGCTTTGCCGCCATGATCTAATACACCATCTTGAATGGCACCCATAATG +CCAATTGACCCTGCACCAAATACTAATTCATAACCTTGTTCAGCAAAATATTTACCTAAATCGTATGCTTTTTGTACATA +TGAAGGGTCATGACCTTTGCTTGCACCACAATAAACTGCGATTCGTTTCATGTTAATCCAGCTCCTTAATTCGATGAATG +ACTTTTAATAGTGATTGTTCAAACACTTTTTGATCTTGCTTTGTAAAAGGTGGGGGACCTTTGTGGCGACCACCTTGTTT +TCTAATTTGTGCATTCATATATCGTTTATCTAATAGTTGTTGAATATTTTTGGAATTGTATATCTTCCCATTATGATGCA +TGACAATTAAGACTTTGTCGACTAATAAACTTGCGAGTCCATAATCTTGAGTGACTACGATATCATCCTTCGTTGATAAT +TGAACAATTTTGTAATCAACTGCATCTGGTCCATCATCAACATATAATGTTGATACATGTGGAGGATATAATTGGTTCGA +AAAATGGCTGAAGCTCCGAATAATTGTCACAAAAATGCCTGTCTCAGTTGTTAAATCTATAATAGAATCAACAACAGGAC +AAGCATCTCCATCAATAATAATATGTGTCACAATTATGCCTCTGTATTGTTTTCTTTATTTTGTTGAGAGGCGCTTTTGG +CAACATAATCTTTATATTTTTTAAATGACTTGATGCGTGCTTTATCAGCTTCTTGTTGGCGTTTTTGTTCTTCTTTGTGT +CGTTTTTCAATATTTTTTTGTAACTTTTTATTCATTTTAGCGATTTCTTTGCGATTTTTTTCAGCTAGTTTATCGCCTTT +TTTCTCAGTTTTCTCATCTAATTTATTAGGTGTTAAGCCTGCTTTTTCTTCGTATTTTTGTGATTTTTTCATATCTTTAA +TACGTTGTATTTCATTCTTTTCGCGGGCTTTTTGCTCTTCTTTATGACGCTTTTCGATATTTTTTTGAAGTATTTTATTC +ATTTTATCAGCGTCTTTACGATTTTGTTTAGCTAATTTTTCGCCTTTTTTCTCAATATAGGCAGGATCATGTTCTCTAGC +AAACTTTTTAAGTTCACGTTTATTTTCAAAATCTTGTTTTTTATCGCCGACATATTCTTTAACATCACTCGCTGTGTTAC +TGATTGCTGCAGATGTTTTTGAAGCAACTTTACTTGTAGCATCTGTAACTTTTTGTACGTCCGGATGTTGTTTGATACGT +TTACGTTCAACAATTAACGGTACCAATACAATTGGTAATACATTAATCATAAATTTGATGACTTTTTTCTTATCCATAGA +TCTTGCCTCCATAATTACTTTATTAATTTTACATACCCTATGATACATCAATATAAACGATGATAGTAGTGAATCACTAT +TAAGTATTTCAGATGTTTTTTAAAAGAAGACAATAAAAACTGCCAATCAAGTGATTCCTTAATTGACAGTCTATATTTTA +AAAGGAAATTAAATACCTTTACCAATGCCAAATCCGAAGTAAAGTATAGCAATAAAGATTACTAATACAATTCTGTAAAT +GGCAAATGGAATTAGTTTGATTTTGTTAATTAGATGCAAGAATGTTTTGATTGCAATTAGTCCAACAGTAAATGCAGCTA +AAAAGCCTAAAATATAAAAAGGTATATCAGCAATCTGAATATCTTGATAATGTTTTAATAAAGATAAACCACTAGCTGCT +AACATAATTGGAACAGCCATAATAAATGTAAAGTCCGATGCTGCTTTATGATTTAATTTCATTAATACCCCAGTTGAAAT +TGTTGAGCCTGAACGGCTGAAACCAGGCCACATAGCTACTGCTTGAGAAATACCAATTACAAATGCTTGGAAATAACTGA +TTTGATCTACTGTTTGTGGGTTTTTAACTTTAGCTGAGTATTTATCAGCAATAATCATATAGATAGCACCTACGAATAAG +CCAATCATAACAGTTGGCACACTAAATAAATGTTCTTCGATGAAATCATCAAATAGTAAGCCTAAAATACCTGCTGGCAC +CATACCCACTAATACATGTAATAAATTTAAACGTCTTGGCTTTGAACGTCTTTGTTGATCGTTATCTCCTTCAACATGTT +TGTGTTTACCAATATGTAAAATCTCTAAGAAGCGTTCGCGGAACACCCATGCTGCTGCAAAGACGGATCCTAATTGGATG +ACGATTTTAAATGTAAATGCTGACTGAGAACCTAAAAATTCAGATGATTTTAACCACATATCATCAACTAGGATCATATG +TCCAGTAGAGGAAACAGGTGCAAATTCTGTTAATCCTTCGACGACCCCTAAGATAATACCTTTTATTAATTCAATGATAA +ACATAATGTACCCACTTTCATTACTCAATTTAATTTATTTAAATATCAAAATTACCATATCATGATAGCATATTCATTTA +AAGACATGCTAGTTATAGTTATAATACTAGACTAAAGATGTATATATTCATTTTCTTTTACATGTAAAACTACAATATTT +TATTGAGCTATTTAATTTGATTTTAAGGAAAACCTTTTATAATAGGTTTAGGTGATATAATTGTGAAAAAATTAACAACA +ATACTGTTTCAATATAAAATTTTTCCGGTACTCATGTTCTTGGTCAGTACTGGTCTCGGCATAATCGTTATAACGCAAAA +TATTTTAATAGCAGATTTTTTAGCTAAAATTATAAGACATCAATTTCAAGGTTTATGGATTGTATTATTTATTTTATTAG +GTGTTTTACTTTTAAGAGCAACTGTGCAATTTCTAAATCAATGGTTAGGTGATACATTAGCATTTAAAGTTAAGCATATG +CTTAGACAGCGGGTTATTTATAAAAATAATGGTCATCCAATCGGTGAACAAATGACTATACTCACAGAAAACATTGATGG +TCTAGCACCTTTTTATAAGAGTTATTTGCCTCAAGTGTTCAAATCAATGATGGTTCCGCTCATCATAATCATTGCAATGT +TTTTCATCCATTTCAATACCGCATTAATTATGTTAATAACTGCACCATTTATTCCTTTGTTTTATATTATTTTCGGTTTG +AAAACGCGAGATGAGTCAAAAGATCAAATGACTTATTTGAATCAATTTAGTCAACGGTTTTTAAATATTGCTAAAGGTTT +AGTGACGTTAAAGCTATTTAATCGTACAGAGCAAACAGAGAAGCATATTTACGACGATAGTACTCAGTTTAGAACTTTAA +CAATGCGCATTTTACGCAGTGCTTTTTTATCGGGATTAATGCTCGAATTTATAAGTATGTTAGGTATTGGATTGGTTGCA +TTGGAAGCAACGCTAAGCTTAGTAGTATTTCATAATATTGATTTTAAAACTGCGGCAATTGCGATTATTTTAGCGCCTGA +ATTTTATAATGCAATTAAGGACTTAGGGCAAGCGTTCCATACTGGAAAACAAAGTGAAGGTGCCAGTGACGTTGTGTTTG +AGTTTTTAGAACAACCGAACTATAATAATGAATTTCTATTAAAGTATGAGGAAAACCAAAAGCCATTTATTCAGTTAACA +GACATATCATTTCGATATGATGATTCTGATAGATTGGTATTAAATGATTTAAATTTGGAAATATTTAAAGGTGATCAAAT +TGCACTTGTAGGTCCAAGCGGGGCAGGTAAATCCACTTTGACACATCTTATTGCAGGTGTTTATCAGCCAACAATAGGTA +CTATAAGTACAAACCAGCGTGATTTAAATATAGGAATACTTAGTCAACAGCCATATATTTTCAGTGCTTCTATAAAAGAG +AATATTACGATGTTTAAAGATATAGAAAATAATACTATTGAAGAAGTGCTAGACGAAGTAGGTTTATTAGACAAAGTGCA +ATCTTTCACAAAAGGCATTAACACAATAATAGGTGAAGGAGGCGAAATGTTATCTGGTGGACAGATGAGACGCATAGAAC +TTTGCCGTCTTTTAGTTATGAAGCCAGATCTCGTTATATTTGATGAGCCTGCAACTGGTTTAGATATTCAAACAGAACAC +ATGATTCAGAACGTTCTGTTTCAACATTTTAAAGATACAACGATGATTGTCATTGCACATAGAGATAATACAATTCGCCA +TTTACAACGACGCTTGTATATAGAAAATGGAAGACTGATTGCTGATGATCGCAATATTTCAGTAAATATAACAGAAAATG +GTGATGACTTATGAAAACACGACTAAAATTTCAAGTAGATAAGGATTTATTGTTAGCTATAGTTGTTGGTGTTTGTGGAA +GTTTAGTTGCGCTCGCCATGTTTTTCTTAAGTGGTTATATGGTGACACAAAGTGCACTTGGTGCGCCACTATACGCTCTG +ATGATTTTAGTCGTTACAGTAAAATTGTTTGGGTTTTTAAGAGCTATTACTCGATACGTAGAGCGCCTTATTTCTCATAA +AGCTACATTTACAATGCTACGTGATATTCGGGTACAGTTTTTCGGTAAATTAGTAAATGTCATTCCTAATGTTTACCGTA +AACTGAGTTCTAGTGATTTAATTTCACGTATGATTAGTCGTGTTGAGGCATTACAAAATATATATTTACGTGTTTATTAT +CCACCAGTCGTCATCGGTTTGACAGCGCTAGTTACAGTCATAGTTTTGGCGTTCATTTCAATCGGCCATGCGCTATTGAT +TATGGTTAGCATGTTGTTCACTTTACTCATTGTTCCTTGGTTAAGCTCAAAAAAAGCACGTACTTTAAAGAAACATGCAG +CTAATGAACAGGCCCGATTTTTAAATCATTTTTATGATTATAAAGCTGGTATGGATGAACTACGTCGATTTAATCAAATT +AATCATTATCGAGATAATTTGATGGCTAAATTAAATCATTTTGATAAATTACAACTTAAAGAGCAACGCTTTTTAACGAT +TTATGATTTTATATTAAATATTATTGCTATGCTTTCGATTTTTGGTAGTTTAGTTCTAGGATTAATTCAAATTAATGCAG +GCCAACTAAATATTATTTATATGACGAGTATAGTTTTAATGGTCTTAACTTTATTTGAACAAGCTGTACCAATGACAAAT +GTCGCGTATTATAAAGCGGATACTGACCAAGCATTGCACGATATTAATGAAGTGATATCTGTACCTTCTACTAATGGAAA +AAAACGTCTTAATGATAAGTATGATGCAACGAACATTTATGAAGTTAAGGATGCTAGTTTTAAGTATTGGAATCAGCAAA +CGTATGTGTTGTCGGATATTAATTTTAATGTTAATAGAGGCGAAAAGATTGCGATTGTGGGTCCTTCTGGTTCAGGAAAA +AGTACATTACTACAAATTATGGCAGGGTTATATCAATTAGATAGTGGCTCTGTTCGTTTCGAAAATATGGATATGTTTGA +AATAGATGACAAAGATAAGTTTGAATCGTTAAATGTCTTGCTACAATCTCAACAATTATTTGATGGTACAATACGTCAAA +ATTTATTTACCGATGAAAAAGATGAAGCGGTGCAAGCAATATTTAAGCAATTAGATTTAGAACATTTGGCACTAGAACGT +CAAATTGACTTAGATGGTCATACATTATCTGGCGGAGAAATTCAGCGTTTAGCGATTACGAGGATGTTATTAAAAGATAC +TGCATCAACATGGATTTTAGATGAACCAACAACTGCATTAGATAAACAAAATAGTTTAAAAGTTATGGATTTAATTGAAG +CACATGCAGAAACATTAATTGTTGCTACACACGATTTAACTTTATTGTCACGTTTTGAGACCATCATTGTGATGATAAAT +GGTAAAATAGTTGAAAAGGGAAACTATCAACAATTACTAGCTAATCAAGGTGCTTTATGGAATATGATTCAATATAATGC +ATAAAAAAACTGCTTGATAGGCTAAGAAACCTTTCAAGCAGTTTTTTAACTAACAAGCTTACGCGTGCTAATTGTTTTTC +TATTCTTAATAAATTCTAAACATTACTTTAATTGTCATGACAAAAGTTAATTATTTTTCCTTTGTTTCATCAAATGCATG +AATGACTTTACCTAATAAGCGATTAAGTTCTTTAACTTCATCTTGCGATAAAGAAGAAGCTGAAGCGACTTTGTCAGATG +CATTACTTAATTCTGGTCTAATAGTTTCACTTTTGTCAGTCAAGTGAATAAATACTTCACGTTGATCGACTTCGGAACGT +TCACGCTTAATTAAGTCTACTTGTTCCATTCGTTTTAATAATGGTGATACTGTACCAGTATCGAGTGCTAATTCAGTTAC +GACTTTCTTGACGTTTACAGGAGATTCATCCCATAAAATTGTTAAGACAAGAAATTGTGGGTATGTTAGATTGTACTTCT +TAAAAACTTTGTTAGAGTAGTAGCGATTAACTTGTCTTTGAGCATTGTACAAACTAAAGCATAGCTGTTCTTTTAAATTA +TGTTGATCAGACATTAAAGTTCTCCTCCAGACATACTATCCGTTTTTTTCTCTTTTCGGATTGGTAATCATTAAAAAGTT +GATTGTTTATTAATTCACAACTTTCTTTGATTCAATGCCATGCTAAAATTAAAGTATGTTTAAAGTTTAGAAGATATTTT +TGATTAAATCAAGCAAAAAGATAATTTAATATATATGTTATCATTTTTAAAAATAACTGTAATAGAAAAGAGAATATAAA +ATGAAAAATAATAAAGATGAAAAAATAAGAATATCCATAATTAACGGATTTTTGGGTAGTGGTAAAACCACGTTACTGAC +ACATTATATTAGTGAATTATTAAAAAATGATGAGAAAATTAAAATCATCATGAATGAATTCGGTACTTTTGATATTGATA +GCAATAGTATTTCAAATGAAATTGAAGTCCATTCATTGATTAATGGTTGTGTTTGTTGCGATCTTAAACAAGAACTTGTC +TATGAACTAAAAGCCATTGCTTTAAAAGGGGACGTTAATCATGTCATCATAGAAGCGACAGGCATTGCGCATCCTTTGGA +ATTACTAGTTGCATGTCAAGATCCGCAAATCGTTAATTTCTTTGAAAAGCCGATTATTTATGGTGTATTAGATGCGACTC +GATTTTTAGAACGTCATCAATATACCGAAAATACAGTTTCGCTGATGGAAGATCAGTTGAAACTAAGTGACATGATTATT +ATTAATAAAATTGATCTTATAACTGATGACAGTCTTGAGAAAATTGATAAGCAATTAGGTATGATTTGTGCAAGTATTCC +AACTTATAAAACAACCTATGGAAAAGTTTCGTTGGAAGAATTGGACTTAACTGTTAAAGACAGAGAGATATCGTCTCATC +ATCACCATCATCATGGGATTAAAAGTATGACTTACACGTTTACAGGTCCGATTGATCGTCATTTGTTTTATCAATTTATA +ATGAAATTACCGGAATCTGTTCTACGTTTGAAAGGTTATGTGTCATTTAGAGATCAACCAAATGCAATTTATGAATTTCA +ATATGCATATGGTTTACCAGACTATGGAATAATTGGCATGCAATTACCATTAACGATTGTTATTATTGGTGAAACTTTAG +ATACAAATCACATACGTAATCAATTGGATATGCTACAATTTACGTAAATCAGAAAATGTGTTGTTTCAATTTAACGGAAA +TAGCACATTAATTTCTTAATACTTTTATTGTGAATATTTTGTGACATTGAGGGATGGAGTGGATGAGATATGGAACAAAT +AATGATTAATCACTATGTTCATTTTTCTAGGCTTGTACAAGGTTTTTGGCGTGCAAATGAATGGAAGATGACTGCGAAAG +AGTTAAATTATTTTATAAATGAATTAGTTGAACGTGGAATTACAACGATGGATCATGCTGATATTTATGGAGATTATCAA +TGTGAATCACTGTTTGGTAATGCTTTGGATTTATCACCCGAATTAAGAAATAAAATTCAAATTGTTACGAAATGTGGTAT +CATTTTGCCTTCTAAGCAATTTGATTTTACAAATGGACATCGTTATGATTTGAGTAGTAAGCACATCGTGAAATCTGTTG +AACAGTCATTAATCAATTTGAATGTAGATTATTTAGATAGTCTACTCATTCATCGTCCTTCACCATTGATGGATCCAGAA +CAAGTTGCTGATGCATTAACTAAACTTGTTAAACAAGGTAAGTTGAAGTCATTCGGGGTGTCGAATTTTAATCATTCACA +ATACCAATTGTTAAATCAATATATTATGAAAGAAAGACTACATATTAGCATCAATCAATTAGAATTATCGCCATATCACG +TTGATAGTTTACAAGATGGAACAATGGATTCAATGTATCAAAACCATGTTCAAATCATGGCTTGGAGTCCTTTTGCAGGC +GGTAAAATTTTCGACAAGGAAGATATTAAAGCGCAACGTATTATGAAAGTTGTTCAATCAATAGCTGACAAATATGGTGT +GAGTGACACAGCTGTGATGATAGCGTGGTTAGTAAAAATACCGCATCGTATCATGCCGATACTTGGAACAAGTCAGTTAA +AGCGTATTGATCAAGCAATCGAAGGGCTACAACTTAATTTAGATGATCAGTCGTGGTTTGACATTTACACCGCTATTATC +GGACAAGATATTCCGTAAACTTATTTACTTTTAAATCATAAAGGAGCATATCATGACAAACGAAGATAAACGTTTCGAAC +AATTAAGATTTGAACGCAAATTTATAGTTATTCCGTATTTAATTTATGCAGTCATTGTATTACTATTAAATATTTTCTAT +TCTGATTTGAAAATAACAATGACATTATTCGGACTTTTCTTTGCGTATAATGTAGTCATTTTGTTCATAGCATTTGTTAA +ACATTATAAACGCACATTGTTACTAAGTCTTATATTAACAGTGCTTAGTGGCGCGGCATTCTTTGGAATTATTTATGTTT +ATGGCATTAATCATTTTTAAACATTAAAAAGAGACTATCTACAATAATCTCATTTGCATTTTATCTAACTAAGTTTAACT +TTTGCTTAGAAAAATGATGCTATGAGGATATTCAGTAGATAGTTTTTTTATGATCTAATATTTAAACTTAGATATCGTTT +TGTAATTAACCGAGATAACTCATCACCTAGCAATATTATTTACAAAATATTTCGATAAAATTGTGTATTTTGCATAAATT +AAAAATGTATGTTTTAAAAAATTGTTATAATTAAATTAAGATTAAACAAAGGGGTGACTGTTATGTCAGAAGAAAAACAT +GTAGTTGAACATGAACAACAAAAGAAAGAAAAGACAAAAAAGCAATACAAGCCATTTTGGATTGTCATGAGTTTTATAAT +ACTTATAGTTGTACTATTACTCCCGGCACCTTCAAGTCTGCCGATAATGGCTAAGGCAGTACTAGCTATTTTAGCTTTTG +CAGTTATTATGTGGGTAACGGAAGCTGTATCATATCCGGTGTCAGCAACTTTAATTATTGGCTTAATGATATTACTTTTA +GGATTTAGCCCTGTTCAAAATTTAGGGGAGAAGCTAGGTAATCCGAAAAGTGGCAGTGCTATTTTAGCTGGAAGTGACCT +TCTAGGAACTAATCATGCATTATCATTAGCGTTTAGTGGATTTGCAACTTCAGCTGTAGCTCTCGTTGCAGCTGCATTAT +TTTTGGCTGCTGCTATGCAAGAAACGAATTTGCATAAAAGACTAGCTCTTTTAGTGTTATCAATTGTTGGTAATAAAACT +AGAAATATAGTTATTGGAGCAATTATCGTTTCAATTGTACTTGCATTTTTCGTTCCTTCTGCAACAGCTAGAGCAGGGGC +AGTTGTACCAATCTTGCTGGGTATGATTGCGGCATTTAAAGTTTCCAAAGATAGCAAGTTAGCGTCTTTATTAATAATTA +CTTCAGTACAAGCTGTGTCAATTTGGAATATTGGTATCAAAACGGCGGCAGCACAAAATATCGTAGCGATTAATTTTATA +AACCATCAATTAGGATTTGATGTTTCATGGGGCGAGTGGTTCTTATATGCAGCGCCTTGGTCCATAGTTATGTCCGTAGC +TTTATATTTCATCATGATTAAAGTGATGCCTCCAGAAATTAATACAATAGAAGGTGGTAAAGATTTAATAAAAGAAGAAT +TGCATAAACTTGGCCCCGTTAGCCCACGTGAATGGCGTTTAATTGTTATATCGATGTTATTATTACTGTTTTGGTCAACT +GAAAAAGTATTACATCCGATTGACTCTGCATCCATTACTATTATTGCTTTAGGTGTTATGTTAATGCCGAAAATTGGTGT +CATGACATGGAAACATGTTGAAAATAAAATACCATGGGGAACAATTATCGTGTTTGGTGTAGGTATTTCACTAGGTAACG +TTCTTTTGAAAACAGGTGCAGCTCAATGGTTAAGTGATCAAACTTTTGGTGTTTTAGGTTTAAAACATTTACCTATTATC +GCGACAATTGCACTTATCACGCTTTTTAATATATTGATTCATTTGGGCTTTGCGAGTGCAACAAGTTTATCATCAGCGTT +AATACCTGTTTTTATTTCGCTAACCTCTACGTTACACTTAGGAGACCAGTCTATAGGATTTGTTTTAATTCAACAATTTG +TTATTAGTTTTGGTTTCTTATTACCTGTTAGTGCACCTCAAAATATGTTGGCTTATGGCACTGGTACTTTTACGGTTAAA +GATTTCTTGAAGGCAGGTATACCATTGACAATTGTAGGGTATATTCTAGTGATAGTTTTTAGCATGACTTATTGGAAATG +GTTAGGTTTGCTTTAATTAAAAATATAAATAAGAATCTAGGTTATTTTAAAGTGACAAAAAGCTTAATAAAATAAAAAGA +TAATTGAAGGGTGTTTTGTTTATGGCAATTGCTGTGTTATTAAATCGAATGTTTCGAATGGAACACAATCCATTATTTGA +ATATATTTATCAACAAAAAGAAGACATTGATGCATGTTATTTTATCATTCCGGAAGAGGACATGTCTTCAGCTTCTGATT +TGAAAGCACAGTTTTATCGCGGTACTTTGCAGCGCTTTTACCAATCGTTGCACGCAGAAAAGCTTACACCTTATGTTATG +TCTTATGACGATATCATTTCATTTTGTAAAGAAAACAATATCTCTGAAGTAGTGACTGCGGGTGATATTATGAGTTATCA +TCTTGAAGAATATGATATTTTACATCAACGTTCTTTATTCAATGAAGCACGCATTGCCGTTACTTTGATACGTGGGAATC +ATTACTTTAAAGCGAGTAAAACAATGAATCAACAAGGGGAGCCATACAATGTTTTTACTAGTTTCTATAAAAAATGGCGA +CCTTACTTGAGGCATAGAGACGTATATCACTATGATTTAAAATCATTCGAAAACTTTGTCATTGCATCACCTGATGATTT +AGTGTTTGATGACATAGCATTTGGATCCTCACAAATAATTGAACAGAATAAATGGCAACATTTTTTAGATCAAGATATAC +AGAATTACGAAAGCGGAAGAGACTATTTACCTGAAGTATTAACAAGTCAGCTAAGTGTTGCTTTAGCATATGGATTATTA +GATATTATTGAAATTTTTAATGATTTATTGGCGCGTTATGATGAAGATGAGGCAAACTATGAAGCATTTATACGTGAACT +CATTTTTAGAGAATTTTATTATGTGTTAATGACACAGTATCCTGAAACCTCATACCAAGCTTTCAAACCTAAATATCGAC +AGATAAAATGGTCGCAAAATGAAGCGGATTTTAATGCATGGTGCGAAGGGCAAACAGGATTTCCAATCATTGATGCAGCA +ATAATGGAATTGACACAAACTGGTTTTATGCATAATCGAATGAGAATGGTTGTGTCGCAATTTTTAACCAAAGATTTATT +TATAGATTGGACATGGGGAGAAAAATTCTTTAGAAAGCACCTTATTGACTATGATGCAGCATCAAATATTCATGGATGGC +AATGGTCTGCTTCTACAGGTACGGATGCAGTGCCGTATTTTAGAATGTTTAATCCAATAAGACAGAGTGAACGCTTTGAT +GCTAAAGCTTTGTATATCAAAACATATCTTCCGATTTTTAATCAAATTGATGCAAAATATTTGCATGATACACAACGCAA +TGAGTCCAACCTTTTTGAACAGGGGATTGAATTAGGTAGTCATTATCCAAGACAAATGGTAGATCATCAAGAAAAACGTA +CACAAGTTTTAGCTACATTTAAAGCGCTAGACTAATTCGGCTCAATTGATAGATCTGGCATGAGTAAAATCTGGAGCAGA +ATATAAAGTTCTTAAGCAATATAAATAAGCCAATTTCTATTATTGATGTGATAGAAATTGGCTTTCCTCAAATATATAAG +TTGTATTTAGTTTTCTTATTAATTTGATGTTATCTTAGTATGTCCGTAAATAAAGTGAGGTATAGTACGACATACTCTAA +AAACGTAGCGAGATAAATATATTTCAATCTAACTTTTATGTTTTGAGGCACTTGCCATTTAGGATATTGTCGTTCGTAAT +ACGACACTTGTTGTATAAATACACCTAGTCCAAATGGCAGCATCATGAGTAAGATACTTCTTAAATAACTTAAACCAATA +TCATGCCATATGTGTCCAATAATCAATTGAAAGACAATGATAGATACTATTAAAACGATTATATTTATTGTCACTTGTTC +AAACGCACTCCTTTTCCAAATAATAGAATTGCTGCTTGCATGACAACCATAAAACATACAAACATAGCAGTTTTAAGCGT +TAGACTTTCTAGAATGTGATTTAGAACATGTAAGGGCTCATTAAAGAAATAAACGGAATGTAAGCGTAAGAAACGACCAA +TATAAATTCCGAATCCATTTAAAAACATTAGCACGACAACAATTAATCTATTAAGCCAACGGTGAGAAGTCAATGTTAGT +ATTTCAAAATAGATTAAAATCATCACATAAACCGCTAAGAAGACACCAAGCAGTAAATAGGTAAAGTATTTCCACTCACT +TAAATTTAGTCCTGCGTAAAAGTTGAATTGAAATTGGTTCAAATGGATTAAATCAGTTACCATATAAAATGTATTTGGTA +AAAGTAACACAAATATAAAACTATATATGATAAAGAGTGGCCATTCATACTTTTTATTCGGTTTGAATAATCGTAATAAT +AGACTAAGCTCAAAAGGTATATATGCTAAAAACAAATTTAAAGTCATAAATTGAAAAATTTTAGTCTCAAAAAGGGAGAC +GATAAATAAAATTAAAAAGTAAATTCTAGCGATGTATCGAGATTGCATCAATAATAAACACTACTTTCTAATGAAAAAGT +TATTCATTCAAATGTGATAGTTATAAACGCATTCAATTCCTAATTAAAAATTTAATTTAACTCGTTGAAGTATATTAATA +AACGAGAATGCATTTATGATTAAGCACTTATGATAACTTGTAATAAAATTTGATTCAACTAAAAAGTAAAGTGCTATTGA +GGATCAAGTAAATTTAAACCGACTGCATCTTTGAATGAACGTTGAATGTCCAAATCAGTTACAGTAAGAATGATTTCAGC +AGTTGCAGAAACAATTGCTTTAGCGAGATGTCCGCTAAAAGTTTCATATGATTGAGTACCGAAAGTAGCATGCAAATGTG +CAAAATGACCATTGTCTAGACGAGAAATATTACCTAATAAGCTCGTCAATTCCAGTGGCTCAGTAATATGTTTTTCTTCG +TATTGTTTCGTTGTTAAATTGAAAAATTTTAATACAACGTCATCACATGCACCAATGCCGCTGACAGATGTAAATGTTAA +GTCTTGGTCATCTGCAAAGGTTGTTATACATTCAACGATATCTTCTCCTTTTTCCAACACTAGTAGTATAGTATGATTAC +TTTTTTGCAATTTCATATGATCAATCCCCTTTATTTTAATATGTCATTAATTATACAATTAAATGGAAAATAGTGATAAT +TACAAAGAAAAAATATTGTCAAATGTAGCAATGTTGTAATACAATATAGAAACTTTTTACGAATATTTAGCATGAATTGC +AATCTGTCGTGGAAAAGAAGAATAACAGCTTTAAGCATGACATGGAGAAAAAAGAGGTGAGCATATGAATAAACAGATTT +TTGTCTTATATTTTAATATTTTCTTGATTTTTTTAGGTATCGGTTTAGTAATACCAGTCTTGCCTGTTTATTTAAAAGAT +TTGGGATTAACTGGTAGTGATTTAGGATTACTAGTTGCTGCTTTTGCGTTATCTCAAATGATTATATCGCCGTTTGGTGG +TACGCTAGCTGACAAATTAGGGAAGAAATTAATTATATGTATAGGATTAATTTTGTTTTCAGTGTCAGAATTTATGTTTG +CAGTTGGCCACAATTTTTCGGTATTGATGTTATCGAGAGTGATTGGTGGTATGAGTGCTGGTATGGTAATGCCTGGTGTG +ACAGGTTTAATAGCTGACATTTCACCAAGCCATCAAAAAGCAAAAAACTTTGGCTACATGTCAGCGATTATCAATTCTGG +ATTCATTTTAGGACCAGGGATTGGTGGATTTATGGCAGAAGTTTCACATCGTATGCCATTTTACTTTGCAGGAGCATTAG +GTATTCTAGCATTTATAATGTCAATTGTATTGATTCACGATCCGAAAAAGTCTACGACAAGTGGTTTCCAAAAGTTAGAG +CCACAATTGCTAACGAAAATTAACTGGAAAGTGTTTATTACACCAGTTATTTTAACACTTGTATTATCGTTTGGTTTATC +TGCATTTGAAACATTGTATTCACTATACACAGCTGACAAGGTAAATTATTCACCTAAAGATATTTCGATTGCTATTACGG +GTGGCGGTATATTTGGGGCACTTTTCCAAATCTATTTCTTCGATAAATTTATGAAGTATTTCTCAGAGTTAACATTTATA +GCTTGGTCATTATTATATTCAGTTGTTGTCTTAATATTATTAGTTTTTGCTAATGACTATTGGTCAATAATGTTAATCAG +TTTTGTTGTCTTCATAGGTTTTGATATGATACGACCAGCCATTACAAATTATTTTTCTAATATTGCTGGAGAAAGGCAAG +GCTTTGCAGGCGGATTGAACTCGACATTCACTAGTATGGGTAATTTCATAGGTCCTTTAATCGCAGGTGCGTTATTTGAT +GTACACATTGAAGCACCAATTTATATGGCTATAGGTGTTTCATTAGCAGGTGTTGTTATTGTTTTAATTGAAAAGCAACA +TAGAGCAAAATTGAAAGAACAAAATATGTAGCATAAGTATTTTGGTGTATATTGATATAAAGTAAAGCGTAATATTATGA +ATGATTAGCATCGTTTTTCTTATGAATTTTATTAAGAAAATTCGATGCTTTACATTTAAAAAGATTCGATTGACTAAATG +TTTTACTCTTTATATTTAAATGTTATATGTAACAAAAAATGATTTTGAGTAATAAACATGTTACAAATATTACATTCTTT +TTAAATTGCAATCCACATACCTAATTCATTAACGTTAATGTGTTAAGATGATAAAAAATGAGTAAGGAAATGTGGGTAAG +GGGATGACAGTAAAAAATTTATTTTTAGGCTTTGTTGCTGTAATATTAACCGTTTGTTTAATTGGTTTATTAATATTAGC +AACAAATGAAGATGCGCTTGCTAAGGTACATAAAACAATTAATACGCTTAACGCGATAAATGTATCAACTGAAGATACTT +ATAAAAAGAAAATGGATATTCTCAATATTCATACTGCTAAAGCATCTGAAGTGAATGAAAATGTGAAAAAGCAAAATCAT +TTTAAACATCGTGTGAATGCAAATAAATCAAATTCTTTTAACGAACAAGAGTGCCAAGTTATTGCTGATCGTTATGCAGA +TAAGCATATCAATGATAATTATGGTTTAGAAAGAATTTCTAAGACAAATCATGGATATAATTATGTGTATTCCAATGATA +ATTCAACTAGTAAGCAACATGTAAGTATTTCAAATCAAGGCATAATAACGAAATAATAGATGGAACAGTGTATTCTAATT +GGATATACTGTTTTTATTTTGCAATAATTTAATTTAAAAAGGTGAATTCAACTTATAAAATGATGTAAATGTTATGTCAA +AATCAACCAATCCGTAATGTATTTTAAAATGTTAATATAGTTCTGAAGAAGTATAAATGAGGTGTTGAAATGGCTAAAAA +TAAGAAAACGAACGCGATGCGTATGCTTGATCGTGCAAAAATTAAATACGAAGTTCATAGCTTTGAGGTACCAGAAGAAC +ATTTATCTGGTCAAGAAGTCGCAGAACTCATACAAGCAAATGTTAAAACAGTATTTAAAACGCTTGTTCTAGAAAATACA +AAACATGAACATTTTGTATTTGTTATCCCAGTAAGTGAAACTTTAGATATGAAAAAGGCAGCTGCTTTGGTTGGAGAGAA +GAAATTGCAGCTTATGCCTTTAGATAATTTGAAAAATGTAACGGGATACATTCGTGGTGGGTGTTCGCCTGTTGGTATGA +AAACATTGTTTCCAACAGTCGTTGACAAATCGTGTGAAAATTATAGTCATATCAGTGTGAGTGGTGGGCTTCGAACAATG +CAAATCACAATAGCTGTTGAGGATTTGATTACAATAACTAAAGGCAAAATTGGAGCAGTTATCCATGAATGATTAATAAC +AACAAAAAGTATGGGGCAAGATTAGGAGTGTGACAGAGATGAAATTTTTATAAAATTCATTTCTGCCACACTCCTTTTTG +ATTGAATTAGCATTTTACGATCATAAACAGTCATTATAATTGAGTATTTGAACATAAAAATGTAATTTTATCGTAACAAT +TTGAGTGTTTGTGATTGTTTTTGGTAATTTATGATTGAAAAGTGAAAGCGTACTCATTATAATACAAAGTGAGATGGGGT +GATGATGATAATTACTGAAAAAAGACACGAGTTAATATTAGAAGAACTTTCGCACAAAGATTTTTTGACTTTACAAGAAT +TAATAGATCGAACTGGTTGCAGTGCTTCAACAATACGAAGAGATTTATCTAAACTACAACAATTAGGGAAATTGCAACGT +GTGCATGGTGGTGCAATGTTAAAAGAAAATCGTATGGTTGAGGCGAATTTAACTGAAAAATTAGCAACGAATCTTGATGA +AAAGAAAATGATTGCTAAAATAGCAGCTAATCAAATCAACGATAATGAATGCTTATTTATCGATGCTGGTTCATCTACAT +TGGAGCTAATTAAATATATTCAAGCGAAAGATATCATTGTGGTAACCAATGGTTTAACACATGTAGAAGCTTTACTTAAA +AAAGGTATTAAAACAATTATGCTAGGTGGTCAAGTTAAAGAAAATACACTTGCTACGATTGGTTCTAGTGCTATGGAGAT +ATTAAGACGATATTGTTTCGATAAAGCTTTTATCGGGATGAATGGATTAGATATTGAACTTGGATTAACTACTCCCGATG +AGCAAGAGGCATTAGTTAAACAAACAGCAATGTCATTAGCCAATCAATCATTTGTACTTATAGATCATTCTAAGTTTAAT +AAAGTATATTTTGCTCGTGTACCTTTGCTAGAAAGTACGACAATCATCACATCTGAAAAAGCATTAAATCAAGAATCGTT +AAAAGAATACCAACAAAAGTATCACTTTATAGGAGGGACTTTATGATTTATACAGTGACTTTCAATCCTTCAATTGACTA +TGTCATTTTTACGAATGATTTTAAAATTGATGGTTTGAACAGAGCAACAGCAACATATAAATTCGCTGGGGGGAAAGGTA +TTAATGTCTCGCGCGTCTTAAAGACATTGGATGTTGAGTCAACTGCCTTGGGATTTGCAGGTGGATTTCCTGGGAAATTC +ATTATAGATACATTAAATAACAGTGCAATTCAATCGAATTTTATTGAAGTTGATGAAGATACACGTATTAATGTGAAATT +AAAAACAGGACAAGAAACAGAAATCAATGCACCGGGTCCTCATATAACGTCAACACAATTTGAACAACTGTTACAACAAA +TTAAAAATACAACAAGCGAAGATATAGTTATTGTTGCTGGAAGTGTACCAAGTAGTATTCCAAGCGATGCGTATGCGCAA +ATTGCACAAATTACAGCACAGACAGGTGCTAAATTAGTAGTCGACGCTGAAAAAGAATTGGCTGAAAGCGTTTTACCATA +TCATCCACTATTTATTAAACCTAATAAAGATGAATTAGAAGTGATGTTTAATACAACAGTGAACTCAGACACAGATGTTA +TTAAATATGGTCGTTTGTTAGTTGATAAAGGTGCGCAATCTGTTATTGTCTCGCTTGGCGGTGATGGTGCTATTTATATT +GATAAAGAAATCAGTATTAAAGCAGTTAATCCACAAGGGAAAGTGGTTAATACAGTTGGCTCTGGTGATAGTACAGTTGC +AGGCATGGTGGCTGGAATTGCTTCAGGTTTAACGATTGAAAAAGCATTCCAACAAGCAGTCGCATGCGGTACTGCCACGG +CATTTGATGAGGACTTAGCAACACGGGACGCTATAGAAAAAATAAAATCACAAGTTACGATTAGCGTACTTGATGGGGAG +TGAAAATAATGAGAGTAACAGAGTTATTAACAAAAGATACAATAGCAATGGATTTAATGGCAAATGACAAAAATGGTGTT +ATTGATGAGTTAGTAAATCAATTAGACAAAGCAGGTAAATTAAGTGATGTCGCGTCATTTAAGGAAGCGATTCACAATCG +AGAATCACAAAGTACAACTGGTATCGGCGAAGGTATTGCCATTCCACATGCCAAAGTGGCCGCAGTTAAGTCACCAGCTA +TTGCGTTTGGTAAATCTAAAGCAGGCGTAGATTATCAAAGTTTGGATATGCAACCAGCACACTTATTCTTTATGATTGCA +GCGCCAGAAGGTGGCGCCCAAACACATCTAGATGCTTTAGCTAAGTTGTCTGGTATTTTAATGGATGAAAATGTACGTGA +GAAATTATTACATGCTTCATCACCTGAAGAAGTACTAGCGATCATAGATGAGGCTGATGATGAAGTGACAAAAGAAGAAG +AGGCAGAAGCTGAAGCACAACAAGTTGCAACTGCAGAACAATCATCTAAACAATCTAATGAGCCATATGTGTTAGCAGTA +ACTGCTTGTCCAACAGGTATTGCACACACATATATGGCACGTGATGCATTGAAAAAGCAAGCGGATAAAATGGGTATTAA +AATTAAAGTAGAAACGAATGGTTCAAGCGGCATTAAAAACCATTTAACTGAACAAGATATTGAAAATGCAACAGGTATCA +TTGTTGCTGCTGATGTTCATGTTGAGACGGATCGCTTCGATGGTAAAAATGTCGTAGAAGTACCAGTAGCAGATGGTATT +AAACGCCCAGAAGAATTAATTAATAAAGCATTAGATACAAGTCGTAAACCTTTTGTTGCCCGTGATGGTCAAAGAAAAGG +TAACTCAAATGACAGTCAAGAAAAATTAAGCCCAGGTAAAGCATTCTATAAACACTTAATGAACGGTGTTTCTAACATGT +TGCCACTTGTAATATCTGGTGGTATTTTAATGGCAATTGTATTTTTATTTGGAGCAAATTCATTTAATCCAAAAAGCTCA +GAGTACAATGCGTTTGCAGAGCAGCTTTGGAACATTGGTAGTAAAAGTGCATTCGCGTTAATCATTCCAATTTTATCTGG +ATTCATTGCACGTAGTATTGCGGATAAACCTGGTTTCGCTTCAGGTCTTGTAGGTGGTATGTTAGCAATTTCAGGTGGTT +CAGGATTTATTGGTGGTATTATTGCAGGTTTCTTAGCAGGTTACTTAACACAAGGTGTTAAAGCCATGACACGTAAGTTA +CCACAAGCATTAGAGGGATTAAAGCCAACATTAATTTATCCACTATTAACAGTGACGGCTACAGGCTTATTGATGATTTA +TGCCTTTAATCCACCAGCATCTTGGTTAAATCATTTGTTATTAGATGGATTAAACAATTTATCAGGTTCTAATATTGTAT +TATTAGGTTTAGTTATTGGCGCTATGATGGCGATTGATATGGGCGGTCCATTCAACAAAGCGGCATATGTTTTTGCAACA +GGTGCGTTGATTGAAGGTAATGCAGCACCAATTACAGCTGCAATGATTGGTGGTATGATTCCACCGTTAGCAATTGCGAC +AGCGATGTTAATTTTTAGACGTAAATTTACAAAAGAACAACGTGGTTCAATTATCCCTAACTATGTGATGGGTATGTCAT +TTATTACAGAAGGTGCGATTCCATTTGCAGCTGCCGATCCATTACGTGTTATTCCTTCAATGATGATTGGTTCAGGTATA +GGTGGCGCAATTGCTTTAGGCTTAGGTTCACGAATTACTGCGCCACATGGTGGTATTATTGTAATTGTTGGTACTGATGG +TGCACACTTACTTCAAACTCTTATTGCACTTCTAGTTGGCACATTAGTTTCAGCATTAATTTACGGTTTAATCAAACCAA +AGTTAACTGAAACAGAAATCGAAGCTTCAAAATCAATGGACGAGTAGTTTTAATGATGTAAAATGATTGTTAGCAAAGAG +CTTCATATTAAGTTGTATGTTCAATGAATATATGTTAGTTTTATATATCGTGTTAACGGTAGCTTATACAAAGCTGTAAA +AACACTTTCTATTAATTCAGTTTTTATGAATTGATATGAAAGTGTTTTTATTTTTAGATAAATGAATGAAGAAATAGACA +CCACAAATGTATAGACTTTTTTAATATTTTGCAAAAAGTTATGCCAAACGAAGCAGATATAGTAAAATATGAGTGTCTTA +AAGTGAAAATTTATAAATAAAGAAGGGTTTATACGTGTCAGAATTAATTATATATAACGGCAAAGTTTATACTGAAGATG +GCAAAATCGATAATGGTTACATTCATGTGAAAGATGGACAGATTGTTGCAATTGGAGAAGTGGATGATAAAGCAGCAATT +GATAATGATACGACAAATAAAATTCAAGTGATTGATGCTAAAGGTCATCATGTATTACCAGGTTTTATTGATATACATAT +TCATGGTGGTTATGGTCAAGATGCAATGGATGGGTCATACGATGGCTTAAAATATCTATCCGAAAATTTGTTGTCTGAAG +GGACGACATCATACTTGGCCACTACAATGACGCAATCGACTGATAAAATAGATAATGCACTTACAAATATTGCTAAATAT +GAAGCGGAGCAAGATGTTCACAATGCAGCGGAAATTGTAGGTATACATTTAGAAGGACCATTTATATCTGAAAATAAAGT +TGGTGCTCAACATCCGCAATACGTTGTACGCCCATTTATCGATAAAATTAAACATTTTCAAGAGACTGCTAACGGATTAA +TAAAGATTATGACGTTTGCACCTGAAATTGAAGGTGCAAAAGAAGCGCTTGAAACGTATAAAGATGACATTATTTTTTCA +ATTGGTCATACAGTAGCAACATACGAAGAAGCAGTTGAAGCTGTTGAGCGAGGAGCTAAACATGTCACGCATTTATATAA +TGCAGCGACGCCATTCCAACATAGAGAACCAGGTGTTTTTGGAGCAGCATGGTTGAATGATGCTCTACATACCGAAATGA +TTGTTGATGGCACTCATTCTCATCCGGCATCGGTTGCAATTGCTTACCGTATGAAAGGTAATGAACGTTTTTATTTAATT +ACCGATGCAATGCGTGCAAAAGGTATGCCTGAAGGAGAATATGATTTGGGTGGACAAAAAGTAACTGTTCAATCGCAACA +AGCACGTCTTGCAAATGGTGCGCTTGCTGGTAGTATTTTAAAAATGAATCATGGGTTACGTAACTTAATATCATTTACAG +GTGATACATTAGATCATTTATGGCGAGTAACAAGTTTAAATCAAGCCATTGCATTAGGTATCGATGATAGAAAAGGTAGT +ATTAAAGTAAATAAGGATGCAGATCTTGTTATTCTAGATGATGATATGAATGTAAAATCTACAATAAAACAAGGCAAGGT +TCACACATTTAGCTAATAAATAATCATAATTAAATGTATGCAATAGATTTAATCTGTTAACATAAGCACTTTATATTATG +ATAAAATAGAAGCAATAACATTTTTTTCTGGGGGTGTCTAAATGGGAAGGCGATAACATGTAGTTGTAATTTAAGTCATA +GTGATAAATTTGAATGCGTGTTACCCATGAGTGACACATATAACATGGAGGTGAATCCCTAGAAATAGGGAATTAATTGG +AAACTTCGACCATAATTAGTTTGATTATATTTATTCTATTAATTGCATTAACCACTGTATTTGTTGGTTCAGAATTTGCA +TTAGTAAAAATTAGAGCAACAAGAATTGAACAGCTAGCAGATGAAGGAAATAAACCTGCTAAAATAGTAAAAAAGATGAT +TGCTAATCTAGATTATTATCTTTCTGCTTGTCAGTTAGGTATAACAGTAACATCTTTAGGGTTAGGTTGGCTTGGTGAAC +CAACGTTTGAAAAGCTATTACACCCAATATTTGAAGCAATCAATTTACCAACTGCATTAACGACGACGATTTCGTTTGCA +GTGTCATTTATAATCGTTACGTATTTGCATGTAGTACTTGGTGAATTAGCGCCTAAATCTATAGCTATTCAACATACTGA +AAAGCTTGCTTTAGTATATGCAAGACCATTGTTCTATTTCGGTAACATTATGAAACCATTGATTTGGCTGATGAATGGTT +CTGCACGTGTTATTATTAGAATGTTTGGTGTAAATCCTGATGCCCAAACTGATGCAATGTCAGAAGAAGAAATCAAAATT +ATTATTAACAATAGTTATAATGGTGGAGAAATCAACCAAACTGAATTGGCATATATGCAAAATATCTTTTCATTCGATGA +AAGACATGCAAAAGATATAATGGTACCTAGAACTCAAATGATTACACTAAATGAACCTTTTAATGTAGACGAATTACTAG +AAACAATAAAAGAACATCAATTTACGCGTTATCCAATTACTGATGATGGTGATAAAGACCACATTAAAGGATTTATTAAC +GTCAAAGAATTTTTAACTGAATACGCTTCTGGAAAAACGATTAAAATAGCAAACTATATACATGAGTTGCCAATGATTTC +AGAGACAACACGTATCAGTGATGCATTAATTAGAATGCAACGTGAACATGTACATATGAGTCTTATTATAGATGAATATG +GTGGAACGGCAGGTATTTTAACGATGGAAGATATTTTAGAAGAAATCGTTGGAGAAATTCGTGATGAATTTGATGATGAT +GAAGTGAATGATATCGTTAAAATTGATAATAAGACATTCCAAGTAAATGGCAGAGTACTATTGGATGATTTAACTGAAGA +GTTCGGTATAGAATTTGATGACTCTGAGGATATTGATACGATAGGTGGATGGTTACAATCTCGTAATACCAATTTACAAA +AAGATGATTACGTGGATACAACTTATGATCGCTGGGTTGTTTCAGAAATCGATAACCACCAAATTATTTGGGTGATATTA +AACTATGAATTTAATGAAGCGAGACCTACTATCGGACAGTCTGATGAAGATGAAAAATCAGAATAGATATTAATATATAA +ACCAACTAAGAATGATTTAATTCATTTTTGGTTGGTTATTTTTTTGACTAAAATTAATGAAAAGTGAAAATAGTATTGGA +ACTCAATATCTTTAATGATTTAATGAATAATTTTTATTGAAAGCGATAATTCGTATTAATTGAGTTTGTTGAAAAATTTA +GGGTAATGTAAAGATATAAAAGATACATAGATTGGAGAGATATAAAGATGTTGAATGAGATACAAATATTAAATAATGGA +TACCCGATGCCTTCAGTTGGGTTAGGTGTTTATAAAATCTCTGACGAAGATATGACTAAAGTTGTAAATGCTGCAATTGA +CGCAGGCTATAGAGCGTTTGATACAGCATACTTTTATGATAATGAGGCTTCACTAGGACGAGCATTAAAGGATAATGGCG +TCGATAGAGAAGATTTGTTTATAACAACGAAGTTATGGAATGACTATCAAGGTTATGAGAAAACATTCGAATATTTCAAC +AAATCGATTGAAAATTTACAAACTGATTATCTTGATTTATTTCTAATACATTGGCCTTGTGAAGCAGATGGTCTATTTTT +AGAAACATATAAAGCTATGGAAGAACTTTACGAGCAAGGTAAGGTAAAAGCAATAGGTGTATGTAATTTTAATGTTCATC +ATCTAGAAAAATTAATGGCTCAATCAAGTATCAAACCAATGGTGAATCAAATTGAGGTACATCCATATTTTAACCAACAA +GAATTACAAGAATTTTGTGATCGTCACGATATTAAAGTGACTGCATGGATGCCTTTGATGAGAAATAGAGGACTACTAGA +CGACCCTGTCATTGTTAAAATTGCTGAAAAATATCATAAAACACCAGCACAAGTTGTATTACGTTGGCATTTAGCACACA +ATAGAATTATTATTCCAAAATCTCAGACACCTAAACGCATTCAAGAAAATATAGATATTTTAGATTTTAATTTAGAATTA +ACAGAAGTAGCTGAAATTGATGCTTTAAATAGAAATGCAAGACAAGGTAAAAATCCAGATGATGTGAAAATTGGGGATTT +AAAATAACTGGATGTTAAATTTTACGTTTATGAATGCCTTTTAATGTGTACATTAAAATAAATGAGTTGGTTTTTACTAT +TTGATAAAACAATACTCAGGTACATTCAAAATCTTTTAAATAAAAAGGATGGACATAGATGAAAATTAGAGTCGTCATTC +CTTGTTTTAATGAAGGGGAAGTCATTACACAAACACATCAACAATTAACTGAAATACTTTCACAAGATAGTAGTGTGAAA +GGCTATGATTATAATATGCTTTTCATAGATGATGGTAGTACGGATACCACTATAGATGAAATGCAACATCTTGCCACAAT +AGATAGGCATGTCAGCTTTATTTCTTTTAGTAGAAATTTTGGAAAAGAAGCAGCTATGATTGCAGGTTACCAGCATAGTA +CTGAATTTGATGCAGTCATCATGATAGATTGTGATTTGCAACATCCACCTGAATATATTCCGAAAATGGTTGAAGGTTTT +ATGGAAGGCTATGATCAAGTGATTGCAAAGCGTGATAGAAGTGGTGAAAATTTTAGTCGCAAAACATTAAGCCATTTGTA +TTATAAGTTAGTTAATTGCTTTGTAGAAGAAGTACAATTTGATGATGGTGTTGGTGATTTTAGACTTTTAAGCCAAAGAG +CTGTTAAATCCATTGCATCACTTGAAGAATATAATCGATTTTCAAAAGGGTTATTTGAATGGATAGGCTATAATACTAAA +GTGTTTACGTATCAAAATGTTGAGAGACAAAAAGGGGAATCTAAGTGGTCCTTTAAAAAGTTATTTAATTATGGTATTGA +TGGATTGATTTCCTTTAATAGTAAACCTTTGAGAATGATGATTTATCTTGGCTTGTTTATCTTTTCAATAAGCGTGTTAT +ATATTATCTATTTATTCATCAATATTATGATATCTGGTGTTAATATTCCGGGATATTTTTCAACGATTGCAGCTATTTTA +TTATTAGGCGGCATACAGTTAATTTCAATTGGTGTTGTAGGTGAATATATTGGCAGGATATATTATGAAGTTAAGGCACG +TCCTAAATATATTATTCAAGCTACAAATCTTTCAAGTCTTGAAAATGATGAGAAGGATACCCATAAAGTTTATTCTAAAT +AAACAAAAAAAGAAGCCCTCATTAATGGGAGCTTCTTTTTAGTCTTTGCATTTTATTTTATAAATAAATCGGATTATGAC +GTAATGTCTAATTTGTGTAATGTTACAGTCATCGTAGTTCCTACATCTATATCACTGCTTACACTGATTTTTGCGTTATT +TTGTTGCGCGAGTTCATTAGCTATATATAAGCCTAATCCAGAACCACCCGTTTTTGTATTACGAGAGTTTTCTACTCTGA +ATGTACGTTCGAATATACGTTCTTGTAGTTCTGGTATAATGCCAATACCTTCATCGCTAATAGCAATGTCGATAGTATCT +TGATCTTCGTTTTCACTAATATTAATATCAATGCGACTACCAACATTTGAAAATTTTAGCGCATTATCAAGTAAGTTTGT +TAAAATACGCTCAAGTGGCGTTCGATATTGATAAAATGCATCAATTTCGTTACAGAAATTCACTTCTAATGTGCGGTTTT +CATGTTTGATACGTTGCTCATATGGTTGCAATATTGATACAAGTAATTGGTCTAGTTGTATTAATTCTGGGGGATATGTT +TTACCTGTATTTAAAGTGATAATATGAGTCATATCATCAAATAATGTTGATAATCTGTTTGCTTGTTTAATTAATATGTC +GTATGACTCTTTAATCTCATGATCCTTAGTGATTATACCATCACGTAGTCCTTCAGAATATGAAATAATGCTTGCTAAAG +GTGTTTTTAAATCATGGGCTAAGTTTTGAATCAGTTCTGTTTTTTCTTGTTGTTCGGATTTAATTTGATTCATTTGTTGC +GTAATTTCAGAAGCCATTTTATTAAAAGATTGATTTAATTCATAAATTTCTTTTGGTGAATTAAACGTTTTATCATTGCT +TGCGTAATTTCCGTTAGCAAATTGCTTAGTTTTTATATTAAACTGCTTAATTTTTTGTATAAGTGGATTAATAAAAATAC +TACATATTAATAAGGTTAAACAGCTTGTAATTATTGTCGTTAAGGTCAAAGTTAGTGTCATATGGCCGTTAAACCACATT +AAAATATATGCAATTGCTAAAATAGTTGAAGTTAATAGTATACTCGATACGACGCCAATAATGATTTGACTTCTAATTGA +TAACACCATTATCGGCTCCTTTCAAATTTATATCCTAATCCCCATACAGTTGTGATGGTATATGTTGTAAAGCTCTCTTT +TTCTAATTTTTCTCTAATACGGTGTATATGGACATTCACGGTATTAGCATCTTCGTAATAGTCATATCCCCAAACTTTTT +CAAGTAATTCTGATTTAGAAATAACTTCATTTTCTCTAGAAGCTAAATACCACAATAACTCAAATTCCTTAATACGCATA +GGGACTTCGTGACCATTTACAGTCACAACTTTACTTAAGTTAATAAGTGTTAATTCATCAAACGACAGTTGTTCAACTGG +TTGATGATGGTATTTCTTCATTCTTGTAAGTAAATTATTAATACGTAAAACGAGTTCCCTTGGACTAAATGGTTTTTTGA +CATAGTCATCTGCACCTAAAGTTAAGGCGTAAATGGTATCATGTTCTTGTGTTTTGGCAGTTAAATAGATAAAGGGGATA +TCTAATTTTTGCCTTTTCATTTCTTTGACAATGTCGTAACCATTAACTTCTGGCATCATGATATCAAGTACCATGATATC +AATATCATTTGATAGTAAAGAAATTGCTTCTTTACCGCTAGTTGTCGTTGTTACTTTGTAACCTTCATATTCAAAATAGG +TTTGACAAATGTCTACAATGTCTTGTTCATCATCCACGATCAGTAAGTGGGTCATCTATTTTTTCACCTCTGTTCTTACG +ACCTCTAAAGTAATTAATGATTTCTTTAAGTGAAATCTGTTTTAACAATGAGTGACTCATTAGTAAAAGGATAAAGAAAG +TTAATTGAAGAGGATACGTAAATATCATATCTGCTAAGATATAATTTATCATAACAAAGGCTCCAAAGAAACTAGCAGCA +TATGCAAAAACTCCAAAAATTAAACCTAATCCAATTGCAATCTCTCCGAGTGGGACAACAATATCAAATAATGACGTCGT +ATGTGCAACTATATTTGCGAAAAACCACTTATACCACTCTGGTGAATCAGTATTGTTAGCGATGACTGGTACTAAACCTT +TCAGCGTAAATCCGCCCGTTAATTTTTCGTAGCCTTGCATTAACATAACAATACCTGAACCCACACGAATGATAAATGTA +ACGAGTAGCAATAATTTATTCATGATGTTCACATTCTTTCTATTTATTGTGTGTAATTTATATAAACATAAGATTAAGCA +ACATAATGCGATTTGTAGTGTTATGTGAACAGGAAGTGTTTGAGCTCATGGGTTGCTTAAGCTAAACAAACCTCAAGCTC +TAAAAAAATTTACTTATGTGCTCTGCAATCTTATGGGATTTAATAGTTGTATATATATTGAAAAAAGGAAAGTATGATTT +CTACAAGCTAGGGGGGCTGTGAAATCATACGCTTTCCATTGAGTAATGAGTGTAATGTATATTTTAAAAATTTGTAATGT +ATAGTAATAGTATGTTGTAAATAAAAGTTTAACTCAGAAATTGATTATTTTAATTTAGCGCCGCCGAAGATGACGTGAGC +TTTTTTACAATAGATTGTTACTTCATCATATTTGCTTAAATCTACATTTTTAAGATCAAATGTTTGTTTTTCTTTATCGT +AGTCAACCATTGCGATTTCTTTACCGTTTTTAATGTCGCCATTTTTTGTTAGGTAGACGTATAAATCTGGACCTTTTGAT +GATTTGTAGTTAGTAAGCATTAATTTACCATTTTTAATCTCAGCTTTACCTTCAACAGTTTCACCGTTTTTAGAACTGAA +TGTACCTGTTAGGTGTTTTGTTTTATCAGTTTTAACATTGCTATCTTCTGACTTTGTTTTTTGTTCAGTTTTGTTACCTT +GATCTTGTGAATTAGAATTACCACAAGCACCTAAAGCTAATGTTGTGATAACAGCACCAGCTGCTAAAAAATATTTTGTA +TTCATGCTAACTCCTCATTTCTTCAATTTGATAAGTTAAGTTTAAAATGAAGGCAAATAATATGCCATTAACTAATTCTT +AACTTCGTTTAAATATCGCTTAACTAATATTGAAGAAAGAATAATTTGAAAATGAAAATAATTGTCTGATTTAAATAAAT +AGGGGTCAAAATAAGAGGAGGGCGTTTTTTTAAGTAAGGAAATATTTATCTGAACTAGATAACACTATAAAACACTTCTG +ATATTTTGAACTAAGAATATCAGAAGTGTTTAATAGTTTTGGTATTAAAATTTATAAACCTTTTGCCACAATAAAAACAA +CAAGAAAATAAGATAGACGATTAAATACAAGTACCAATATGGTGTTAAAACCATCATAACGATAAATGAAATGTTAATAA +TGATAAGTCCTTTAATCATTAGTTTCTTTATTTTGTTATTATTTTCAGCAGTTTCATCTAAGCTATTAAATGTTGAAATA +AACAACATAATACCACCAATGATGAAAATGGCAGGGGCTAAAAATGGAATAGACATATATATTGATTGTGTACTTGAAAA +GTCGGTAACACGTTGCGTTAAACTGACAACAATCGTTAATATGATAGCAATAAATTTTTGGTCTGTAATGTATACAGGTT +TTAATTTTTTATCAATGTAAAACTGTAGTATCGTCCAAATGATAAACATCGTTATAGTACACAGAGTAATAAAGTTATAT +TTATCAATTATGAATGTATTTATAAAGGCGTTTATCGTAATAGATAACAACATTGCACTTAATGATAAATGTTGCATTTG +ATGCCTATATAAATTGAATCCAGTTATAAGTACGACGATAGCAATATTAATTAATATATATACAATCATGATGGACTCCT +GTTCGTTATACTTCAATCAACTATTATATCAATTAATATATTGTTATTCATTAGATATGACTTGCAAAAAGTAAAATAAG +TTATCCTTTATACACCTTTTTTATTGCTCCAAAGTAATGTATGAAGTTGTGGTAACACATAAACGTGATTCATATCATTA +CTTTGCATAACTAAATCCACCAACTGCTCGTAGCGTTCTAACAACTTTTCGGTATGATTATCTACGCTGTCTGATAAATA +TGGGTTACCAACTTGTAAATAGAAGGGAATATCTGGATAACGGTGGTGTATCATTTTGGCAAAATCATAATCTTTATCGT +CGAATACAACTACTTTTAAGTTTAATGATGAAGGTACGCATTGTGTAATCACTTCATCTAACTTTTTTAAATCAGGTGTC +ATAGTTGAACTTGGTGGTTTTGGACTAATCGTTAAATCATCAATTTGTGTCATCCAAGGTTGGAATTTACTGCCTTGTGT +CTCCAGTGCGCTGAAAATACCTTTATCTTGAAATAAGTCAACTAACTCTTGGATACCTTTAATTAATGCTGGGTTACCAC +CAGAAATTGTAACGTGGTTAAATAAATCGCCACCAATTCGTTTTAATTCATCATAAATTTCTTCAGCGGTCATGAGTTTT +ATATCGCCTTTAGCACTACCATCCCAAGTAAATGCAGAATCACACCAGCTACAGCGATAATCACATCCAGCTGTTCTCAC +AAACATCGTTTTTCTACCGATTACTCGACCTTCACCCTGAATGGTTGGACCGAATATTTCGAGTACAGGAATTTTAGCCA +TTAGTTACACCTGTTCCTTTGGTCTAAATACGACATAACTTGTTGGTGTTTCTCTTACAAATACTTGAATACATTTTGGT +TGGTGTTCGAGCGATGCCAAATTTTCTTTAACAATTTGATAAATTGTTTCCGCTACGATTTCAGTTGAAGGGATTTTGTT +TTTAAAAGCAGGTAAGTTATTTAACAGTTGATGGTCAAATTTACCGTGTATCATCTTTTTCAAATGGCTAAAGTTCACTA +AGAAGCCAGTGTCATCTAGTTTATCACCGACAATTGTTAAATTAACAAAGTAAGTATGACCATGGACATTTTGACAAATA +CCTGCTTCTTCACATGGAATGTGATGTGCAGCCGAAAAATTAAAATCTTTATTTAATTCGAATTGATATGGATGCGTTGT +ACTAGGATAGATTTGTTGTAACATTTTAAAGCGCTCCTTTACTTTCAAGATATTGATTTAGTCCACGTTGACGTAAATGA +CAAGCTGGACATTCACCACAGCCATCCCCAATGATACCGTTATAGCATGTTAATGTTTTTGTACGAATATAATCTAAAAC +TTCGAGTTCATCACTTAATTTCCACGTTTCTGCTTTGTTTAACCACATTAAAGGAGTATGAATGACAAAATCTTTGTCCA +TAGCTAGGCTTAATGTTACGTTCATTGATTTTATAAAACTATCGCGACAGTCTGGGTAGCCTGAAAAGTCTGTTTCACAT +ACGCCTGTAATAATATGCTTAGCCCCAATTTGATAAGCTAGAGCGCCTGCAAACGACAAGAAAAGTAAATTTCTAGCTGG +AACAAATGTATTAGGTATACCATCTTCATTATTAGTAATTTCCATATCATGTTGTGTTAATGCGTTTGGAGTAAGTTGTG +ATAATAATGACATATCTAAAACGTGATGTTTCATTCCTTGATCTTGTGCAATTTGTTTTGCGACTTCAATTTCAGTATCA +TGTCTTTGGCCATAATTAAACGTTACGAGTTCAACTTCTTTGAAATGTTTTTTTGCATAAAAGAGACATGTTGTACTGTC +TTGACCACCACTAAAGACAACGATGGCTTTTTCATTATTTAATACACTTTCCATTTTGTAATTGCTCCTATCATTAATAA +TATTAATAAAGAGGTTAATGGCATTGATAAGCCCGTTTTTAATTTATAAAATAAAAAAAGCCTATCTCCATAAAAGATAG +ACGAAAGAAATGGGTTGCTCCTATAATATATATTAAAGGTTTACCAACGAATGTTTGAAATTTCAATGTCTAGTTTTTTA +TAGAGGGTTTCAGCTAGGAACCTCTGATATTCATTTATGTACGATGAATAGAATATACCATATATTAGTTAAAATCAAGG +TCAGGATAATGACAATGCTTTTAAAGTTTTAGCTACATCCAATACATATTATAAAAATAAAACACATTTAGAACGGAGTA +TACAATGATTCTAGTCATAGATAATAATGATTCATTTACATATAATTTAATAGACTATATTAAGACTCAAACGAAACTAA +CAGTTCAAGTTGTTGGTATTGATAATCTGCTGATAGAAGACGTCATTAATATGAAGCCAAAAGCAATTGTTATTTCGCCT +GGGCCGGGTAATCCGGATGATTATCCTATCTTGAATGAAGTGTTAGAACAATTTTATCAGCGTGTACCTATACTAGGTGT +ATGTTTAGGATTTCAATGTATCGTGTCTTATTTTGGTGGAAATATCATTCACGGCTATCATCCTGTACACGGACATACTA +CACAGTTACGCCATACCAATGAAGGTATTTTTCAAGGACTGCCTCAAAATTTCAATGTAATGCGTTATCATTCATTAATT +GCTGACGGAGCGACTTTTCCAAATTGCTTAAAGATTACAGCAAAAAACGATGAAGCGATTATTATGGCATTTGAGCATAT +TAGATTTCCGGTTTTTGGTGTGCAATATCATCCTGAATCTATTTTGAGTGAATACGGTTATCGACAAGTTGAATTATTTT +TATCGAAGGTAGGTGATTACTGTGAGAATAGAATATAATTATCGCTACTATTTAACTGAAAATGAATATAAGCAATACCA +TATTCAATTAAAGGGATTTATAAAGAAGTATGTTGCTACTAAGTTGGCTGATGTGGGAGAAGTGATACACTTTGCACAAG +CGCAGCAACGACAAGGTAGATATGTCTCGTTATATTTAAGTTACGAAGCGGCAAAGTATTTTAATCATGCTATGTGTACA +CATTCATTAGCTAAAGATGATATTTATGCAGCAGCTTATAGTTTTGAAAAAGCGGAAAGCATAAATTCAACATATGAACA +TCAAACTTCTTATGTATCAAAGTATCATTTTTCATTTGTTGAATCTTCTGAGGTTATGATGACTAATATTAAACGTGTCC +AACAAGCAATTGTTGAAGGCGAAACGTATCAAGTGAACTATACGGCGCGCTTAACGGATAACATTTATTATCCTATTAGT +ACTTTATATGAACGATTAACTCAATTTAGTAATGGTAATTATACTGCGTTATTACAAACTGATGAAATCCAAGTAGCGTC +TATCTCACCAGAATTATTTTTTCAAAAAGGACAATTTAACAATGTCGATAACGTTATCATAAGCAAACCGATGAAAGGGA +CAATGCCTAGAGGTAAAACGGAAGCTGAAGATCAACAGTATTATAAAACATTGCAAACTTCTTCGAAAGATCGTGCAGAA +AATGTCATGATTGTTGATTTACTAAGAAACGATATAGGGAGAATATCACAGAGTGGCTCAATTAAGGTGTATAAACTATT +TTTTATTGAGGCATATAAAACTGTATTTCAAATGACTTCGATGGTAAGTGGAACTTTAAAAAATAATACAGACTTAACTC +AAATTTTAACATCGTTATTTCCTTGTGGTTCGATTACAGGTGCACCGAAACTGAATACAATGAAATATATTAAACAATTA +GAAAGTTCACCTCGTGGTATATACTGCGGAGCAATTGGACTATTACTTCCAACTGAAGATGATAAAATGATTTTTAATAT +TCCGATTCGCACTATTGAGTATAAATATGGACAAGCGATTTATGGAGTCGGAGCAGGTATTACAATTGATTCTAAGCCAA +AAGATGAAGTGAATGAATTTTACGCAAAAACCAAGATTTTGGAGATGTTATAATGCAATTATTTGAAACAATGAAAATTG +ATAATGGACATATCCCTAGACTTACTTATCATACTAATCGCATAAAATGTTCTTCTGAGCGATTAAACTTTAAATTTGAT +GAACATGCATGGCGAAATGAATTAAACGATGTAACAACAAAGTATCACAGTGGTCAATATAGACTTAAAATCGTATTAAA +TGCTGAAAGCAAATTTGAAACGATAGTGTCACCTTTACCTGAGAAAAGTAGTTTTACAGCAAAATTTCAAGTGTTGCCCA +AAGTAGTTAATCCAACTTTTATAAAAAATAAAACGACAGAACGAAAGCATTTAGCACACAATCATGAAACAGATTTAATA +TTGCTAACTTCAGAGGACGGCAAGGTCCTTGAATTTGATATTGGCAACATTGTCATTGAAGAGGATGGAAAATGGTACAC +ACCAAGTTATAAAGATGATTTCTTAAAAGGATGCATGCGTGATTATTTAATAGATAGTGACAAACTTGTTGAAAAAGACT +TTAATAAAAACGAATTGATTTATAAATATCATAACAATGAGATACGTTTATTTTTGATAAATAGTTTACGAGAGGTTGCC +GATGTCCACCTTTGCCTTTAAAATTAGTATGATAATAAAGTAGAAAATGATGTGAAAACAAATTTAAAAATTAAATGGGG +CCAAATATGATTATTGTTTATATTGTGCTGTTGTTAATTCTTGTATACGTAAATTATCGATTAGTGAATCGATTGCTATC +TGAAAATAGAATATATGTTGTTCGTTTGATAGCAACAATTACTACTGTTATAAGCTTTATCCTTGTATACGCATTAATTC +ACGAACTTATGCCTTTTGTTGTGCGGGCAATGGATTTAATGTACCACCAGTAAATTTAGTGAGTAGGTAAAATGAACTAT +TCAATCGATTCTATAATTAAAAATGAAAGTAAAGTAACCAAATATTGAAGAGGTGTACAAATGAAAATATATAGTCAAGG +TGACCAAGCCATTGTAGTCGCAATTGAAAAAGAAGTATCTAAAAGTTTAACCGAAGATTTATTAACACTTCGCTCATATT +TAATTGAACAAAATTATCCATTTATTATAGAAATTGTGCCATCAGAATCAGACATGATGATTGTCTATGACGCAAGGGAT +ATGATTAAACACCATAATATACAATCACCTTTTTTATACATGAAAGCACTAATAGAATCGATTCATTTAAACATAAAACA +TGATTTTAACCAGCAAGATTTGATTGAAATACCAATTGTGTATGGTTCGAAATATGGTCCGGATTTAGAATCACTTTTAA +AACATTACAAAATCAAGCTAGAAACTTTTATTGAATTACATTCTAAGGCGCAATATTTTGTTTCGATGATGGGATATTCA +CCTGGGTTTCCTTATTTAACTGGATTAAATAAGAAATTGTATATTAATCACACGAGTAAACAGAAAAAATTCATTCCAGC +TGGTTCTGTAGTACTTGAAGGGAAAAAATGCGGTATTGTAACTACGGATACAATTAATGATTGGTTAGTTATTGGTTATA +CACCATTATCACTTTTTAATCCGAAAGAATCAGATTTCGCACGCTTAAAGTTAGGCGATAATATTAAATTTAGACCTATC +AATGAAAATGAATTAGAAGTAGGAGCGTTTAAAGATGTCAATCATAATTGAAAAAAGTGGCTTATTCAGTAGCTTTCAGG +ACTTTGGCAGAAGGGGATATGAACATGATGGTGTAATTCCATGTGGTGCACTTGATACTTTAGCACATGAAATTGCTAAT +CGATTAGTTGCAAATGACAAGAATGAAGCAACTTTGGAAATGACTAATAAAATGGCAACGATTCGTTTTACAGAACCTAC +GCTGATTGCATTAGCAGGGGGTAATGTCAAAGCTTACACTGAGCATATGACTATATCTCCATATAAATTGTATTTGTTAG +ATAAAGGCGATGTTTTAAAGTTTAGAGAAACAAGTTATACATCGCGAGTGTATTTAGCTGTGGGAGGCGGATTTGAATTA +GATGCATGGTTAGGATCTAACTCAACCGACTTTAATGTAAAAATTGGTGGTTTTAAAGGTAGAACATTACAAGATGGCGA +TGAAATAAAGCTTAAGAGAGATTATACAGCTCGTCATCATAAGTTATTTGAAAACCTTGCTCACACGAAACAAACAGATT +GGGGTATTGATGGATACGCCTTGTCATTTAATTATATGTCTGATGTATTTCATGTCGTTAAAAATAAAGGTACGGAAGAT +TTTAAAGAAGATGCCATTCAAAGATTTGTGAAACATGATTATAAAGTAACGAGCAAAGCAAATCGCATGGGGATGATGCT +TGAAGGTGAAAAAATCAAAGCTTTTTATGAAGATATGCCACCGTATCAGACTGTCAAAAAAGGAACGATACAAATTAAGC +GTGATGGCACACCTATTATCCTATTAAATGATCATTATACGCTAGGTAGCTACCCGCAAATCGGTACAATCGCAAGTTAT +CATTTAACGAAATTAGCACAAAAACCGCAAGGATCACGTTTGAAATTTCAATTTATAGATATTTTAACGGCTGAAAAGAA +CCTTGTTAAGTATAGTAACTGGTTAAACCAATTATTCCATGGAATAGAATATAGAATGCAATTAGAAATGATGAAATAAT +ATTTGGTACGTAGCTCATACTCGAGTCCGATGCAAATAATTTCCTCCTAATGTATAATGAAATAATACTGTGTTTTATCT +GCGAAATGTATCATTTTCTAATTCGTTTCACAGTAAAATGAAAAGATAAAGTGTGTTTTTACTTGAATTTTGACTAAAAT +TACTCTATATTTATTAATTGAGCTATGCTTATTATTACAATTTGATTACAAATTTTAAATTTGTTAATTGAATGATAATA +TTAAATAAAGAAACTTACACAAGCAAATATGAGTTGTAGCCCAAAATACTTGTTAAATCAAAGTTGAAAGCTACAAATAA +TGAAAATTATAAACTTGAATCTGAAAGTAATTACTATAATTATGACAATGTTAACTTTTAAACGCACTTATTAATTAACT +ACATAATGTTAATATCTAATTTATTCAAGTACTTTCGCAAGATTTATTATCTAAATAACGGGGGAAAGAATCATGAGTTC +ACAAAAAAAGAAAATTAGTCTTTTTGCGTTCTTCTTATTAACCGTAATAACGATTACCTTGAAGACGTATTTTTCTTATT +ATGTTGATTTTTCTTTAGGTGTTAAAGGTTTAGTACAAAACTTAATATTATTGATGAATCCTTATAGTTTAGTAGCACTG +GTTTTAAGTGTGTTCCTATTCTTTAAAGGCAAAAAAGCATTTTGGTTCATGTTCATAGGCGGCTTCTTATTGACGTTCCT +ATTATATGCCAATGTTGTGTACTTTAGATTCTTCTCTGATTTTTTAACGTTTAGTACTTTAAACCAAGTAGGTAACGTAG +AATCTATGGGTGGTGCGGTTAGTGCATCATTCAAATGGTATGACTTTGTTTATTTCATTGATACGTTAGTTTACTTATTC +ATTTTAATATTTAAAACAAAATGGTTAGACACAAAAGCATTTAGTAAGAAATTTGTTCCTGTCGTAATGGCGGCTTCAGT +AGCATTATTCTTCTTAAACTTAGCTTTTGCTGAAACTGACAGACCAGAATTATTAACACGTACATTTGACCATAAATATT +TAGTGAAATATTTAGGACCTTATAACTTTACAGTATACGATGGTGTTAAAACTATCGAAAATAATCAACAAAAAGCGCTA +GCATCTGAAGATGACTTAACAAAAGTATTAAATTATACGAAACAACGTCAAACAGAGCCTAACCCAGAATATTATGGGGT +GGCAAAGAAGAAAAATATTATTAAGATTCATTTAGAAAGTTTCCAAACCTTCTTAATTAATAAAAAGGTTAATGGTAAAG +AAGTAACACCGTTTTTAAACAAATTATCAAGTGGGAAAGAGCAATTCACATACTTCCCTAACTTTTTCCATCAAACAGGT +CAAGGTAAAACATCTGACTCTGAATTTACAATGGATAACAGTTTATACGGTTTACCGCAAGGTTCTGCCTTTTCATTAAA +AGGAGATAATACGTATCAGTCATTACCAGCAATTTTAGATCAAAAGCAAGGCTACAAATCTGATGTCATGCACGGTGACT +ATAAAACATTCTGGAACAGAGACCAAGTATATAAACACTTTGGTATCGATAAATTCTATGATGCAACATACTATGACATG +TCAGATAAAAACGTTGTAAACTTAGGCTTGAAAGACAAAATTTTCTTTAAAGATTCTGCTAATTATCAAGCTAAGATGAA +ATCACCATTCTATTCTCATTTAATTACATTGACTAACCACTATCCATTCACATTAGATGAAAAGGATGCAACTATTGAGA +AATCAAACACAGGTGATGCAACAGTTGATGGTTATATTCAAACAGCACGTTATTTAGACGAAGCATTAGAAGAATATATT +AATGACTTGAAGAAAAAAGGATTATATGACAATTCAGTGATTATGATTTATGGTGACCACTATGGTATCTCTGAAAACCA +TAACAATGCCATGGAAAAACTATTAGGTGAAAAAATCACACCGGCTAAATTTACAGATTTAAACAGAACTGGTTTCTGGA +TTAAAATCCCTGGTAAATCTGGTGGTATCAATAATGAATATGCTGGTCAAGTCGATGTAATGCCAACAATTTTACATTTG +GCTGGTATAGATACGAAGAACTATTTAATGTTCGGTACTGATTTATTCTCTAAAGGTCATAATCAAGTAGTTCCATTCAG +AAATGGTGACTTTATAACAAAAGATTATAAATATGTTAATGGTAAGATTTATTCTAATAAAAATAATGAACTCATAACTA +CTCAACCAGCTGATTTCGAAAAGAATAAAAAGCAAGTTGAAAAGGATCTCGAAATGAGTGACAACGTGCTTAATGGTGAT +TTGTTTAGATTCTACAAAAATCCAGACTTCAAAAAGGTAAATCCTTCGAAGTATAAATATGAAACAGGACCTAAAGCAAA +CTCTAAAAAATAATATCTAAACACGAACTCGGATTGATAAAATATCAATCCGGGTTTTTATTATGTTCTTTTATATTATG +TTTTACATTATATGTGTTGATAAAAAGGTATATTATAAAGATCATTAGCATAGTCATCAAAGCCTTAACAAGTTTTATTT +ATAGTTGAAGCAACAATATATTAAATTATGACACTTAACGGTATCGATTTCTTTTGATTTCAAAAAGGTGTATAATGTAT +AGAACTGATTTAAGTACGTAGAGATTTCAACAGAAGAAGGAAGATGGGTATGGAAGCATATAAAATTGAACATTTAAATA +AATCTTATGCCGATAAGACTATATTCGATAACCTAGATTTATCAATTTCAGAAGGTGAAAAAATAGGTTTAGTAGGCATA +AATGGTACAGGGAAAAGTACGTTGTTAAAAGTAATTGGTGGTATTGATGATGATTTTACAGCCAATGTTATGCATCCAAA +TCAATATCGAATTCGATATTCGTCTCAGAAACAGGACCTTAATGAAGATATGACAGTTTTTGATGCAGTATTAAGTTCTG +ATACAACAACTTTACGCATCATCAAGCAATATGAGCAGGCAGTACAAGCTTATGCGGATGACCAAAGTGATAAATTGTTC +AAGCGAATGATGGATGCGCAAGATGCTATGGATCAACATGATGCTTGGGACTATAACGCTGAAATTAAAACAATCCTCTC +AAAACTAGGTATACATGATACTACTAAATACATTAAAGAATTATCCGGCGGACAACAAAAACGTGTTGTACTTGCTAAAA +CATTAATAGAACAACCAGATTTATTGTTATTAGATGAACCTACGAACCATTTAGACTTCGAATCAATCAGCTGGTTGATC +AATTATGTGAAGCAATATCCTCATACTGTTTTATTCGTAACCCATGATCGATATTTTTTAAATGAAGTTTCCACTAGAAT +TATTGAACTAAACAGAGGTAAGTTAGCGTCATATCCTGGTAACTATGAATCTTATATTGAAATGCGCGCTGAAAGAGAAG +TAACACTTCAAAAGCAACAACAAAAGCAACGAGCTTTATATAAGGAAGAACTTGCTTGGATGAGGGCTGGAGCTAAGGCT +CGTACTACAAAGCAACAAGCTAGAATTAATCGATTTAATGACCTAGAAAATGAAGTTAACCAGCAATATAAAGACGATAA +AGGTGAATTGAATCTTGCTTATTCAAGATTAGGTAAGCAAGTGTTCGAATTAGAAGACTTATCAAAGGCTATTAATGATA +AAGTATTATTTGAACATCTGACGGAAATTATTCAAAAAGGTGAGCGTATTGGTGTTGTTGGGCCAAATGGAGCTGGTAAA +ACAACACTCTTAAATATTTTGAGTGGAGAAGACCAACAATTCGAAGGTAAATTGAAGACTGGGCAGACGGTTAAAGTAGC +TTATTTTAAGCAAACAGATGAGACCCTGGATAGAGATATTCGTATGATTGATTATTTAAGAGAAGAAAGTGAGATCGCAA +AAGAAAAAGATGGAACCTCGGTATCTATTACACAACTTCTTGAACGATTTTTATTTCCAAGTGCAACTCATGGTAAAAAA +GTTTATAAATTATCTGGTGGAGAGCAAAAGCGTTTGTATTTATTACGTCTACTCGTACACCAGCCAAATGTTCTGTTGTT +AGATGAACCGACAAATGATTTAGATACTGAGACTTTAACAATACTTGAAGATTATATTCATACTTTCGGTGGTACAGTGA +TTACCGTAAGCCATGATCGCTACTTCTTAAATAAAGTTGCACAGTCATATTGGTTTATTCATGATGGTCAGATGGAAAAG +ATTATCGGAACTTTTGAAGATTATGAAAGTTATAAAAAATCATTAGATAAAAATAAATCCACATTGAAGCAACAATCTAA +ATCTTCTACAACTGTACGTAAGAAAAATGGTTTATCATATAAAGAAAAATTAGAATATGAACAATTGATGAAACGCATAG +AACAAGCGGAAGTAAGAATGGAAGAAATTGATGTGCTCATGATTGAGGCAAGTGCAGATTATGGGAAAATTAAAGAATTA +AACGAAGAAAAAGAACAACTTGAAATTCAATATGATTTAGACATCACAAGATGGAGTGAGTTAGAAGAAATTAAAGAACA +ACAATAAGGGGTCAATTTATGATGCAACAAACATTATCGCATTACTTTGGGTATGAAACGTTTCGACCAGGACAAGAAGA +AATTATTAGCAAAGTATTAGACCATCGTAATGTGCTTGGTGTCTTACCAACTGGTGGAGGTAAGTCTATATGCTATCAAG +TACCAGGTTTATTGTTAGGTGGTACAACAATTGTAATAAGTCCACTAATATCATTAATGAAAGATCAAGTGGATCAATTA +AAAGCGATGGGAATTCAAGCTGCTTTTTTAAATAGTAGTTTGACTCAAAAAGAGCAACAACGTATTGAAAAAGCATTATC +AAATGGAGAAATTCAATTTTTGTATGTTGCACCAGAACGATTTGAAAACCGATATTTTTTAAATATGCTTCAGCGTATAA +AGATTCACTTAGTCGCGTTTGATGAAGCGCATTGTATTTCTAAATGGGGTCATGATTTCAGGCCGAGTTACCAAAATGTT +ATTTCAAAAGTATTTACGTTACCTCAAGATTTTACAATAATAGCGTTGACAGCAACTGCCACGGTTGAAGTACAGCAAGA +TATTAGAGAAAAGTTAAATATCGCTCAAACTGATCAAATTAAAACGAGTACTAAGCGTAGAAACTTAATTTTTAAAGTAA +ATCCTACTTATCAACGTCAAAAATTTATATTGGATTATATTAAAACACACGATGAAGATGCAGGTATTATTTATTGTTCT +ACACGTAAGCAAGTTGAAGAGCTTCAAGAAGCCTTAGAAAGTCAGAAAATTGAAAGTGTTATATATCATGCAGGTTTGAG +CAATAAAGAAAGAGAAGAAGCGCAGAATGATTTCTTATTTGATCGTGTTAAAGTAGTCGTTGCTACAAATGCTTTTGGTA +TGGGTATTGATAAATCCAATGTACGCTTTGTTATTCATTATAATATGCCTGGAGATTTAGAATCTTATTATCAAGAAGCG +GGTCGTGCAGGTCGTGACGGGTTGAAAAGTGAATGTATTTTGTTATTTAGCGAACGCGATATCAATTTACACGAGTATTT +TATAACAGTCTCTCAAGCTGATGATGACTATAAAGATAAAATGGGCGAAAAGTTAACTAAAATGATTCAATATACAAAAA +CAAAAAAATGTCTAGAAGCAACAATTGTCCATTATTTTGAACCGAATGAAAAATTAGAAGAATGTGAACAATGTAGTAAT +TGTGTTCAACAAGATAAATCATATAATATGACACAAGAAGCTAAGATGATTATTAGTTGCATCGCTCGTATGAAACAACA +AGAGAGTTATAGTGTTATCATTCAAGTGTTAAGAGGAGAGTCAACAGATTATATTAAGTATAAAGGTTATGACCAAATTT +CAACCCATGGTTTAATGAAAGGTTACACAACATCAGAATTAAGTCACTTAATAGATGAATTAAGATTCAAAGGGTTCTTA +AATGAAAATGACGAAATATTAATGTGTGATACTTCAATTAAAAAATTACTCAGTAATGAAGTAGAAGTATTCACAACACC +ATTTAAGCAAAAAGCGACTGAAAAAGTATTTATAAATACGGTTGAAGGGGTTGACCGAGTATTATTCAGTCAGTTGGTAG +AAGTTCGTAAAAAGTTAAGTGACAAATTAACGATAGCACCTGTAAGTATATTTTCTGATTACACGTTGGAGGAATTTGCT +AAACGTAAGCCTGCTTCGAAACAAGATATGATTAATATTGATGGCGTAGGTAGTTACAAATTAAAACATTATTGTCCAGC +ATTTTTAGAAACGATTCAAAATTATAAAGCCAAAGTATAGTGAAGACACCTAGTAAGCATTCCTTGTTTTACTGGGTGTT +TTTTTGAATTCAAGGTAAAATAACACTATTTCTTAACTGGAGATTACCTATTATTCATTATTTTATTAAAAAAGTTCAAG +GATAAATCATTTAAATTTGAAAATTGTAATAAACGTGGTTAAATAGGGAATACATACATATAATCTCGGTTTTATGCTAT +GAGAAAAAAAGAGGTGGCAAAGTGATTAAGTTTAAAAATGTAACTAAGCGTTATGGCAAACATGTTGCTGTCGATAACAT +TAGTTTCAATATTAATGAGGGTGAATTTTTTGTGCTAATTGGACCTTCAGGTTGTGGAAAAACTACGACATTAAAAATGA +TTAATCGACTCATTCACTTAAGTGAAGGTTATATTTATTTTAAAGATAAACCAATAAGTGATTATCCAGTATACGAAATG +CGTTGGGATATTGGATACGTATTGCAGCAGATTGCATTATTCCCACATATGACAATCAAAGAAAATATTGCACAAGTGCC +ACAAATGAAAAAGTGGAAAGAAAAAGATATAGATAAAAGAGTAGATGAATTACTTGAAATGGTTGGATTAGAACCTGAAA +AATATAAAAACAGAAAACCTGATGAATTGTCAGGGGGGCAACGACAACGTGTAGGAGTTATACGTGCGTTAGCAGCTGAT +CCACCAGTTATTTTAATGGATGAACCGTTTAGTGCATTAGACCCAATCAGCCGAGAAAAACTTCAAGATGATTTAATTGA +ATTACAAACTAAAATTAAGAAGACAATCATATTTGTTACACATGATATTCAAGAGGCGATGAAACTTGGTGATAAGATTT +GTCTTTTGAATGAAGGGCATATTGAACAAATTGACACACCAGAAGGATTTAAAAATAATCCTCAAAGTGAATTTGTTAAA +CAATTTATGGGTAGTCATTTAGAAGATGATGCGCCATGTGTTGAAGAGAACGCAATTATCCGTGACTTGGATATTATGAA +ACCAATCGATGAGGTTACATCTATGAGCGCTTATCCAATTGTTTATGACAATCAACCAATTGAAGTATTGTATCAACTTT +TATCAGAGAGCGAGCGTGTCATTGTCATGCAAGAAGATAGCGTAGGTCAATATGTTATTGATAGGAAAGATATCTTCAAA +TATTTGTCCCAGAAAAAGGAGGTAGCTCAACATGACTAACTTTTTCGACATATTGAGTGAACGTAAGGGGCAACTCTTTT +CGACAATGATAGAACATATTCAAATATCATTTATCGCATTATTGATTGCAACTGCTATTGCGGTACCATTAGGTATTTTA +TTAACGAAGACTAAAACGATATCTGAAATCGTAATGAATATTGCGGCAATTCTTCAAACCATACCATCGTTGGCATTATT +AGGTTTAATGATTCCTTTATTTGGTATCGGTCGTGTGCCAGCAATTATTGCACTTGTAGTGTATGCGTTGTTACCAATTT +TAAGGAATACGTATACTGGAATTAAAGAAGTTGATCCATCACTCATTGAAGCGGCTAAAGGTATAGGTATGAAACCATTT +AGACGTTTAACTAAAGTCGAACTTCCGATAGCAATGCCTGTTATAATGGCTGGTGTAAGAACGGCTATGGTATTAATTAT +AGGTACAGCAACACTAGCAGCATTAATTGGTGCAGGCGGACTAGGAGATTTAATTTTATTAGGTATAGACCGTAACAATG +CATCGTTGATATTATTAGGTGCAATTCCAGCAGCCTTATTGGCAATTATATTTGATTTAATTTTAAGATTTATGGCTAAA +TTATCTTATAAAAAGTTATTGATGACGTTAGGTGTTATAGTGATGATTATTATACTGGCTATCGCTATTCCTATGTTTGC +ACAAAAAGGTGATAAAATTACGTTAGCTGGAAAGCTTGGCTCCGAGCCATCGATTATTACAAATATGTATAAAATTTTAA +TAGAAGAAGAGACCAAAAATACTGTAGAAGTGAAAGATGGTATGGGCAAAACAGCATTTTTATTTAATGCTTTAAAATCT +GACGATATAGATGGGTATTTAGAATTTACTGGAACAGTTTTAGGTGAATTAACAAAAGAACCATTGAAGTCAAAAGAAGA +GAAAAAAGTTTATGAACAAGCTAAGCAAAGTCTTGAAAAGAAATATCAAATGACTATGTTAAAACCAATGAAGTATAACA +ATACGTATGCTTTAGCTGTAAAACGTGATTTTGCTAAACAACATAATATACGTACAATTGGTGATTTAAATAAGGTTAAA +GATCAACTTAAACCAGGATTTACATTGGAATTTAATGATCGTCCAGATGGTTACAAAGCTGTTCAAAAGGCTTATAATTT +AAATTTAGATAACATACGTACAATGGAACCTAAGTTGAGATATCAAGCGATCAATAAAGGTAATATTAATTTAATAGATG +CATATTCAACTGACGCTGAATTAAAACAATATGATATGGTTGTGTTAAAAGATGATAAGCACGTATTTCCACCATATCAA +GGAGCACCATTATTTAAAGAAAGCTTTTTAAAGAAACATCCAGAAATTAAGAAACCGTTAAACAAACTAGAAAACAAAAT +ATCTGATGAAGATATGCAAATGATGAACTATAAAGTAACAGTTAAAAATGAAGACCCATATACAGTTGCGAAAGATTATT +TAAAAGCAAAAGGGTTAATCAAATAACGACCAACGCCACATAAGATGCGTAACACCAAATTATATCTTATGTGGCGTTGT +TATATTTAAATCTATAATTATGTTCAATTTAAACATGCAATAATGATTAAAAAATATGACATGTTAAACACAATGTAAGC +TATTATGATGTGAAAATAGTAGCATTGCATTTTAGAAACATAGAGCGATATAATGAATATAAGTTTTTTGAAATTTCAGT +TAATTCTAAGGAGGTTGTTTTTATTATGAAAGAACAACTTAATCAACTATCAGCATATCAGCCTGGTTTATCTCCAAGGG +CATTGAAAGAAAAGTATGGCATTGAAGGAGATTTATATAAACTTGCATCAAATGAAAATTTGTATGGACCATCGCCTAAA +GTTAAAGAAGCGATATCAGCACACTTAGATGAGTTATATTATTATCCTGAAACAGGATCACCGACATTAAAAGCGGCGAT +TAGTAAACATTTAAATGTAGATCAATCACGCATTTTATTTGGTGCGGGATTAGATGAAGTTATATTAATGATTTCTAGAG +CTGTATTAACGCCAGGGGATACTATTGTTACAAGTGAAGCGACATTCGGTCAATATTATCACAATGCGATTGTTGAATCA +GCTAATGTGATACAAGTACCTTTAAAAGATGGTGGCTTCGATTTAGAAGGTATTTTAAAAGAAGTTAATGAAGATACGTC +ATTGGTATGGTTATGTAATCCAAATAATCCTACAGGTACATATTTTAATCATGAGAGCTTAGATTCGTTTTTATCTCAAG +TACCTCCACATGTACCAGTAATTATAGATGAAGCTTATTTTGAATTTGTGACAGCAGAGGACTACCCGGATACACTTGCT +TTGCAACAAAAATATGACAATGCTTTCTTATTACGTACATTTTCAAAGGCGTATGGATTAGCGGGTTTACGTGTAGGATA +TGTGGTAGCAAGTGAACATGCGATTGAAAAATGGAACATCATTAGACCACCATTTAATGTGACACGTATATCTGAATACG +CAGCAGTTGCAGCACTTGAAGATCAACAATATTTAAAAGAGGTAACACATAAAAATAGTGTTGAACGCGAAAGATTTTAT +CAATTACCTCAAAGTGAGTATTTCTTGCCAAGTCAAACGAATTTTATATTTGTAAAAACAAAGCGGGTAAATGAACTTTA +TGAAGCACTTTTAAATGTAGGGTGTATTACGCGACCATTTCCAACTGGTGTTAGAATTACAATTGGTTTTAAAGAACAAA +ATGATAAAATGTTAGAAGTTTTATCAAACTTTAAATACGAATAGTAAGTGGGGAGTGGGACAGAAATGATATTTTCGCAA +AATTTATTTCGTCGTCCCACCCCAACTTGCATTGTCTGTAGAAATTGGGAATCCAATTTCTCTTTGTTGGGGCCCCGCCG +GCAAGGTTGACTAGAATTGAAAAAAGCTTGTTACAAGCGCATTTTCGTTCAGTCAACTACTGCCAATATAACTTTGTAGA +GCATTGAACATTGATTTATGTCTCAAGCTCAATGCAGTGTGAATGATGAGGTGAGAGTATTCAGTGTAAAAAGCAACAAT +AGATGATATTGTTTTGTATCAATTGCTTTTTTGCTATACTGAATCAATACTGATATTTTCAGGAGAAGATTAAAATGACC +CGTAAATCAATCGCGATTGATATGGATGAAGTATTGGCAGATACATTAGGAGAAATCATTGATGCTGTCAATTTTAGAGC +GGATTTAGGTATTAAAATGGAAGCTTTGAATGGTCAAAAACTTAAACATGTTATTCCTGAACATGATGGATTAATTACAG +AAGTATTGAGAGAACCAGGCTTCTTCAGACATCTTAAAGTGATGCCGTATGCACAAGAAGTTGTGAAAAAATTAACTGAA +CATTATGATGTATATATTGCTACAGCAGCAATGGATGTACCAACATCATTTAGTGATAAATATGAATGGTTACTAGAGTT +CTTTCCATTTTTAGATCCTCAGCATTTTGTTTTTTGTGGTAGAAAAAACATCGTTAAAGCTGATTATTTAATAGATGACA +ATCCTAGACAGCTTGAAATTTTTACTGGTACACCGATTATGTTTACAGCAGTGCATAATATTAATGATGATCGATTTGAA +CGCGTAAATAGCTGGAAAGATGTAGAACAGTATTTTTTAGATAATATTGAGAAATAAAATATATCACTTGAAAAATTTCA +TGTAGAAAAGATGATGGATAGGCTATAAAGTAATTGTGACTGAGATGAACTTTTATGTCTTAGACACTACAACACTATAT +TGGCAGTAGTTGACTGCGGGGCCCCAACATAGAGAAATTGGATTCCCAATTTCTACAGACAATGCAAGTTGGGGTGGGCC +CCAACATAAAGAAATACTTTTTCTTTAGAAATTAGTATTTCTTATGCATGAGTGTAACTCATGCATTCATATTTTTAAGT +ACACATTAGCTGTGACTAATGATAAAGAATCGCTACATAATCAATCATTAGTCGTTCTTTATCATTTCCGTCCCGCTCTC +AATAAATGTTAGTCTATCTTATTATTATAAATCGGATGAATGTGTTAATCTATGGCAGATTACACGTCATCCGATTTTTT +ATAGAATTTGAAAAAGACGCATAAACCACTATGATTTAAAATACAACATCAATCATTTTAGTGGCATGCGCCAAAATTAT +ATGTCTGTTTTTGAAACAGGGTAATAGCTTAAAGCTAATAAAAACGAATATAAGGTGCGTTGAATCTTATGATTACACTC +CAAACCTAATATAATATCGGGTTAAGATCATTCCGGATGCTTACAAATCATTGACAGTAAGTAACTGAATGGCATTTGGT +ATAACCTCAATATCAATAGGTGTTTCTAATGAAATTTCGCCATCAATATCAACTTTCATTGCTGGATCTGTTGTAAGTGA +AATCTTTTTACCAGGTATATGCTCAATACCTTGAGTAATTTCATTCCAATTCATGCTATCACGCTTTTTAAAAATATCAT +TTAAAATACTGAAACTTTGTTCATTAAAAATGAAAGTGTTCAGTTCACCATCTTGAGGAGACAAATCAGTCAATGGTATA +CGACTACCACCAATGAATGGACCATTTGCTGTTAGTATCATGGTCGTTTCGCCAGAATATGTCTTATCATCTATTGATAA +TTGATAATTAAATTGTGTTGGATTTAGCAGTGTTTTGACAGTTGATCCAATATAACTCAATTTACCAAATATATCTTTTG +AACCATCTTGTACGTTTTCAGCGTTTTGAACAATGAGACCTAAGCCAACAAAGTTGAGTGCATATTGATTATTTATTTTA +ATTACATCGTATGTACCAACTTGTGCAGAAATCATTTGTTCACTAGCTTGTTTATGATTAGGTGCTATATTTAGCGTTTT +TGTAAAATCATTAAAAGTACCGCCTGGTAAAATGCCAATAGGGAGTTGAAGGTCATGTGTCATAACACCGTTTATAAGTT +CGTTAACCGTGCCATCACCGCCAAGAATAAATAATATATCTACATCTTTTGCATAGTTTTTAGTTTTGATTTCTTGGCAA +TATTTAATAATGTCACCTTCGTTTTCACTCAATTGAATAGAAAGATGCTTACAAATTGAACTTAATGCTGTTGTAACTTC +CCCAATACCTTGATTAATATTTTTTAATCCACTGTGTTCATGGTAAAAGAGGACACCATGTGTATATTTATTTTCCATAG +TTTAGCCTACTTTCTAAAAATTGGTTCATTAAATATATATACCCACTTTTAATTGTTAATACCAAAAATATGTTTTTAAA +TAGAGAAAATGGTAATAAATGAAATTGATTTCTATAGAGTGGGACGAGAAAATATAGTTATAGCTGTCTATAATGAGCAT +ATTAAGTTTTTATTTATACTGATATCTTGAATTTAATTAATAGAAACCTATAAAAAAACAGTAAGCCATTTAAATGACTT +ACTGTTTTTTGAATTAGGCCAACAATATTAACGTATACCTTTCATCGCTTTGATGATTAAAGGTGAGAATGCTAATACAA +TTGTTGTAACAATAATTGCAACAACACCTAGGAAAATAAAGTAATTTGTTTGACCTAGTGGTTCTATTAACTTAACTAAA +GTACCATTGATTGCTTGTGCAGAAGCGTTAGTTAAGTACCAAATACTCATCATTTGGGCATTAAATGCTTTAGGTGCTAA +CTTAACAGCAGCACTATTACCCGTTGGTGATAAGCATAGCTCACCGATAACACAAATAATGTACGATAAAATAACCCAGT +TAACTGAAAAGTTTGATGAACCTGATGCATAACCTACAATACCAATTAGTATGTATGACGCACCTGCTAAGAACGTACCA +ATTGCAAATTTTACTGGCAGGCTAGGTTGTTTAGTTCCAAGCTTTTGCCATAAAAGTGAAATAATTGGAGCTAGTAATAA +AATAAATAATGGGTTAATTGATTGGAAGATCGCTTCACCAAAGTTTGTTTTCCAACCAAATAAGTTTAATTTCATATCTG +AATGTTCAATTCCATATATGTTTAATACATTAGACCCTTGTTCTTGAATAGCCCAGAACACCATTCCAAGAATAAATAAT +GGAATAAATGCTTTAACACGAGAACGTTCAGTATCAGTGACATCTTTACTTCTAATAATTAAAGTGAAGTAAATGATTGG +TAATGCAATACCTAATACTAAAACAGTATTACTAACTAAGTTAAATGATAATGAGTTAGTTAATGCACCAATAACGATAA +TTAATACAATTGCTAAAACAACACTTCCGATAATAAGACCATACTTTTTCTTTTCAGCTGGTGTCAATGGGTTAGTAGGT +TTCATACCAACGCTACCTAAGTTTTTGCGGTTGAAAAGTACATACCATACTAAACCTAATGCCATACCAACTGCTGCAAT +CAAGAATCCGCCGTGGAAGTTTTTAACATTAACAAAGTGTTGCAAAATAATAGGTGATAATAATGCACCCATATTAACTG +ACATATAGAAAATAACAAAACCTGCATCCATACGTCTATCATTTTCAGGATATAAACGGCCAACGATATTTGAAATGTTT +GGCTTCATTAAACCTGAACCAATAATGATGAAGAACATTGATGTGAATAAGCCGATTAATGCAAATGGTAAGCTTAAACA +AATATGTCCGATAATAATAAAGACTGCACCTAATAAAGTAGCGCCTCTAGTGCCTGTAATTCTGTCAGCAATCCATCCGC +CTGGTATTGATGTCATATAGATTAATGAACCATAAACTGACATAATTGACATAGCTGTTGTTTTATCAATTCCAAGGCCA +TTATCTGTTACGGCAAAGTACATGTAGAAAATGAGTAGGGCACGCATGCCATAATAACTAAACCTTTCCCAGAACTCTAC +AAAGAAGAGTACGCCTAGTCCTCGAGGATGCCCGAAAAATCCTGTTTGAGGTATGTCTTGAATTTGATTTCCATGGGAGT +TTTGTTGTGTCATGTATACATCCCATCCTTTCTTCCCCTAATACAATAGTTTAAGTAAATTTGATTGTGCCTTATCAACT +AAATATTCCAACAAATCACAAAAAAATAAAAATATTGAGAATATTCTGTTATTAAGCGCGCACAATTAATTAGAGATAAT +ATAAAAATATACTTATTGACGTTAAAGTGCAATAGGTTTCTATAAAAATGTTGTTTAATTCGTGTACTAATATTCTTTTG +GTGTACTTTGGTATAAAATGTTATTTTAATGTAACCTATTATGTATGCTAAATGAAAAACACACCAATAAGTTAGGAAAA +TCAACTTATTGGTGTGTTTATTAAATGATACATTTAACGATTATCTATTTTTTCGGGATATAAATCATGATTCATCAAAC +GATGCTCAGCCATTTTTTCATATTTAGAATTTGGACGTCCATAGTTTGTATAAGGATCAATAGAAATTCCACCACGTGGT +GTGAACTTGCCCCAGACTTCAATATAATGTGGGTCCATAAGCTCTATCAAATCATTCATAATAATATTCATACAATCTTC +GTGAAAATCACCGTGATTTCTGAAACTAAATAAGTATAATTTCAAAGATTTTGATTCAACCATTTTAACATTTGGAATAT +ATGAAATATAGATAGTTGCAAAATCTGGTTGCCCAGTAATTGGACATAATGATGTAAATTCTGGACAGTTGAATTTTACG +AAATAGTCACGACCTTGATGCTTATTATCAAACGATTCTAATACATCAGGACGATAGTCAAAATTGTAAGTATTGTCTTG +ATTTCCTAATAAAGTTATATCTTGTAATTCATCTTGTTGACGGCCATGTGCCATATAAAGCGCTCCTTTAAATTTATTTT +TTTATTATTTTGGCGTCTCGGCGTGCTTTTTCAAACATGTAATAACTTGCACCGATAATAACGACGTAACCTAATGTTGC +ATAGAAATCTGGAGATTCTCCGAATAGAATAAATCCAAGTATTGCTGTGAAAATTATAGATGCATACGTAAAAATAGAAA +TATCTTTTGCTGCTGCAAAACTATATGCTAAAGTAACACCAATTTGACCCACAGCGGCAGCTAAGCCAGCCCCTAATAGA +TAAAGTATTTGCATCTGACTCATTGGTTCATAAGTATATGCAGTGAAAGGTATTAAAACGATGACAGAAAATAAGGAGAA +GTAAAATACTATAGTATATGGTGCTTCTCTTGTACTAAGTGCTCGAACACATGTATATGCTGATGCTGCAAAAATACCTG +AGAATAAGCCAGCTAATGATGGAATCATAGATGATGAAAATTCAGGTTTCACTATTAAAAGCATACCTAAAATAGCAATT +ATCATTGCTGTAATTTGATACTTCCTTACCTTTTCATGTAAGAAAACAATGCTTAATAAAATCGTCCAGAAAGGATTGAG +TTTCATTAATGAATCGGCATCACTAAGTACCATATGATCAATGGCATAAATATTTAACAATACACCAATAAGTCCAAGTG +TTGATCGTGTTATTAATAAGGGTTGACTTGAAAGTCTGCCAAACATTGGCTGATGGTATTTATATATAAAAAATAATGGA +ATAAACATTGCTACTAAGTTTCGTGCTAATGATTTTTGAAAAACAGGAAGGTCACCTGCAAGTCTGAAAAACACTGACAT +AAAACTGAAACCAATAGCCGAAATTAAAATGGCAATGATACCTTTTACTTTAGGATTCAATTTTATCGCCTCTTTTATAT +AAAATTAACGTATTTATATTAGCATAAAACAACATGTTGTGCATAAATAGTTGAAATTTACTATAAAAAGACTATAATAG +ACTGTAGCGAACAAACGTTCTGTGTTTATTTGTCGGAATAATAGGGCATTACACTTTTATGAATGTTTGTGTTATTACAT +AAAACAAATATCAATTCAGTATCAAGCTAATAAGCTTTTTCTTGATTTCTGTTGATACAATTGAGATTGACACAGATTTA +AAAAAATCAAGTGATATCTACTAAAAAATTTTTTTAAATTTGTTCAAGTTTTTCTAATTTAGTATTGGTGCCTAGTTGGA +ACGTTTTACGAACATTCGATTAGAAAATGGCACTTTAAATCATAGTGTGTCTTATGTATAATGAAACACATAATATAGTG +TTGGTGAAACGAAAAAGACACAATATCTTGTGTTTTGTATGCAAATGCTTTATTTATGAAGAAATTACATTTAAAAGTAA +TTTAACACAGAAATTTAATAGTTATTATCAATTAATAGTCATATTTTTAGAAAATGTACTGAGCAAATGGAAGATATCCA +ATGATGTAAACACTACATATAGTGATTTTTATACATTCAACCCATATAAGCTACTATTTTCTCAAATATAAATCTATGCA +ATTGGTTTACATTTGAGAAAATAAGTAGCTTCATTATAGTTAATACAATGCTGAGATAACCATAGTAACCATGTTGTTAA +AGCATTTTTTAATTGGAATGACTACTTTATTTAAAAGGGTTGAAGAAAGAAGGTGATCCAATGAAAATAATATATTTTTC +ATTTACTGGAAATGTCCGTCGTTTTATTAAGAGAACAGAACTTGAAAATACGCTTGAGATTACAGCAGAAAATTGTATGG +AACCAGTTCATGAACCGTTTATTATCGTTACTGGCACTATTGGATTTGGAGAAGTACCAGAACCCGTTCAATCTTTTTTA +GAAGTTAATCATCAATACATCAGAGGTGTGGCAGCTAGCGGTAATCGAAATTGGGGACTAAATTTCGCAAAAGCGGGTCG +CACGATATCAGAAGAGTATAATGTCCCTTTATTAATGAAGTTTGAGTTACATGGAAAAAACAAAGACGTTATTGAATTTA +AGAACAAGGTGGGTAATTTTAATGAAAACCATGGAAGAGAAAAAGTACAATCATATTGAATTAAATAATGAGGTCACTAA +ACGAAGAGAAGATGGATTCTTTAGTTTAGAAAAAGACCAAGAAGCTTTAGTAGCTTATTTAGAAGAAGTAAAAGACAAAA +CAATCTTCTTCGACACTGAAATCGAGCGTTTACGTTATTTAGTAGACAACGATTTTTATTTCAATGTGTTTGATATTTAT +AGTGAAGCGGATCTAATTGAAATCACTGATTATGCAAAATCAATCCCGTTTAATTTTGCAAGTTATATGTCAGCTAGTAA +ATTTTTCAAAGATTACGCTTTGAAAACAAATGATAAAAGTCAATACTTAGAAGACTATAATCAACACGTTGCCATTGTTG +CTTTATACCTAGCAAATGGTAATAAAGCACAAGCTAAACAATTTATTTCTGCTATGGTTGAACAAAGATATCAACCAGCG +ACACCAACATTTTTAAACGCAGGCCGTGCGCGTCGTGGTGAGCTAGTGTCATGTTTCTTATTAGAAGTGGATGACAGCTT +AAATTCAATTAACTTTATTGATTCAACTGCAAAACAATTAAGTAAAATTGGGGGCGGCGTTGCAATTAACTTATCTAAAT +TGCGTGCACGTGGTGAAGCAATTAAAGGAATTAAAGGCGTAGCGAAAGGCGTTTTACCTATTGCTAAGTCACTTGAAGGT +GGCTTTAGCTATGCAGATCAACTTGGTCAACGCCCTGGTGCTGGTGCTGTGTACTTAAATATCTTCCATTATGATGTAGA +AGAATTTTTAGATACTAAAAAAGTAAATGCGGATGAAGATTTACGTTTATCTACAATATCAACTGGTTTAATTGTTCCAT +CTAAATTCTTCGATTTAGCTAAAGAAGGTAAGGACTTTTATATGTTTGCACCTCATACAGTTAAAGAAGAATATGGTGTG +ACATTAGACGATATCGATTTAGAAAAATATTATGATGACATGGTTGCAAACCCAAATGTTGAGAAAAAGAAAAAGAATGC +GCGTGAAATGTTGAATTTAATTGCGCAAACACAATTACAATCAGGTTATCCATATTTAATGTTTAAAGATAATGCTAACA +GAGTGCATCCGAATTCAAACATTGGACAAATTAAAATGAGTAACTTATGTACGGAAATTTTCCAACTACAAGAAACTTCA +ATTATTAATGACTATGGTATTGAAGACGAAATTAAACGTGATATTTCTTGTAACTTGGGCTCATTAAATATTGTTAATGT +AATGGAAAGCGGAAAATTCAGAGATTCAGTTCACTCTGGTATGGACGCATTAACTGTTGTGAGTGATGTAGCAAATATTC +AAAATGCACCAGGAGTTAGAAAAGCTAACAGTGAATTACATTCAGTTGGTCTTGGTGTGATGAATTTACACGGTTACCTA +GCAAAAAATAAAATTGGTTATGAGTCAGAAGAAGCAAAAGATTTTGCAAATATCTTCTTTATGATGATGAATTTCTACTC +AATCGAACGTTCAATGGAAATCGCTAAAGAGCGTGGTATCAAATATCAAGACTTTGAAAAGTCTGATTATGCTAATGGCA +AATATTTCGAGTTCTATACAACTCAAGAATTTGAACCTCAATTCGAAAAAGTACGTGAATTATTCGATGGTATGGCTATT +CCTACTTCTGAGGATTGGAAGAAACTACAACAAGATGTTGAACAATATGGTTTATATCATGCATATAGATTAGCAATTGC +TCCAACACAAAGTATTTCTTATGTTCAAAATGCAACAAGTTCTGTAATGCCAATCGTTGACCAAATTGAACGTCGTACTT +ATGGTAATGCGGAAACATTTTACCCTATGCCATTCTTATCACCACAAACAATGTGGTACTACAAATCAGCATTCAATACT +GATCAGATGAAATTAATCGATTTAATTGCGACAATTCAAACGCATATTGACCAAGGTATCTCAACGATCCTTTATGTTAA +TTCTGAAATTTCTACACGTGAGTTAGCAAGATTATATGTATATGCGCACTATAAAGGATTAAAATCACTTTACTATACTA +GAAATAAATTATTAAGTGTAGAAGAATGTACAAGTTGTTCTATCTAACAATTAAATGTTGAAAATGACAAACAGCTAATC +ATCTGGTCTGAATTAGCAGATGATTAGACTGCTATGTCTGTATTTGTCAATTATTGAGTAACATTACAGGAGGAAATTAT +ATTCATGATAGCTGTTAATTGGAACACACAAGAAGATATGACGAATATGTTTTGGAGACAAAATATATCTCAAATGTGGG +TTGAAACAGAATTTAAAGTATCAAAAGACATTGCAAGTTGGAAGACTTTATCTGAAGCTGAACAAGACACATTTAAAAAA +GCATTAGCTGGTTTAACAGGCTTAGATACACATCAAGCAGATGATGGCATGCCTTTAGTTATGCTACATACGACTGACTT +AAGGAAAAAAGCAGTTTATTCATTTATGGCGATGATGGAGCAAATACACGCGAAAAGCTATTCACATATTTTCACAACAC +TATTACCATCTAGTGAAACAAACTACCTATTAGATGAATGGGTTTTAGAGGAACCCCATTTAAAATATAAATCTGATAAA +ATTGTTGCTAATTATCACAAACTTTGGGGTAAAGAAGCTTCGATATACGACCAATATATGGCCAGAGTTACGAGTGTATT +TTTAGAAACATTCTTATTCTTCTCAGGTTTCTATTATCCACTATATCTTGCTGGTCAAGGGAAAATGACGACATCAGGTG +AAATCATTCGTAAAATTCTTTTAGATGAATCTATTCATGGTGTATTTACCGGTTTAGATGCACAGCATTTACGAAATGAA +CTATCTGAAAGTGAGAAACAAAAAGCAGATCAAGAAATGTATAAATTGCTAAATGACTTGTATTTAAATGAAGAGTCATA +CACAAAAATGTTATACGATGATCTTGGAATCACTGAAGATGTGCTAAACTATGTTAAATATAATGGAAACAAAGCACTTT +CAAACTTAGGCTTTGAACCTTATTTTGAGGAACGTGAATTTAACCCAATCATTGAGAATGCCTTAGATACAACAACTAAA +AACCATGACTTCTTCTCAGTAAAAGGTGATGGTTATGTATTAGCATTAAACGTAGAAGCATTACAAGATGATGACTTTGT +ATTTGACAACAAATAACAATTAAATTAAAAGACCTTCACATGTAAAGGGAAATAGCGATTCGTTTCGTCTTGTCTCCTAC +ATGTTGAAGGTCTTTTTTTATGTGTATCTAACTCATTATGAGTCTGAGTAAGAAATCAATGCTCTAAGATGTACAATGCT +ATTTATATTGGCAGTAGTTGGCGGGGCCCCAACACAGAAGCAGGCGGAAAGTCAGCTAACAATATTGTGCAAGTTGGCGG +GGCCCCAACATAGAAGCAGGCGGAAAGTCAGCTAACAATAATGTGCAAGTTGGCGGGGCCCCAACATAAAAGCAGGCGGA +AAGTCAGCTAACAATATTGTGCAAGTTCGGGCGGGGCCCCAACATAAAGAAAAACTTTTTCCTTTAGAAATTATCACTTC +CACATGAGTTTTACTCATGTATTCCTATTTTTAAGTACACATTAGCTGAGGCTAATGTTAAGAACCACTACTTAATCAAT +CATTAGTAGTTTTTATCATTTCCACTATTCCCAGACATCAAAATCTTAAGTGTTCTATTTTACTTTAAGTAAACAAAATA +CACATTCCGAAAAATTAAATTTCAGTTTAATTGCAAATATCAATAAAATTGACACTAAATTATTTGAAAGGCTATTGAAA +TTATGGTCAAAAAACGCTACTATTAATGAGAAATATTATCAATGATAATGATTATCATTAATTTAAAGGGAGAAAAATTT +GTAATGAAGTATTTATTAAAGGGAAATATTTTGCTTCTATTACTAATATTGTTGACAATTATTTCGTTGTTCATAGGTGT +GAGTGAACTATCAATTAAAGATTTACTACATTTAACTGAGTCACAGCGGAATATTTTATTCTCAAGCCGAATACCAAGGA +CGATGAGTATTTTAATTGCTGGAAGTTCGTTGGCTTTAGCAGGCTTGATAATGCAACAAATGATGCAAAATAAGTTTGTT +AGTCCGACTACAGCTGGAACGATGGAATGGGCTAAACTAGGTATTTTAATTGCTTTATTGTTCTTTCCAACCGGTCATAT +TTTATTAAAACTAGTATTTGCTGTTATTTGCAGTATTTGCGGTACGTTTTTATTTGTTAAAATCATTGATTTTATAAAAG +TGAAAGATGTCATTTTTGTACCGCTTTTAGGAATTATGATGGGTGGGATTGTTGCAAGTTTCACAACCTTCATCTCATTG +CGCACGAATGCTGTTCAAAGCATTGGTAACTGGCTTAACGGGAACTTTGCCATTATCACAAGTGGACGCTATGAAATTTT +ATATTTAAGTATTCCTCTTTTAGCATTGACATATCTTTTTGCTAATCATTTCACGATTGTAGGAATGGGTAAAGACTTTA +CTAATAATTTAGGTTTGAGTTACGAAAAATTAATTAACATCGCATTGTTTATTACTGCAACTATTACAGCATTGGTAGTG +GTGACTGTTGGAACATTACCGTTCTTAGGACTAGTAATACCAAATATTATTTCAATTTATCGAGGTGATCATTTGAAAAA +TGCTATCCCTCATACGATGATGTTAGGTGCCATCTTTGTATTATTTTCTGATATAGTTGGCAGAATTGTTGTTTATCCAT +ATGAAATAAATATTGGTTTAACAATAGGTGTATTTGGAACAATCATTTTCCTTATCTTGCTTATGAAAGGTAGGAAAAAT +TATGCGCAACAATAATAAAAAAATAATGCTTTTAATTGCAGTAACGTTATTAATTAGTATGCTGTACTTATTTGTAGGTA +TTGATTTTGAAATATTTGAATATCAATTTTCAAGTCGTTTAAGAAAGTTCATATTAATTATTTTAGTAGGTGCTGCCATT +GCAACTTCAGTGGTGATTTTTCAAGCGATTACAAATAACCGTCTATTGACACCATCAATAATGGGGTTAGATGCAGTTTA +TTTATTTATCAAAGTATTGCCAGTCTTTTTATTTGGAATTCAATCGGTATGGGTTACTAATGTATATTTGAACTTTATAT +TAACACTTATAACGATGGTGTTATTCGCACTAATCCTATTCCAAGGTATCTTTAAAATCGGACATTTTTCAATTTATTTT +ATCTTACTTATTGGTGTCCTTTTAGGAACATTTTTTAGAAGCATAACAGGTTTTATTCAACTGATTATGGATCCTGAGTC +ATTTTTAGCAATACAAAGTAGTATGTTTGCTAATTTTAATGCTTCTAATTCGAATTTAGTTACTTTCTCAGCAGTGCTAT +TAGTAATCTTATTAGTCATTACAATTTTACTATTGCCTTATTTAGATGTATTGCTTTTAGGTCGTGCTGAAGCAATTAAT +CTTGGGATATCGTATGAAAAATTAACGCGAATTCTACTTGTAATAGTCTCAGTTTTAGTTTCTGTGTCAACTGCATTAGT +AGGACCAATTACATTTTTAGGTTTATTAACTGTAAATCTAGCGCATGAACTAATGAAGACGTATGAACATAAGTATATTT +TAATTGCGACAATTTGCTTGAGTTGGATTAGTTTATTTAGTGCGCAATGGGTAGTTGAAAGTGTGTTTGAAGCTACGACA +GAAATGAGTATACTTATTGATTTGATTGGTGGAAGTTATTTCATTTATCTATTAGTTAGAAGGAGAAATGCGCAATGATT +CAAGTTGAAAATTTAACTAAAACTATAAATAATCAAATGATATTGGAAGATATTAGCATAGATATCGAAAAAGGTAAATT +GACTTCTTTAATTGGACCTAATGGTGCGGGTAAGAGTACTTTACTTTCAGCGATATGTAGGTTAATTCGTTTTGAGAACG +GTGAAGTGAAAATAGATGGACAGCTCATGTCTGATTATAAAAATAATGACTTGTCGAAAAAAATATCTATATTAAAACAA +ACAAACCATACTGAAATGAATATTACGGTAGAGCAGTTGGTAAACTTTGGACGATTCCCTTATTCTAAAGGTCGTTTGAC +GAAAGAGGATCATGATATTGTCAATGATGCGCTAGATTTGTTGCAACTACAAGATATCAGAAATCGTAATATTAAGTCAT +TATCTGGTGGACAACGTCAGCGTGCATACATTGCAATGACAATAGCACAAGATACTGAATATATTTTGCTAGATGAACCA +TTAAATAATTTAGATATGAAGCATGCTGTTCAAATTATGCAAACGTTAAAAATGTTAGCGCATAAAATGAATAAAGCGAT +TGTCATTGTGTTACATGATATTAACTTTGCGTCCTGTTATTCAGATCAGATTGTAGCATTGAAAAACGGACAACTAGTTA +AGTCAGATTTGAAAGATAATGTCATTCAAAGTAGTGTTTTAAGTGATTTATATGACATGAATATTCAAATTGAACATATA +AGAAATCAAAGGATTTGTTTATATTTTAAGGATTGATAATTTGGAGACACTTTAAAGGGGTGATGCGCCAATTAAAGAAG +GGTTAAACGTAAAGCATTTATTTATATTTCACATCAAGCACACAGATTAAGCCAAAAGAGGAGAATATTATATTATGAAG +AAAACAGTCTTATATTTAGTATTAGCAGTAATGTTTTTATTAGCGGCATGCGGTAACAATTCTGATAAAGAACAATCAAA +ATCAGAAACTAAAGGTTCTAAAGATACAGTAAAAATTGAAAATAACTATAAAATGCGTGGCGAGAAAAAAGATGGTAGTG +ACGCTAAAAAAGTTAAAGAAACTGTTGAAGTACCAAAAAATCCTAAAAATGCAGTTGTGTTAGACTATGGCGCATTAGAT +GTAATGAAAGAAATGGGCTTATCAGATAAAGTAAAAGCATTACCTAAAGGGGAAGGCGGTAAGTCATTACCGAATTTCTT +AGAATCATTTAAAGATGATAAATATACAAACGTTGGTAATTTAAAAGAAGTGAATTTTGATAAAATTGCTGCGACGAAAC +CCGAAGTAATCTTTATCTCTGGACGTACAGCTAATCAAAAGAATTTAGATGAATTCAAAAAAGCTGCACCTAAAGCGAAA +ATTGTTTATGTTGGTGCAGATGAAAAGAACTTAATTGGTTCAATGAAACAAAACACTGAAAATATCGGAAAAATTTACGA +TAAAGAAGATAAAGCTAAAGAATTAAATAAAGATTTAGATAACAAAATTGCTTCAATGAAAGATAAAACGAAAAACTTCA +ATAAAACTGTTATGTATTTACTAGTTAACGAAGGTGAATTATCAACATTTGGACCTAAAGGTCGTTTTGGTGGATTAGTT +TACGATACATTAGGATTCAATGCAGTTGATAAAAAAGTAAGTAATAGCAATCATGGACAAAATGTTTCTAACGAATATGT +TAATAAAGAAAATCCAGATGTTATTTTAGCGATGGATAGAGGTCAAGCGATAAGTGGTAAATCAACTGCGAAACAAGCAT +TAAATAATCCTGTATTAAAAAATGTTAAAGCAATTAAAGAAGACAAAGTATATAATTTAGATCCTAAATTATGGTACTTT +GCAGCTGGATCAACTACAACTACAATTAAACAAATTGAGGAACTTGATAAAGTTGTAAAATAATTTTAAAAGAGGGGAAC +AATGGTTAAAGGTCTTAATCATTGCTCCCCTCTTTTCTTTAAAAAAGGAAATCTGGGACGTCAATCAATGTCCTAGACTC +TAAAATGTTCTGTTGTCAGTCGTTGGTTGAATGAACATGTACTTGTAACAAGTTCATTTCAATACTAGTGGGCTCCAAAC +ATAGAGAAATTTGATTTTCAATTTCTACTGACAATGCAAGTTGGCGGGGCCCAAACATAGAGAATTTCAAAAAGGAATTC +TACAGAAGTGGTGCTTTATCATGTCTGACCCACTCCCTATAATGTTTTGACTATGTTGTTTAAATTTCAAAATAAATATG +ATAGTGATATTTACAGCGATTGTTAAACCGAGATTGGCAATTTGGACAACGCTCTACCATCATATATTCATTGATTGTTA +ATTCGTGTTTGCATACACCGCATAAGATTGCTTTTTCGTTAAATGAAGGCTCAGACCAACGCTTAATGGCGTGCTTTTCA +AACTCATTATGGCACTTATAGCATGGATAGTATTTATTACAACATTTAAATTTAATAGCAATAATATCTTCTTCGGTAAA +ATAATGGCGACAGCGTGTTTCAGTATCGATTAATGAACCATAAACTTTAGGCATAGACAAAGCTCCTTAACTTACGATTC +CTTTGGATGTTCACCAATAATGCGAACTTCACGATTTAATTCAATGCCAAATTTTTCTTTGACGGTCTTTTGTACATAAT +GAATAAGGTTTTCATAATCTGTAGCAGTTCCATTGTCTACATTTACCATAAAACCAGCGTGTTTGGTTGAAACTTCAACG +CCGCCAATACGGTGACCTTGCAAATTAGAATCTTGTATCAATTTACCTGCAAAATGACCAGGCGGTCTTTGGAATACACT +ACCACATGAAGGATACTCTAAAGGTTGTTTAGATTCTCTACGTTCTGTTAAATCATCCATTTTAGCTTGTATTTCAGTCA +TTTTACCAGGAGCTAAAGTAAATGCAGCTTCTAATACAACTAAGTGTTCTTTTTGAATAATGCTATTACGATAATCTAAC +TCTAATTCTTTTGTTGTAAGTTTAATTAACGAGCCTTGTTCGTTTACGCAAAGCGCATAGTCTATACAATCTTTAACTTC +GCCACCATAAGCGCCAGCATTCATATACACTGCACCACCAATTGAACCTGGAATACCACATGCAAATTCAAGGCCAGTAA +GTGCGTAATCACGAGCAACACGTGAGACATCAATAATTGCAGCGCCGCTACCGGCTATTATCGCATCATCAGATACTTCG +ATATGATCTAGTGATAATAAACTAATTACAATACCGCGAATACCACCTTCACGGATAATAATATTTGAGCCATTTCCTAA +ATATGTAACAGGAATCTCATTTTGATAGGCATATTTAACAACTGCTTGTACTTCTTCATTTTTAGTAGGGGTAATGTAAA +AGTCGGCATTACCACCTGTTTTAGTATAAGTGTATCGTTTTAAAGGTTCATCAACTTTAATTTTTTCATTTGGGATAAGT +TGTTGTAAAGCTTGATAGATGTCTTTATTTATCACTTCTCAGTACATCCTTTCTCATGTCTTTAATATCATATAGTATTA +TACCAATTTTAAAATTCATTTGCGAAAATTGAAAAGAAAGTATTAGAATTAGTATAATTATAAAATACGGCATTATTGTC +GTTATAAGTATTTTTTACATAGTTTTTCAAAGTATTGTTGCTTTTGCATCTCATATTGTCTAATTGTTAAGCTATGTTGC +AATATTTGGTGTTTTTTTGTATTGAATTGCAAAGCAATATCATCATTAGTTGATAAGAGGTAATCAAGTGCAAGATAAGA +TTCAAATGTTTGGGTATTCATTTGAATGATATGTAGACGCACCTGTTGTTTTAGTTCATGAAAATTGTTAAACTTCGCCA +TCATAACTTTCTTAGTATATTTATGATGCAAACGATAAAACCCTACATAATTTAAGCGTTTTTCATCTAAGGATGTAATA +TCATGCAAATTTTCTACACCTACTAAAATATCTAAAATTGGCTCTGTTGAATATTTAAAATGATGCGTACCGCCAATATG +TTTTGTATATTTTACTGGGCTGTCTAAGAGGTTGAATAATAATGATTCAATTTCAGTGTATTGTGATTGAAAACAATTAG +TTAAATCACTATTAATGAATGGTTGAACATTTGAATACATGATAAACTCCTTTGATATTGAAAATTAATTTAATCACGAT +AAAGTCTGGAATACTATAACATAATTCATTTTCATAATAAACATGTTTTTGTATAATGAATCTGTTAAGGAGTGCAATCA +TGAAAAAAATTGTTATTATCGCTGTTTTAGCGATTTTATTTGTAGTAATAAGTGCTTGTGGTAATAAAGAAAAAGAGGCA +CAACATCAATTTACTAAGCAATTTAAAGATGTTGAGCAAAAACAAAAAGAATTACAACATGTCATGGATAATATACATTT +GAAAGAAATTGATCATCTAAGTAAAACTGATACAACTGATAAAAATAGTAAAGAATTTAAGGCACTACAAGAAGATGTTA +AAAACCATCTCATACCTAAATTTGAAGCATATTATAAGTCAGCAAAAAATTTGCCTGATGATACAATGAAAGTTAAGAAA +TTAAAAAAAGAATATATGACGCTTGCAAATGAGAAGAAGGATGCGATATATCAATTAAAAAAATTCATAGGTTTATGTAA +TCAATCTATCAAGTATAACGAAGACATTTTAGATTATACGAAACAATTTGAAAAAAATAGATACAAAGTTGAATCAGAAA +TTAAATTAGCTGATAATAAAAGTGAAGCAACTAATCTTACGACAAAATTAGAACATAATAATAAAGCGTTAAGAGATACT +GCGAAGAAGAACCTAGATGATAGTAAAGAAAATGAAGTAAAAGGCGCGATTAAAAATCACATTATGCCAATGATTGAAAA +GCAAATTACCGATATTAACCAAACTAATATTAGTGATAAGCATGTTAATAATGCAAGGAAAAACGCAATAGAAATGTATT +ACAGTCTGCAGAACTATTATAATACACGTATTGAAACAATAAAGGTTAGTGAGAAGTTATCAAAAGTCGATGTAGATAAG +TTGCCGAAAAAGGGTATAGATATAACTCACGGCGATAAAGCCTTTGAAAAAAAGCTTGAAAAATTAGAAGAAAAATAACT +ATAATCATTTTTCAAAGTTAAAAATTTTGAATTTATGGTTAACATGTCAACTTACTATGTGTATAATGGTAAACATTGAT +ATTAACTATATGTATAAAAATGTCACGCAGATGCTATTTAAATGTGATAAATATTTTTAGAGGTGAATAGAGTGGCTATA +AAGCTAAGTTCAATTGACCAATTTGAACAGGTTATTGAGGAAAATAAATATGTTTTTGTATTAAAACATAGTGAAACTTG +TCCAATATCGGCAAATGCGTACGATCAATTTAATAAATTTTTATATGAACGCGATATGGACGGTTATTATTTGATTGTCC +AACAAGAACGCGATTTGTCAGATTATATTGCTAAAAAAACGAACGTTAAACATGAATCACCTCAAGCATTTTATTTTGTA +AATGGTGAAATGGTTTGGAATCGAGACCACGGTGATATCAATGTGTCGTCATTAGCACAAGCAGAAGAATAATGAAACTA +TAGGGTTGGAACATTTTGCCTTACACTACTAGACGTGAATAGCACAACTTAAATTCGTGTGAATCAGAGTAGTTTGGCTA +TAATGATGTTCTGACCTTTTATTTTATGTCACCTTTAGAAGCAGTTAAGTTAGTACTTTTTTACAAACATATGTATAATA +TATTCGAGTATTTTTATTGAAAATATTTTGGAAAACGACGAATCCAATAAGAAAATTTAAACATGATTTGTAAGTTAGTT +TAATAGGAAATATATGCTAAACCAAAAGAAGCATATTGTTATTTACTGGAATAATTAATAATCATGTCATGTTAAATGTT +AGCATATAATCACGAGATAAAATCTAAAATTTAAGATTAATCTTTTATGAATAAAAAACGTATCACAACAAATAATAAAG +TAAGGTGGTCAAGGTTATGAAAGTATTAGTAGCCATGGATGAGTTTCATGGAATTATTTCAAGTTATCAAGCTAATAGAT +ATGTTGAAGAGGCAGTTGCAAGCCAAATTGAAACTGCAGATGTAGTTCAAGTACCATTGTTTAATGGAAGACATGAATTA +TTAGATTCTGTATTTTTATGGCAATCTGGGCAAAAGTATCGTATACCAGTACATGATGCAGATATGAATGAAGTTGAAGG +TGTTTACGGACAAACTGATACAGGGATGACCGTTATCGAGGGGAATTTATTTTTAAAAGGTAAAAAACCAATTGTTGAAC +GAACAAGTTATGGTTTAGGAGAAATGATTAAACATGCATTAGATAACGACGCAAAACATGTTGTAATTTCACTAGGTGGG +ATTGATAGTTTTGATGCTGGTGCAGGTATGTTACAAGCATTAGGTGCTCAATTCTATGATGACGAAGGGCGTGTCGTAGA +TATGAGACAAGGTGCTGGTGTAATTAAATATATTCGTCGTATGGATATGTCGAACTTACACCCTAAAATGGAAACAGCAA +GAATTCAAGTAATGTCGGATTTTTCAAGTCGATTATATGGTAAGCAAAGTGAAATCATGCAAACTTATGATGCGCATCAG +TTGAATCATAATCAAGCAGCAGAAATCGATAATTTAATTTGGTATTTTAGTGAGTTATTTAAAAGTGAATTGAAAATTGC +AATTGGTCCAGTTGAACGTGGTGGTGCTGGTGGTGGAATTGCAGCAGTCTTGAATGGACTGTATCAAGCTGAAATATTAA +CCAGTCATGCATTAGTAGACCAACTAACACATTTAGAAAATTTAGTTGAACAAGCGGATTTAATTATTTTTGGAGAAGGA +TTAAATGAAAATGATCAGTTGCTAGAAACGACAACATTGCGTATTGCAGAACTTTGTCATAAACATCAAAAGGTTGCCAT +TGCAATTTGTGCAACTGCTGAAAAGTTTGATTTATTTGAATCACAAGGGGTTACAGCAATGTTTAATACATTTATCGATA +TGCCAGAAACTTATACTGACTTTAAAATGGGGTTACAAATTAGGCATTATACGGTTCAGTCTTTAAAACTGTTGAAAACA +CATTTTAATGTTGAGGTTTAGTAAAGAAGGACTAAATTGGTGATGCTGTCATGATGGTTAATAACATTTATGATGGTTAG +CAAAACGAATTAGAAGATCGAAAGTATACGTAAAAAATATGAAAAATCACGCTATCATTGCACTGAATGTTAGCGTGATT +TTTATATATTAATTAAGCCTGAGTTGAACTAGTATATAATCGTTGGTTTTTAGTGATTTTCAGCGATATCTTCTACAATT +CCAATGATTACTTGTACTGCTTTTTCCATAACATCAATGGATGCATATTCATATGGGCCGTGGAAGTTACCGCAACCTGT +AAAGATGTTTGGAGTTGGTAACCCCATAAATGACAATTGTGAACCATCTGTACCACCGCGAATAGGTTCAGTGTTTGCTG +GAATATCTAATTTGGCAAAGACACGTTTAGGTATATCAATAATATGAGGCAATGGTAATATTTTTTCTGCCATATTGAAA +TATTGATCCGATATATCAACTTTAACTGGATAATTTTCAAAATGGGCATTGATATCGTCACGTATTTCTAAAATACGTTT +CTTACGCAATTCGAATTGTTTTTTATCATGATCACGAATAATGTATTGCAAAGTTGCTTTTTCAACAGTTCCTTCAAAGT +TCATTAAGTGATAAAAGCCTTCGTATCCTTCTGTTCGCTCCGGAACTTCACTATCAGGTAGCAAACTATCGAATTGTTCA +CCTAAACGTATTGCGTTTACCATTGCATTTTTAGCTGAACCAGGATGAACATTTACACCGTGGCATGTAATAACCGCTTC +AGCAGCGTTAAAGCTTTCATATTGTAATTCTCCATATTGACTACCATCCATAGTATAAGCAAAATCAGCATTGAAGCGGT +CAACATCAAATTTATGTGGACCACGACCGATTTCTTCGTCTGGTGTAAATCCAATGCGAATGGTACCATGTTTAATTTCT +GGATGTTCTTGTAAATAACAAATAGCTTCCATAATTTCCACAATACCCGCTTTATCGTCTGCACCTAGTAACGATGTACC +ATCAGTTACCATTAATGTATGACCAACTAAACTGTTAAGTTCTGGAAATACTTTAGGATCTAAGACACGTTTAGTATTGC +CTAGTTTGTATGGCTTACCATCATAGTTTTCAATAATTTGCGGTTTAACATTTGAAGCATTGAAATCAGGTGATGTATCA +ACATGCGCCAAAAATCCAACTGTTGGGACGTCGACATCGATGTTACTTTCTAATGTAGCAAATAAGTAGCCATTTTCATC +TAAATCAGTTGGCAATCCTAATTGTTGTAATTCTTTTTCTAATAAATGTAACAAATCCCATTGCTTTTCAGTTGAAGGTG +TTGTTGTAGATTTTGGATCAGATTGCGTATCAATTGTCGTATATCTTGTTAATCTATCTATCAATTGGTTCTTCATTATA +TTCGACCCCTTAAACTCTATTATTCATGTTGTAAGATTTTTTATATGTCTTACCTTTGATTTTACCATACAGTTGTTTGA +TACGTGTGTATAGGTAATATAGAATTTCAGAAACTAATATACCGAAAGCAATCGCACCTGAAATCAGTGTAACTTCTAAA +AATGTATTTACAGCACTTGTATAATCATTTGATACTAAAAAACGAGTCGCTTGATAAGCTGCACCACCAGGTACTAATGG +TATAATGCCTGGCACTATGAATATAATTACCGGTCGTTTATATCTGCGACTCATAGTATGACTCATTAAGCCTAAAATTA +AGCTTCCCAAAAATGAAGCGCCAACTTTTCCAAACTCTAAATCTACCGTTAATTGGTAAATCGTCCATGCAATGGCACCC +ACAAATCCACATGCTACTAAGAGGCGTTTGGGTGCATTGAAAATGATAGAGAAAAGTACTGTTGATATAAAGCTGATTGT +AAAATGAAATAAATAAAATAGCATGCTTTAACAGTCCTTCCTTAAATGATTAATAAAACGATTGCGACACCAGCACCGAT +TGCGAATGCTGTTAATGCAGCTTCAACACCGCGAGACATACCTGCAAGTAATTCACCCGCTAATAAATCTCGAATGGCAT +TGGTAATTAATATACCAGGGACAAGTGGCATGACACTGGCTATAGTAATGATATCTTGATTGGTTGCAATGCCTAATTTA +GTAAATGTGGCTGCAATGGATATGACCACAGCGGCTGCAACAAACTCTGAGAAAAATTTAATTTGTATATAGCGTTGCAC +AAAGCTGAATGTTAAAAATGCGGATCCGCCAGCAATGACTGCAATCCAACAATCTGATGCGACACCACCAAACATAAATA +GGAAGAAGCCACATGCAATGGCAGCTGCAAAGAAATTCGTTAAAAAAGAATATTGTAATGATGCATGCTGTAAATGAATA +AATTCAGATTTAGCTTCATCAATTGTGAGTTCTTTATTTGATATTTTACGTGAAAGACTATTCGTTAAAGCGATTTTCTC +TAAATCTGTTGTACGCTCTTGTACACGAATTAATCTTGTACTTGTTCGATCGTTTAATGAAAAAATAATTGCAGTTGAAC +TGACAAAACTATATGTATTATGAAGACCATAACTATGTGCGATACGGTTCATTGTATCTTCAACTCGATATGTTTCAGCA +CCTGATTCAAGTAAAATTCTACCTGCAATTAATACAACATCAATCACTTTGTTTTCATCTATAATTGTGATTGAATCTGG +CATATCAATTCACCTCCAATGATATGTGTTATTTATTTGAACAATTGAAGTTTACAACTTGTTGTTACAACTTTCAATAG +TGAGACTTTGTGTTAGTATGATGAACTTGTATGGTTCAAATTTAAATAAGAAAAACTGTTAATCTTTGCTATTATACTAT +GATTTAATAATAGCAAAGGATTAACAGTTTTGTCGTTGTTATAAATTGATAATAGGGTTAAACATTACTTTGTTTCGCCC +TTGATTTTTGGCTACATGCACCATATCGTCTGCATCTTTAAACACTTTACGCTGTGATTTTGGATCGTCATCTGTTAAAT +AACCAACACCGATAGACACTGACAATTTAATAACTTCTTTGTTTGGTAAATGGAATGATGATTTTTCAACACCCGAACGA +ATATTTTCAGCTAATTTAACACTTTGATCAAGTGAATAATTGTGAATGACAACTGAGAACTCTTCGCCACCATTTCTAAA +AATTTTAAATTGATTCGGCACATAGTTTTTAAGTAATTGAGACATTTGTTTTAATACAGCATCACCTGATTTGTGTGAGT +AGGTATCATTGACATCTTTAAATCCATCGATATCGATTAATAATAATGCGATACTTTGATGTTCTTTTTCAGCTTTTCGT +GAAATTTCATTTAAATGTCTATCAAATTCTTTTACATTACCTAAGCCTGTTAAGTAATCATATTTATCTTCGTTTTCATA +ACGATTTACGAGTGAGAAGAAATGCCAAATATCGACAAATGTTATCGCTGAAGCTAAAGTGATAATTAATGAAATTGGTA +TTAAAATGATAACTTCCGATAGTGTGTAAATAGGACTCACTAACGCGACACCAAATAAAATGATTATTGTAACAACATTA +AGTATTAATAATGATAGCACATCATTTTGTTTTAAAAATGGTCCAATAGCACTTGTTACTGCAGCAATAACAATCAACGT +AACACCGTACATAATCGAGTTGTTAAATACTACAATTTCAACAATTGCTACAATTACTGTGGCAGATAATGTATAGACCA +TATTTGTAAATCTACCTAAAAACAATAAAGGAACGAATGTTAAGTGAATTAAATAATCTTCACGATAAGGGATAGGGTAG +ACAGATAATAATAATGATACGATTGTCATTAAAACAGTGACATAAGCCTTAGAAAAAACCATACGTTTGTTTTCTGAATA +CTGTAAGCGATGGAATAAATAGATTCCAGCGACTATAACAGATATATTGTATATAAATGCTTCGAACATGTCTGAATCGA +CTCCTTTAATTGACCACTAGCTATTGTAAGTGAAAACTTACAATTTGTCATTAGTTTACATATAAAATTAATGTATGATA +TAGACTTTGATGTTAAAATGTTGCCTTAAATGATATGATGAAAAAATGAATAATAGGCGATATATAAGAAATGAATCGTA +TAGTTGTAAATATGATATCATTGATTGAACGAATTAATTTATAATAAAGCTATAAGATATACGTAGAAAATAGATATATC +ATTCTATAAAGACAATATTAAATAAATATAACGTTAAAAACAATTAATATCGATGAAGGTGAATAAATGGTTACATTATT +ACTAGTTGCAGTAACAATGATTGTCAGTTTGACGATAACACCAATTGTTATTGCAATATCGAAAAGATTAAATTTAGTTG +ATAAACCAAATTTTAGAAAAGTACACACTAAACCTATTTCAGTTATGGGTGGTACAGTGATTCTCTTTTCATTTTTAATA +GGTATTTGGATTGGTCATCCTATTGAAACAGAAATCAAACCACTTATTATTGGTGCGATTATTATGTACGTACTTGGGCT +TGTAGATGATATCTACGATTTGAAACCGTATATAAAATTGGCTGGTCAAATTGCCGCTGCCTTAGTAGTTGCTTTTTATG +GTGTGACTATTGATTTTATTTCGTTGCCAATGGGTACAACGATTCATTTTGGATTTCTTAGTATTCCAATTACTGTGATT +TGGATTGTTGCTATTACAAATGCAATTAACTTAATTGATGGACTCGATGGTTTGGCGTCGGGTGTTTCTGCAATCGGACT +CATTACAATAGGGTTCATTGCAATTTTACAAGCTAATATTTTCATAACGATGATTTGTTGTGTTTTATTAGGCTCTTTAA +TTGGGTTTTTATTTTACAATTTCCATCCTGCCAAAATATTTTTAGGTGATAGTGGGGCTTTAATGATTGGATTTATCATC +GGATTCCTTTCTTTACTCGGATTCAAAAATATTACAATTATTGCATTGTTCTTCCCAATTGTTATCTTAGCAGTTCCATT +CATTGATACTTTGTTCGCAATGATTCGACGTGTGAAAAAAGGGCAGCATATAATGCAAGCTGATAAATCGCATTTGCATC +ATAAACTATTAGCTTTAGGCTACACACATAGACAAACAGTATTATTAATCTATTCAATCTCTATTTTATTTAGTCTTTCG +AGCATTATTTTGTATGTATCGCCACCATTAGGTGTTGTATTAATGTTTGTATTAATCATATTTAGTATTGAATTAATTGT +TGAATTTACAGGATTAATAGATAACAACTACCGACCAATATTAAATTTAATTAGTCGTAAGTCATCTCATAAAGAGGAAT +AGGGAATGAAAGCATAGCTGTATGGGATAATTTGTATTATATGGCTTTACTCTTTACAATTTTTTTGTATTAAATTTCAA +ATATAAAAAGCACTGCCATAAACGTGTACTTCAATTGTCGTTTAATAATACGCAATTGATATTTACCGTCTTATGATAGT +GCTTTTTATTTTTATTCAGTTGGTATATCGAAAGGTAACTGCTTTGGAGTTTCTTCAGTCAAATCGAAATTTCCTGCAGT +CATTTGATTTAAAAAGTTAATAAACGCTTCATAGTCACTTTTAACGACATCGATATAGTAGCTTACCTTATCAGTGTAAG +TTTGGTTTCTTAACATAAAATGAGTTGAAGCTAATTCATATTCAAATTTACCAGTTTGATCATAATTCAGTGTTACTATA +CATGGTACTGCTTCTCGTAGTTCGACACGCCCGATATCATAAATGACGTCTCTAACAGCACCGCTATAGGCGCGAATTAA +ACCGCCACCACCTAATTTAATACCACCAAAATATCTTGTTACTACGACACACGCATTATGAACATCGAGCTTTTTTAATA +TGTCTAACATTGGGACACCGGCAGTTCCTGTCGGTTCACCATCATCATTCGCTTTTTGAATATTCATTTCAGGTCCAATA +GTATATGCAGAACAATTATGAGTGGCATCTTTATGTTCTTTTTTTATTGCAGCAATAAATGCTTTAGCTTCATCTTCATT +TTGAACAGGTTTGATATGAGCAATGAATCTTGATTTACTAATCACATTTTCAATAATGTGTTCTTTTTTAACAGTAATGA +TATTTTGTGTCATAATAACTCCTTAATTCATAAGCTTAAGATTATTTAATCTTCATTATACACTGAAAATGACATGACTA +TAAATCGTTTGATTGCCATTTTCTTTTTAACTGAAATATTGTATCATTGCTATGAGTATATTTTAGGAGGACGACTATGA +AAATTGCTGTGATGACCGATTCTACAAGTTATCTGTCGCAGGACTTAATCGATAAATATAATATTCAAATAGCGCCATTA +AGTGTGACTTTTGAAGATGGCAAGATTATACCAGAAGAAAAAGTTCGTACTAAAAAGCGTGCCATTCAAACATTAGAAAA +GAAAGTATTAGATATTGTAAAAGACTTTGAAGAAGTAACTTTATTTGTCATAAATGGAGATCATTTCGAAGATGGTCAAG +CGTTATACAAAAAGTTACAAGATGATTGTCCTTCAGCTTATCAAGTAGCATACTCTGAGTTTGGTCCAGTTGTTGCAGCA +CATTTAGGTTCTGGTGGATTAGGTTTAGGCTATGTTGGCAGAAAAATAAGATTAACATAATTATAAAATTTTAATAAAAG +AGTCTATATTGTAATTGGAAATTATCTCTCGTATACATGGCTTTAAATGTTCATCATTTGAAAGCCAAAATGCTAAAGAT +ATAAGAAAATCATTATAATATTAGGCTCTTTTTTACGTTGAAATGAGGTTTTAAGCATTAAACATTACGGGAAATTAATT +CATCCTCATACTTCACTTACTAATGAAAAAATTAAAAAAGAAGTAACAGGTGTCATCAAACAAAATTCAAACTATTATTG +TGTTCAATGTGAAAGTACAAATCCAAAGCATTTTTATCAGTATGATTCCTCAGTACATTCCAAGAAAATTGTATATTGCA +GAAATTGTATATCACTGGGTCGAATGGATAATGTAACAAGATATAAAATAACAGAGAGTTCGCAAAGTTCATCACAAGCA +TATTATCATCTCTCATTTGAATTGTCGGAACAGCAGTCTTATGCCTCAGAACATATTGTTCGAGCCATTAGAAAGAGACA +AACGATTTTGTTATATGCCGTAACAGGTGCAGGTAAGACAGAAATGATGTTTCAAGGCATTCAATATGCAAGAATACAGG +GAGATAATATAGCTATTGTGTCACCACGTGTAGATGTTGTTGTAGAAATTAGTAAACGTATTAAAGACGCATTTCTTAAT +GAAGATATAGACATACTACACCAGCAATCAAGACAACAATTTGAAGGGCATTTTGTTGTATGCACAGTGCATCAACTTTA +CCGATTCAAACAGCACTTTGATACTATTTTTATTGATGAAGTCGATGCCTTTCCTTTATCAATGGATAAAAATTTACAAC +AAGCATTGAAGTCATCTTCTAAAGTTGAACATGCAACAATTTATATGACAGCAACACCACCGAAACAACTTCTGTCAGAG +ATTCCCCACGAAAATATAATTAAATTGCCAGCTCGCTTTCATAAAAAATCACTTCCAGTTCCTAAATATCGTTATTTCAA +ACTTAATAATAAGAAGATTCAGAAAATGTTATACCGAATTTTACAAGATCAAATTAATAATCAACGTTATACACTGGTGT +TTTTTAACAATATAGAAACAATGATTAAAACATTTTCGGTTTATAAGCAGAAAATTACTAAATTAACATACGTCCATAGC +GAGGATGTTTTTCGCTTTGAAAAAGTTGAACAATTAAGGAATGGACATTTCGATGTCATTTTTACTACGACAATATTAGA +ACGTGGATTTACAATGGCAAATTTGGATGTTGTTGTTATCGATGCACATCAATATACTCAAGAGGCTTTAATACAAATTG +CTGGACGTGTTGGACGAAAATTAGAATGTCCTACTGGAAAAGTATTGTTTTTTCATGAAGGGGTAAGTATGAATATGATT +CAAGCTAAAAAAGAGATTCAAAGGATGAACAAATTAGCATTAAAAAGAGGTTGGATTGATGAATAATTGTTTGAGTTGTG +GTGCTAAGTTATATGAAAATATAACCATTTATAATTTGTTCAAGAAACCTAATAGATTATGTGACAGATGCAAAGAGAAT +TGGGACAATATTAAACTTGATATTAAAGCAAGGCGATGTTCAAGGTGCTTAAAACACTTAAATCAAGATGAAGCGTATTG +TTTAGACTGCAAGTTTCTATCGGCACACTTTAATTTAATGGAACAATTATATTGTCAATTTCAATATGACGGTTTAATGA +AAGAGATGATACATCAGTATAAATTTTTGAAAGACTATTATTTATGTGAATTATTGGCACATTTGATTGAAATACCACAA +ACATCTTATGACTATATTGTGCCAATTCCTTCTTCGCCGGCACATGATTTATCTAGAACATTTAACCCGGTAGAAGCAGT +ACTAAAAGCTAAAGGGATTCGCTTTGATAAGATTTTAAAGATGTCAAATAGACCAAAACAGTCTCATTTAACTAAGAAAG +AGCGTCTGGCAGATGAAAATCCATTTATTATTGATACGGAATTAGATTTAAATGGTAAGGAAATATTACTCGTTGACGAT +ATTTATACAACTGGATTAACAATTCATCGTGCAGGGTGTAAATTATATGCTAAAAATATCAGAAAATTCAAAGTGTTTGC +GTTTGCACGATAGCGTAAAAATGTTAAAATATAATAAAGAGTTACCAATAAAGAGGTTTAAGGAGAGATTACTATGATTA +GATTTGAAATTCATGGAGATAACCTCACTATCACAGATGCTATTCGCAACTATATTGAGGAAAAAATTGGTAAGTTGGAA +CGTTATTTTAATGACGTACCAAATGCAGTGGCGCATGTTAAAGTTAAAACTTATTCAAATTCAGCTACTAAAATTGAAGT +AACAATTCCATTGAAAAATGTTACGTTAAGAGCTGAAGAGCGAAACGATGATTTATACGCAGGTATTGATTTAATTAATA +ATAAACTTGAAAGACAAGTTCGAAAATATAAAACACGTATTAATCGTAAGAGCCGTGATCGAGGAGATCAAGAAGTGTTT +GTTGCCGAATTACAAGAAATGCAAGAAACACAAGTTGATAATGACGCTTACGATGATAACGAGATAGAAATTATTCGTTC +AAAAGAATTCAGCTTAAAACCAATGGATTCAGAAGAAGCGGTATTACAAATGAATCTATTAGGTCATGACTTCTTTGTAT +TCACAGACAGAGAAACTGATGGAACAAGTATCGTTTACCGCCGTAAAGACGGTAAATATGGCTTGATTCAAACTAGTGAA +CAATAAATTAAGTTTAAAGCACTTGTGTTTTTGCACAAGTGCTTTTTTATACTCCAAAAGCAAATTATGACTATTTCATA +GTTCGATAATGTAATTTGTTGAATGAAACATAGTGACTATGCTAATGTTAATGGATGTATATATTTGAATGTTAAGTTAA +TAATAGTATGTCAGTCTATTGTATAGTCCGAGTCGAAAATCGTAAAATATTTATAATATAATTTATTAGGAAGTATAATT +GCGTATTGAGAATATATTTATTAGTGATAAACTTGTTGACAACAGAATGTGAATGAAGTATGTCATAAATATATTTATAT +TGATTCTACAAATGAGTAAATAAGTATAATTTTCTAACTATAAATGATAAGATATATTGTTGTAGGCCAAACAGTTTTTT +AGCTAAAGGAGCGAACGAAATGGGATTTTTATCAAAAATTCTTGATGGCAATAATAAAGAAATTAAACAGTTAGGTAAAC +TTGCTGATAAAGTAATCGCTTTAGAAGAAAAAACGGCAATTTTAACTGATGAAGAAATTCGTAATAAAACGAAACAATTC +CAAACAGAATTAGCTGACATTGATAATGTCAAAAAGCAAAATGATTATTTAGATAAAATTTTACCAGAAGCATATGCACT +TGTTAGAGAAGGCTCTAAACGTGTATTCAATATGACACCATATAAAGTTCAAATTATGGGTGGTATTGCAATTCATAAAG +GTGATATCGCTGAGATGAGAACAGGTGAAGGTAAAACATTAACAGCGACAATGCCAACATACTTAAATGCATTAGCTGGT +AGAGGTGTTCACGTTATTACAGTCAATGAATACTTATCAAGTGTTCAAAGTGAAGAAATGGCTGAGTTATATAACTTCTT +AGGTTTGACTGTCGGATTAAACTTAAACAGTAAGACGACAGAAGAAAAACGTGAAGCATACGCACAAGACATTACTTACA +GTACTAATAATGAGCTAGGTTTTGATTACTTACGAGATAACATGGTGAATTATTCTGAAGATAGAGTAATGCGTCCATTA +CATTTTGCAATCATTGATGAGGTTGACTCAATTTTAATCGACGAGGCACGTACGCCATTAATTATTTCTGGTGAAGCTGA +AAAGTCAACGTCACTTTATACACAAGCAAATGTTTTTGCGAAAATGTTAAAACAGGACGAAGATTATAAATACGATGAAA +AAACGAAAGCTGTACATTTAACAGAACAAGGTGCGGATAAAGCTGAACGTATGTTCAAAGTTGAAAACTTATATGATGTA +CAAAATGTTGATGTTATTAGTCATATCAACACAGCTTTACGTGCGCACGTTACATTACAACGTGACGTAGACTATATGGT +TGTTGATGGCGAAGTATTAATTGTCGATCAATTTACAGGACGTACAATGCCAGGCCGTCGTTTCTCGGAAGGTTTACACC +AAGCTATTGAAGCGAAGGAAGGCGTTCAAATTCAAAATGAATCTAAAACTATGGCGTCTATTACATTCCAAAACTATTTC +AGAATGTACAATAAACTTGCGGGTATGACAGGTACAGCTAAAACTGAAGAAGAAGAATTTAGAAATATTTATAACATGAC +AGTAACTCAAATTCCGACAAATAAACCTGTGCAACGTAACGATAAGTCTGATTTAATTTACATTAGCCAAAAAGGTAAAT +TTGATGCAGTAGTAGAAGATGTTGTTGAAAAACACAAGGCAGGGCAACCAGTGCTATTAGGTACTGTTGCAGTTGAGACT +TCTGAATATATTTCAAATTTACTTAAAAAACGTGGTATCCGTCATGATGTGTTAAATGCGAAAAATCATGAACGTGAAGC +TGAAATTGTTGCAGGCGCTGGACAAAAAGGTGCCGTTACTATTGCCACTAACATGGCTGGTCGTGGTACAGATATCAAAT +TAGGTGAAGGCGTAGAGGAATTAGGCGGTTTAGCAGTAATAGGTACAGAGCGACATGAATCTCGTCGTATTGATGACCAG +TTACGTGGTCGTTCTGGACGTCAAGGTGATAAAGGGGATAGTCGCTTCTATTTATCATTACAAGATGAATTAATGATTCG +TTTTGGTTCTGAACGTTTACAGAAAATGATGAGCCGACTAGGTTTAGATGACTCTACACCAATTGAATCAAAAATGGTAT +CAAGAGCTGTAGAATCAGCACAAAAACGTGTAGAAGGTAATAACTTCGACGCGCGTAAACGTATCTTAGAATACGATGAA +GTATTACGTAAACAACGTGAAATTATCTATAACGAAAGAAATAGTATTATTGATGAAGAAGACAGCTCTCAAGTTGTAGA +TGCAATGCTACGTTCAACGTTACAACGTAGTATCAATTACTATATTAATACAGCAGATGACGAGCCTGAATATCAACCAT +TCATCGACTACATTAATGACATCTTCTTACAAGAAGGTGACATTACAGAGGATGATATCAAAGGTAAAGATGCTGAAGAT +ATTTTCGAAGTCGTTTGGGCTAAGATTGAAGCAGCATATCAAAGTCAAAAAGATATCTTAGAAGAACAAATGAATGAGTT +TGAGCGTATGATTTTACTTCGTTCTATTGATAGCCATTGGACTGATCATATCGACACAATGGATCAATTACGTCAAGGTA +TTCACTTACGTTCTTATGCACAACAAAATCCATTACGTGACTATCAAAATGAAGGTCATGAATTATTTGATATCATGATG +CAAAATATTGAAGAAGATACTTGTAAATTCATTTTAAAATCTGTAGTACAAGTTGAAGATAATATTGAACGTGAAAAAAC +AACAGAGTTTGGTGAAGCGAAGCACGTTTCAGCTGAAGATGGTAAAGAAAAAGTGAAACCGAAACCAATCGTTAAAGGCG +ATCAAGTTGGTCGTAACGATGATTGTCCATGTGGTAGTGGTAAAAAATTCAAAAATTGCCATGGAAAATAAATGATATAA +AATAACTCCTTCCAATTAAACACCTATAGTTTGTGTTATGGGAGGAGTCTTTTTATTTTACAAGCGTTAAATACTTTAAA +AAATGTGAAGAAGTTGTTAAACGTTGTTATGTACTTAGTTTTAAAAAATCGGTTTAGGCATATGTCGATGATAAATGTAC +TGATTTTTAACAATAAATGCATAAACTAATTGTCAGTGTGCTTATATTTCTTAACATTGTTATTTAACAAAATTATGTTA +AAATTTAGCATTATAAAAGATGCAAATCAATGACTTGAATTGAAATATAAATAGGAGCGAATGCTATGGAATTATCAGAA +ATCAAACGAAATATAGATAAGTATAATCAAGATTTAACACAAATTAGGGGGTCTCTTTGACTTAGAGAACAAAGAAACTA +ATATTCAAGAATATGAAGAAATGATGGCAGAACCTAATTTTTGGGATAACCAAACGAAAGCGCAAGATATTATAGATAAA +AATAATGCGTTAAAAGCAATAGTTAATGGTTATAAAACACTACAAGCAGAAGTAGATGACATGGATGCTACTTGGGATTT +ATTACAAGAAGAATTTGATGAAGAAATGAAAGAAGACTTAGAGCAAGAGGTCATTAATTTTAAGGCTAAAGTGGATGAAT +ACGAATTGCAATTATTATTAGATGGGCCTCACGATGCCAATAACGCAATTCTAGAGTTACATCCTGGTGCAGGTGGCACG +GAGTCTCAAGATTGGGCTAATATGCTATTTAGAATGTATCAACGTTATTGTGAGAAGAAAGGCTTTAAAGTTGAAACTGT +TGATTATCTACCTGGGGATGAAGCGGGGATTAAAAGTGTAACATTGCTCATCAAAGGGCATAATGCTTATGGTTATTTAA +AAGCTGAAAAAGGTGTACACCGACTAGTACGAATTTCTCCATTTGATTCATCAGGACGTCGTCATACATCATTTGCATCA +TGCGACGTTATTCCAGATTTTAATAATGATGAAATAGAGATTGAAATCAATCCGGATGATATTACAGTTGATACATTCAG +AGCTTCTGGTGCAGGTGGTCAGCATATTAACAAAACTGAATCGGCAATACGAATTACCCACCACCCCTCAGGTATAGTTG +TTAATAACCAAAATGAACGTTCTCAAATTAAAAACCGTGAAGCAGCTATGAAAATGTTAAAGTCTAAATTATATCAATTA +AAATTGGAAGAGCAGGCACGTGAAATGGCTGAAATTCGTGGCGAACAAAAAGAAATCGGCTGGGGAAGCCAAATTAGATC +ATATGTTTTCCATCCATACTCAATGGTGAAAGATCATCGTACGAACGAAGAAACAGGTAAGGTTGATGCAGTGATGGATG +GAGACATTGGACCATTTATCGAATCATATTTAAGACAGACAATGTCGCACGATTAATATATATTTTAAAACCGAGGCTCT +AAAAGGGCGTCGGTTTTTGGTTTTTTTAAAGGTAGCTAAATAAATTGTAAATTAGATTTTGGAATATGATTTGTTTATGA +ATATTTAAGTACAATTCGGTAGATAGAGTTAGAATATATTTTTTAAAAGTTGTGTTTGTTAAAACAACATATGCAGTGTG +CATTTAGTAATATTACCTATGGCGATTTTCAAGGTATTGAATTAATTATTGAAAACGTTCTCAATTACATGGTATGAATA +CATTTTACACTATGATAAAAGGTTGTATTCTTTTTATATTGTTAACCATTTGATTACATCGTTATAACAATAGCTTTTGA +CAAAATGTATTGTGCTATAGTATTTGCATACTTAAAATACTAACAGCAAAGGAATGACAGCAAGATGAAAAAATCTCTTA +CAGTGACGGTTTCGTCAGTGTTAGCTTTTTTAGCTTTAAATAATGCAGCACATGCACAACAACATGGCACACAAGTAAAA +ACACCTGTTCAACATAATTATGTATCAAATGTTCAAGCACAAACGCAATCACCGACAACTTATACAGTAGTTGCTGGCGA +TTCATTATATAAGATTGCTTTAGAGCATCACTTAACGTTGAATCAATTATATTCATACAATCCTGGTGTAACACCTTTAA +TTTTTCCTGGTGACGTGATTTCACTTGTGCCTCAAAATAAAGTGAAACAAACTAAAGCGGTTAAATCACCAGTAAGAAAA +GCAAGCCAAGCTAAAAAGGTAGTAAAACAACCTGTACAACAAGCATCTAAAAAAGTAGTAGTTAAGCAAGCACCTAAGCA +AGCAGTAACTAAGACAGTTAATGTAGCATACAAACCTGCTCAAGTACAAAAATCAGTACCAACTGTACCTGTTGCACATA +ACTACAATAAATCAGTTGCTAACAGAGGAAACTTATATGCTTATGGAAACTGCACATATTATGCTTTCGATCGTCGTGCA +CAATTAGGTAGAAGTATAGGAAGTTTATGGGGCAATGCAAATAACTGGAATTACGCAGCAAAAGTTGCAGGATTTAAAGT +AGATAAAACACCAGAAGTTGGCGCTATTTTCCAAACAGCTGCTGGTCCATATGGACATGTTGGTGTTGTTGAATCTGTAA +ACCCTAATGGAACAATTACTGTTTCTGAAATGAACTATGCTGGATTTAATGTTAAATCTTCAAGAACAATTTTAAATCCA +GGAAAATATAATTACATCCACTAAGTAATATATCAAGACAAGACTATCCTCTTAGCCTGTTTAAGTAACAGGTTGAGAGG +ATTTTTTGGTATCATTTAATCAGAGTTATATAAAGAAGATATTTAAATGATATTTAATTGATTGATATGTAAAAAGAAAG +TATAATTTATGATTAATTAATGGAGGAGGTAATTGAAATGGGTGTACATCAATATTTTAAAAGATTATCAGATATGGAAA +GACTTATAAGATTACCTGGAAAATTTAAATATTTTGAACACAATGTTGCAGCACACTCCTTTAAAGTAACTAAAATTGCA +CAATATCTAGCAACAGTTGAAGAATATCATGGACGAAAGATTAATTGGAAAAGCTTATATGAAAAAGCATTAAATCATGA +TTTCGCCGAAGTGTTTACTGGTGATATAAAAACACCTGTTAAATATGCGAGTAGTGAATTAAAAAAATTATTTTCGCAAG +TTGAAGAAGAAATGGTAGAGACCTTTATTGAAGAAGAAATTCCATTACAATATAGAGATGTTTATAAGCAACGACTGCAA +GAAGGTAAAGATGATTCATTAGAAGGCCAAATACTTTCAGTTGCTGATAAAATTGATTTGCTTTATGAAACATTTGGAGA +AATACAAAAACGTAATCCCGAAGAATTATTTTTCGAAATTTATGAAATGAGTCTAGAAACAATTATTCAATTTGACCATT +TAGCATCTGTACAAGATTTTATTAATAATATCATTCCAGAAATGTTGACTGAAAACTTTATACCTAGAACAGAATTAAGA +GAAACAACCATGAACATTTTAAATAAAAGAAAAGAGGAAAATGAATGATATGGTATTTTAGCGCAGCATTCTTTCCATGT +GTCTTGGTAGTATTATTTAGTGTAATAACAAGAAGTAAATGGGTCGGTACTATTCTGACATTAATTTTAATTGGTGCCTC +AATCTATAAAGAGTATTTCCATAACGAGTGGATTATTTTTATTGATGTAGTGTCATTATTAGCTGGTTATTTAATTATAG +ATCAACTCGAATTTCATAAACATCAAGATGAAGATCGCTAAGATTAACTTTAAAATAATGTTTCAAACAAATTTGTTGAA +ACAAAATGATGATTAATATAATGTGTATTTACATACTAAAAATAACAAGATAAACGATTTGATTTAAGGCAAGCATAGTT +AGCACAACTATGTTTGTTTTTCTTTGTTCGACATTTTTACGAACAAACGTTTGCTTTTTGTGTGACTACTTTGCTAAAAT +ATGTAATGAGAAAGCAAAGAGTTGAGCGGAAATATAAATAATACGTTGAAAGAGGAGGCATATGTGACAATGGTTGAACA +TTATCCTTTTAAAATACATTCTGATTTTGAGCCTCAAGGTGACCAACCGCAAGCAATTAAAGAAATCGTGGAAGGTATTA +AAGCGGGGAAAAGACATCAAACTTTATTAGGTGCTACTGGCACAGGGAAAACATTTACGATGAGTAATGTTATTAAAGAA +GTTGGGAAACCAACGTTAATTATCGCGCATAACAAAACATTAGCAGGACAATTATATAGTGAGTTTAAAGAATTTTTTCC +TGAAAACAGGGTGGAATACTTTGTAAGTTACTATGATTATTATCAACCAGAGGCATACGTACCGTCTACTGACACTTTTA +TTGAAAAAGATGCCTCAATCAATGATGAAATTGATCAACTACGACATTCTGCTACAAGTGCATTATTTGAACGCGATGAT +GTAATTATTATTGCTAGTGTAAGTTGTATATATGGTTTAGGTAATCCTGAAGAATATAAAGATTTAGTAGTAAGTGTTCG +AGTTGGTATGGAAATGGATAGAAGTGAATTACTTAGAAAACTTGTAGATGTGCAATATACACGAAATGACATCGATTTCC +AACGAGGAACGTTTCGAGTGCGTGGTGATGTAGTGGAAATATTCCCAGCCTCTAAAGAAGAACTTTGTATAAGGGTTGAG +TTTTTCGGCGATGAGATTGACCGTATCCGAGAAGTTAACTACCTAACAGGTGAAGTGTTGAAAGAAAGAGAACATTTTGC +GATATTCCCAGCTTCTCACTTCGTAACACGTGAAGAAAAGTTGAAAGTTGCGATTGAACGTATTGAAAAAGAATTGGAAG +AACGATTGAAAGAATTACGAGATGAGAATAAATTACTAGAAGCGCAAAGGTTAGAACAGCGTACCAACTATGATTTAGAA +ATGATGCGAGAGATGGGATTCTGTTCAGGAATTGAAAACTATTCCGTACATTTAACTTTGCGACCACTGGGTTCGACACC +ATATACTTTATTGGATTACTTTGGCGATGATTGGTTAGGGAAGGTGCGAACAAGTCCCTGATATGAGATCATGTTTGTCA +TCTGGAGCCATAGAACAGGGTTCATCATGAGTCATCAACTTACCTTCGCCGACAGTGAATTCAGCAGTAAGCGCCGTCAG +ACCAGAAAAGAGATTTTCTTGTCCCGCATGGAGCAGATTCTGCCATGGCAAAACATGGTGGAAGTCATCGAGCCGTTTTA +CCCCAAGGCTGGTAATGGCCGGCGACCTTATCCGCTGGAAACCATGCTACGCATTCACTGCATGCAGCATTGGTACAACC +TGAGCGATGGCGCGATGGAAGATGCTCTGTACGAAATCGCCTCCATGCGTCTGTTTGCCCGGTTATCCCTGGATAGCGCC +TTGCCGGACCGCACCACCATCATGAATTTCCGCCACCTGCTGGAGCAGCATCAACTGGCCCGCCAATTGTTCAAGACCAT +CAATCGCTGGCTGGCCGAAGCAGGCGTCATGATGACTCAAGGCACCTTGGTCGATGCCACCATCATTGAGGCACCCAGCT +CGACCAAGAACAAAGAGCAGCAACGCGATCCGGAGATGCATCAGACCAAGAAAGGCAATCAGTGGCACTTTGGCATGAAG +GCCCACATTGGTGTCGATGCCAAGAGTGGCCTGACCCACAGCCTGGTCACCACCGCGGCCAACGAGCATGACCTCAATCA +GCTGGGTAATCTGCTGCATGGAGAGGAGCAATTTGTCTCAGCCGATGCCGGCTACCAAGGGGCGCCACAGCGCGAGGAGC +TGGCCGAGGTGGATGTGGACTGGCTGATCGCCGAGCGCCCCGGCAAGGTAAGAACCTTGAAACAGCATCCACGCAAGAAC +AAAACGGCCATCAACATCGAATACATGAAAGCCAGCATCCGGGCCAGGGTGGAGCACCCATTTCGCATCATCAAGCGACA +GTTCGGCTTCGTGAAAGCCAGATACAAGGGGTTGCTGAAAAACGATAACCAACTGGCGATGTTATTCACGCTGGCCAACC +TGTTTCGGGCGGACCAAATGATACGTCAGTGGGAGAGATCTCACTAAAAACTGGGGATAACGCCTTAAATGGCGAAGAAA +CGGTCTAAATAGGCTGATTCAAGGCATTTACGGGAGAAAAAATCGGCTCAAACATGAAGAAATGAAATGACTGAGTCAGC +CGAGAAGAATTTCCCCGCTTATTCGCACCTTCCTTAGTAATGATTGATGAATCACATGTGACATTACCGCAAGTTCGAGG +CATGTATAACGGAGACAGAGCGCGTAAACAAGTTTTGGTGGATCATGGGTTTAGATTACCGAGTGCATTAGATAACCGTC +CACTTAAATTTGAAGAATTTGAAGAAAAGACAAAACAACTTGTGTATGTATCTGCAACGCCTGGACCATACGAAATTGAA +CATACGGATAAGATGGTTGAACAAATTATTCGTCCTACTGGTTTACTGGATCCTAAGATTGAGGTTAGACCTACTGAAAA +TCAAATTGACGATTTATTAAGTGAAATTCAAACAAGAGTTGAGCGTAATGAACGCGTACTTGTTACAACGCTCACTAAAA +AGATGAGTGAAGATTTAACCACATACATGAAAGAAGCGGGTATTAAAGTTAATTATCTGCATTCAGAAATCAAGACATTA +GAACGAATTGAAATAATTAGAGACTTACGAATGGGTACATATGATGTTATCGTAGGTATTAATTTATTAAGAGAGGGTAT +TGATATACCAGAAGTTTCTCTAGTTGTCATATTAGATGCAGATAAAGAAGGGTTTTTACGTTCTAACCGCTCATTAATTC +AAACAATAGGTAGAGCTGCGCGTAACGATAAAGGTGAAGTCATTATGTATGCCGATAAAATGACTGATTCGATGAAGTAT +GCAATTGATGAGACACAACGTCGTCGAGAAATACAGATGAAACATAATGAAAAACATGGTATTACACCTAAAACAATTAA +TAAAAAAATACATGATTTAATTAGTGCTACTGTTGAAAATGACGAAAATAATGACAAAGCACAAACTGTGATACCTAAGA +AGATGACGAAAAAAGAACGTCAAAAGACAATCGACAATATAGAAAAAGAAATGAAACAAGCAGCGAAAGATTTAGATTTC +GAGAAAGCTACAGAATTAAGAGATATGTTATTTGAATTAAAAGCAGAAGGGTGACAAGTAAATGAAAGAACCATCCATAG +TAGTAAAAGGTGCTCGTGCGCATAACTTGAAAGATATTGATATCGAACTACCTAAAAATAAATTAATTGTTATGACAGGT +TTATCTGGGTCAGGTAAATCGTCATTAGCATTCGATACTATATATGCTGAAGGACAACGACGTTATGTTGAATCATTAAG +TGCCTATGCGCGTCAATTTTTAGGCCAAATGGACAAACCAGATGTTGATACAATTGAAGGATTATCGCCAGCAATTTCAA +TAGATCAAAAAACAACAAGTAAAAATCCAAGATCAACTGTAGCAACAGTAACAGAAATATATGATTATATACGTTTGTTA +TATGCACGTGTTGGTAAACCTTACTGTCCAAATCACAATATAGAAATTGAATCGCAAACAGTACAACAAATGGTTGACCG +CATTATGGAATTAGAGGCACGTACAAAGATTCAATTATTAGCACCTGTCATCGCTCATCGTAAAGGTAGTCATGAAAAGC +TAATCGAAGATATTGGTAAAAAAGGTTATGTACGTTTAAGAATCGATGGCGAAATTGTTGATGTAAATGATGTACCTACT +TTAGATAAGAACAAGAATCATACAATAGAAGTTGTTGTAGACCGATTAGTTGTTAAAGATGGAATTGAAACACGACTAGC +TGACTCTATAGAAACTGCCTTAGAGCTTTCAGAAGGACAATTAACAGTCGATGTCATTGACGGGGAAGACCTTAAGTTTT +CAGAAAGCCATGCTTGTCCTATATGTGGATTTTCAATCGGAGAGTTAGAACCAAGAATGTTTAGCTTTAACAGTCCTTTT +GGTGCTTGTCCGACATGTGATGGCTTAGGCCAAAAGTTAACAGTCGATGTAGACTTGGTTGTTCCCGACAAAGATAAGAC +GCTAAACGAAGGTGCAATAGAACCTTGGATACCGACGAGTTCTGATTTTTATCCAACATTGTTAAAACGTGTTTGTGAAG +TTTATAAAATCAATATGGATAAACCTTTTAAAAAGTTAACAGAACGTCAACGTGATATTTTATTGTATGGTTCTGGTGAC +AAAGAAATTGAATTTACATTTACACAACGTCAAGGTGGTACTAGAAAACGAACAATGGTTTTCGAGGGTGTAGTTCCTAA +TATAAGTAGACGATTCCATGAATCTCCTTCAGAATATACACGTGAAATGATGAGTAAATATATGACTGAACTACCTTGCG +AAACTTGTCATGGAAAGCGATTGAGTCGTGAAGCGTTATCTGTTTATGTAGGTGGTTTAAATATTGGTGAAGTAGTCGAA +TATTCAATCAGTCAAGCGCTGAACTATTATAAAAACATTGATTTGTCAGAACAAGATCAAGCGATTGCAAATCAAATATT +GAAAGAAATTATTTCCCGACTCACTTTTTTAAATAATGTGGGACTTGAATATTTAACGTTAAACAGAGCTTCAGGTACAC +TTTCAGGTGGTGAAGCACAACGTATTCGATTAGCAACGCAAATTGGGTCGCGTTTGACTGGTGTCTTATATGTATTAGAT +GAGCCATCAATTGGACTGCATCAAAGAGATAATGATCGATTAATTAATACACTTAAAGAAATGAGAGATTTAGGAAATAC +TTTAATTGTAGTTGAACACGATGATGATACAATGCGTGCGGCTGATTACTTAGTGGATATAGGTCCTGGTGCTGGTGAAC +ATGGAGGGCAGATTGTGTCTAGTGGTACTCCTCAAAAGGTAATGAAAGATAAAAAATCATTAACAGGACAATACTTGAGT +GGTAAGAAACGTATTGAAGTACCTGAATATCGCAGACCGGCTTCAGATCGTAAAATTTCTATACGTGGAGCTAGAAGCAA +CAATCTTAAAGGGGTTGATGTGGACATACCACTATCAATCATGACGGTTGTTACAGGTGTATCAGGTTCTGGTAAAAGCT +CATTAGTAAATGAAGTATTATACAAATCATTAGCTCAAAAAATTAATAAATCTAAAGTAAAGCCAGGATTGTACGATAAG +ATTGAAGGTATTGATCAACTTGATAAAATTATTGATATTGATCAATCACCAATAGGTAGAACGCCACGCTCTAATCCAGC +AACATATACTGGTGTGTTTGATGATATACGTGATGTGTTTGCGCAAACAAATGAAGCTAAAATTCGAGGATATCAAAAAG +GGCGTTTTAGTTTTAATGTAAAAGGTGGACGCTGTGAAGCTTGTAAAGGTGACGGTATTATTAAAATTGAAATGCATTTT +TTACCTGATGTTTATGTTCCTTGTGAAGTGTGTGATGGTAAACGATATAATCGTGAGACACTAGAGGTTACTTACAAAGG +TAAAAATATTGCTGACATTTTAGAAATGACTGTTGAAGAAGCAACACAATTTTTTGAAAATATTCCTAAGATTAAGCGCA +AGTTACAAACACTAGTTGATGTTGGTCTTGGATACGTCACATTAGGTCAACAAGCTACAACGTTATCAGGTGGTGAGGCT +CAACGTGTGAAACTTGCATCTGAACTTCATAAACGTTCAACTGGTAAATCTATTTATATCCTAGATGAACCGACAACAGG +GTTACATGTTGACGATATTAGTAGATTATTAAAAGTATTAAACCGATTAGTTGAAAATGGTGATACTGTTGTAATTATTG +AACATAACCTAGATGTTATCAAAACAGCAGACTATATTATAGACTTAGGTCCTGAAGGTGGTAGTGGCGGTGGTACTATT +GTTGCGACTGGCACACCCGAAGATATTGCTCAGACAAAGTCATCATATACAGGAAAGTATTTAAAAGAAGTACTTGAACG +AGATAAACAAAATACTGAAGATAAATAAGATTAAAAGAAGTGAAGGATGTTATAAATTTATCCTTCGCTTCTTTTTATTA +ATTTAGTAATGAATAGTAGAAAGAAAAGATGCGTAAAAAGAATTATGTTAAGATAGGGTCAATCTAGAGTAGTTAAACAT +AAATCGAACTGGGAGTGGGACAGAAATGATAAAGAATCACTAATGATTTATTATGTAGTGGTTCTTTGTCATTAGCCACA +GCTATTGTGTACTTAAAAATAGGAATGCATGAGTGCAACTCATGCATAAGAAATACTAATTTCTAAAGAAAAAGTATTTC +TTTATGTTGGGGCCCCGCCAACTTGCATTGTTTGTAGAATTTCTTTTCGAAATTCTTTATGTTGGGGCCCCGCCAACTTG +CATTGTTTGTAGAATTTCTTTTCGAAATTCTTTATGTTGGGGCCCCGCCAACTAATTCCAATATATCATTGTAGAGCTTA +GGTCATTGATTTTTGGCTCGGACTTTTATGGCGATATGAACCATGTAAATTAAGCAAGCAATAAATTAATGATTGATATT +GACTTGTAAAATAATAACAATAATGAACAATTAATATTTATTTTAGCTTTTCAATGTAGATTGGTGTTATATTTTTGATA +TGATAAGAAGAGATGTAAGAGTAGGGATAAATACAATTGAGGTGAACCCATGTTAACGACAGAAAAACTAGTTGAAACAT +TAAAGTTAGATTTAATCGCTGGTGAAGAAGGACTATCGAAGCCAATTAAAAATGCTGATATATCAAGACCGGGCTTAGAG +ATGGCAGGTTATTTTTCACATTATGCGTCAGATAGAATACAACTATTAGGAACAACGGAACTATCGTTTTACAATTTATT +ACCAGATAAGGATCGCGCAGGTCGTATGCGTAAACTATGCAGACCAGAAACGCCTGCAATTATTGTGACACGTGGATTGC +AGCCACCAGAAGAATTAGTTGAAGCTGCAAAAGAATTAAATACCCCACTTATAGTTGCTAAAGATGCGACTACAAGTTTA +ATGAGTCGCTTAACAACGTTTTTAGAGCATGCACTTGCAAAGACGACATCTTTACATGGTGTTTTAGTAGATGTTTACGG +TGTTGGTGTACTAATTACCGGTGATTCAGGAATAGGTAAAAGTGAGACTGCGTTGGAATTAGTTAAACGTGGGCATAGAT +TAGTAGCAGATGATAATGTAGAAATACGTCAAATTAATAAAGATGAACTAATAGGGAAACCACCAAAGTTAATAGAACAT +CTATTAGAAATACGTGGACTAGGTATTATCAATGTTATGACTTTATTTGGCGCGGGTTCAATATTAACTGAAAAACGAAT +TAGATTAAATATTAATTTGGAAAACTGGAACAAGCAAAAGTTATATGACCGCGTAGGTCTTAATGAAGAGACGCTAAGTA +TTTTAGATACTGAAATCACTAAAAAAACAATACCTGTAAGACCTGGTAGAAATGTTGCGGTAATTATTGAGGTCGCTGCA +ATGAACTATCGATTAAATATCATGGGCATTAACACGGCCGAAGAATTTAGTGAAAGATTAAATGAAGAAATTATCAAGAA +CAGTCATAAGAGTGAGGAGTAGGTTGAATGGGTATTGTATTTAACTATATAGATCCTGTGGCATTTAACTTAGGACCACT +GAGTGTACGATGGTATGGAATTATCATTGCTGTCGGAATATTACTTGGTTACTTTGTTGCACAACGTGCACTAGTTAAAG +CAGGATTACATAAAGATACTTTAGTAGATATTATTTTTTATAGTGCACTATTTGGATTTATCGCGGCACGAATCTATTTT +GTGATTTTCCAATGGCCATATTACGCGGAAAATCCAAGTGAAATTATTAAAATATGGCATGGTGGAATAGCAATACATGG +TGGTTTAATAGGTGGCTTTATTGCTGGTGTTATTGTATGTAAAGTGAAAAATTTAAACCCATTTCAAATTGGTGATATCG +TTGCGCCAAGTATAATTTTAGCGCAAGGAATTGGACGCTGGGGTAACTTTATGAATCACGAGGCACATGGTGGATCGGTG +TCACGCGCTTTTTTAGAACAATTACATTTGCCTAATTTTATAATAGAAAATATGTATATTAACGGCCAATATTATCATCC +AACATTCTTATATGAATCCATTTGGGATGTCGCTGGATTTATTATCTTAGTTAATATTCGTAAACATTTAAAATTAGGAG +AAACATTCTTTTTATATTTAACTTGGTATTCAATTGGTCGATTCTTTATAGAAGGATTACGTACAGATAGCTTAATGCTC +ACAAGTAATATTAGAGTTGCACAATTAGTATCAATTCTTTTAATTTTAATAAGTATAAGTTTAATTGTATATAGAAGGAT +TAAGTATAATCCACCGTTGTATAGCAAAGTTGGGGCGCTTCCATGGCCAACAAAAAAAGTGAAGTAGTGATAGTTTGAGG +AAATTTTTATCAAAAACACATCATCATACAAACCCTTTATGGCGTGTATACCGTCTTGTTAAATTTTCGAAAGTTTTTAA +GAATGTAATTATCATTGAATTTTCGAAATTTATTCCAAGTATGGTACTGAAAAGACATATATATAAACAACTTTTAAATA +TTAATATCGGTAATCAATCGTCGATAGCTTATAAAGTAATGTTAGATATTTTTTACCCAGAACTGATTACGATTGGTAGT +AACAGTGTTATTGGTTACAATGTAACAATTTTGACGCATGAAGCATTAGTTGATGAATTTCGTTATGGACCAGTGACGAT +AGGATCTAACACTTTGATTGGTGCAAATGCTACCATTTTACCCGGTATAACGATTGGTGACAATGTAAAAGTTGCAGCTG +GTACGGTTGTTTCAAAAGATATACCGGATAATGGATTTGCATATGGCAACCCTATGTATATAAAAATGATTAGGAGGTGA +CAATTTTATGGCGCAAAAGAATAATAATGTAATTCCAATGACTTTTGATGATGCATTTTATCGTAAAATGGCTAAACAGA +AGTTTAAACAAAGAGAATATAAACGAGCTGCTGAATACTTTGAAAAAGTGTTAGAATTGTCACCTGATGATCTGGAAATT +CAAATTGATTATGCACAATGTCTAGTGCAACTTGGTATTGCTAAAAAAGCAGAACATTTATTTTATGACAATATTATTTA +TAATAGGCATCTAGAAGATAGCTTTTATGAATTGAGTCAGCTCAACATTGAAGTTAACGAACCAAACAAGGCATTCTTGT +TTGGTATTAATTATGTTATTGTTAGCGACGACCAAGATTATAGAGATGAATTAGATCAAATGTTTGATGTGAAATATCAA +AGTGAAGAACAAATTGAACTTGAAGCTCAATTGTTTGTAGTTCAAATACTATTCCAATATCTTTTTTCTCAAGGTCGATT +AAAAGATGCAAAGAATTATGTCTTACATCAACCACAAGAAGTTCAAGATCATCGTGTAGTACGTAATTTATTGGCAATGT +GTTATTTATATCTCGGTGAATATGATACGGCTAAAGCATTGTACGAAGCACTATTACAAGAGGATAGTACAGATATATAT +GCATTATGCCATTATACTTTGCTACTTTATAACACTAAGGAAAATGAACAATATCAAAAATATTTAAAAATATTAAACAA +AGTTGTACCTATGAATGACGATGAAAGTTTTAAATTAGGTATTGTATTAAGTTATTTAAAGCAGTATCGTGCATCACAAC +AATTGTTGTACCCTTTATATAAAAAAGGGAAATTTTTATCAATTCAAATGTACAATGCTTTAGCATATAATTATTATTAT +TTAGGTGAAGAAGACGAAAGTCATTACTACTGGGATAAATTGAAGCAAATTTCTAAAGTGGAAATTGGACATGCGCCTTG +GGTAATTGAAAATAGCAAAGAAGTTTTTGACCAACATATTTTGCCATTACTTCAAAGTGATGACAGTCATTATCGTTTAT +ATGGTATTTTTTTATTGGATCAATTAAATGGTAAAGAAATTGTGATGACGGAAAGTATTTGGCAGGTTTTGGAAAATCTA +AATAATTATGAGAAATTGTATTTAACGTATTTAGTTCAAGGTTTAACGCTCAATAAATTAGACTTCATTCATCGCGGCTT +ATTAACGCTTTACCATAATGAATTATTTGTAAGTGAAAATGATGTAATGGTTGCATGGATTAATCAAGGTGAACTCATAA +TTGCTGAAAAAGTAGATTTAACTGATGTTGAGCCATATATCGGTGCGTTTATTTATTTGTATTTTAAAAATCAACCTCGA +AACGTTACAAAGAAGCAAATTACAACATGGTTAGGCATAACACAATATAAACTGAACAAAATGATTGAATTTCTCTTGAG +CATATAGATTTATGAAAAGTTAGATTTATTATATAATGCGCATAATGATTAATAATGAGGAGGCGTTAATAAAATGACTG +AAATAGATTTTGATATAGCAATTATCGGTGCAGGTCCAGCTGGTATGACTGCTGCAGTATACGCATCACGTGCTAATTTA +AAAACAGTTATGATTGAAAGAGGTATTCCAGGCGGTCAAATGGCTAATACAGAAGAAGTAGAGAACTTCCCTGGTTTCGA +AATGATTACAGGTCCAGATTTATCTACAAAAATGTTTGAACACGCTAAAAAGTTTGGTGCAGTTTATCAATATGGAGATA +TTAAATCTGTAGAAGATAAAGGCGAATATAAAGTGATTAACTTTGGTAATAAAGAATTAACAGCGAAAGCGGTTATTATT +GCTACAGGTGCAGAATACAAGAAAATTGGTGTTCCGGGTGAACAAGAACTTGGTGGACGCGGTGTAAGTTATTGTGCAGT +ATGTGATGGTGCATTCTTTAAAAATAAACGCCTATTCGTTATCGGTGGTGGTGATTCAGCAGTAGAAGAGGGAACATTCT +TAACTAAATTTGCTGACAAAGTAACAATCGTTCACCGTCGTGATGAGTTACGTGCACAGCGTATTTTACAAGATAGAGCA +TTCAAAAATGATAAAATCGACTTTATTTGGAGTCATACTTTGAAATCAATTAATGAAAAAGACGGCAAAGTGGGTTCTGT +GACATTAACGTCTACAAAAGATGGTTCAGAAGAAACACACGAGGCTGATGGTGTATTCATCTATATTGGTATGAAACCAT +TAACAGCGCCATTTAAAGACTTAGGTATTACAAATGATGTTGGTTATATTGTAACAAAAGATGATATGACAACATCAGTA +CCAGGTATTTTTGCAGCAGGAGATGTTCGCGACAAAGGTTTACGCCAAATTGTCACTGCTACTGGCGATGGTAGTATTGC +AGCGCAAAGTGCAGCGGAATATATTGAACATTTAAACGATCAAGCTTAATTCGAAGTCGAATTAAGATGTTGAGCTGTAA +ATTATTTGGATATTTATTTTAATAGTGTCATCACAGCGTTAAAATAATGTCTTACTTTTAAATTAAAGCAAATTATATAG +AAAACTAGAACTTAGTACGTATCATTTGTGCGTTTCAATGAGTTCTAGTTTTTTTATATGTTATATTAAACTTATAACTT +TATGGGAGTGGGACAGAAATGATAAAGAGCCACTAATGATTTATTATGTAGTGGTTCTTAAACATTAGCCACAGCTAATG +TGTACTTAAAAATAGGAATACATGAGTAAAACTCATGCATAAGAAATACTAATTTCTATAGAAAAAGTATTACTTTATCG +TTGTCCCACCCCAACTTGCACATTATTGTAAGCTGACTTTCCGCCAGCTTCTGTGTTGGGGCCCCGCCAACTTGCACATT +ATTGTAAGCTGACTTTTCGTCAGCTTCTGTGTTGGGGCCCCGCCAACTTGCACATTATTGTAAGCTGACTTTTCGTCAGC +TTCTGTGTTGGGGCCCCGCCAACTTGCATTGTCTGTAGAAATTGGGAATCCAATTTCTCTATGTTGGGGCCCACACCCCA +ACTCGCATTGCCTGTAGAATTTCTTTTCGAAATTCTCTGTGTTGGGGCCCACACCCCAACTCGCATTGCCTGTAGAATTT +CTTTTCGAAATTCTCTGTGTTGGGGCCCACACCCCAACTCGCATTGCCTGTAGAATTTCTTTTCGAAATTCTCTGTGTTG +GGGCCCCTGACTAGAGTTGAAAAAAGCTTGTTGCAAGCGCATTTTCATTCAGTCAACTACTAGCAATATAATATTATAGA +CCCTAGGACATTGATTTATGTCCCAAGCTCCTTTTAAATGATGTATATTTTTAGAAATTTAATCTAGACATAGTTGGAAA +TAAATATAAAACATCGTTGCTTAATTTTGTCATAGAACATTTAAATTAACATCATGAAATTCGTTTTGGCGGTGAAAAAA +TAATGGATAATAATGAAAAAGAAAAAAGTAAAAGTGAACTATTAGTTGTAACAGGTTTATCTGGCGCAGGTAAATCTTTG +GTTATTCAATGTTTAGAAGACATGGGATATTTTTGTGTAGATAATCTACCACCAGTGTTATTGCCTAAATTTGTAGAGTT +GATGGAACAAGGAAATCCATCCTTAAGAAAAGTGGCAATTGCAATTGATTTAAGAGGTAAGGAACTATTTAATTCATTAG +TTGCAGTAGTGGATAAAGTCAAAAGTGAAAGTGACGTCATCATTGATGTTATGTTTTTAGAAGCAAGTACTGAAAAATTA +ATTTCAAGATATAAGGAAACGCGTCGTGCACATCCTTTGATGGAACAAGGTAAAAGATCGTTAATCAATGCAATTAATGA +TGAGCGAGAGCATTTGTCTCAAATTAGAAGTATAGCTAATTTTGTTATAGATACTACAAAGTTATCACCTAAAGAATTAA +AAGAACGCATTCGTCGATACTATGAAGATGAAGAGTTTGAAACTTTTACAATTAATGTCACAAGTTTCGGTTTTAAACAT +GGGATTCAGATGGATGCAGATTTAGTATTTGATGTACGATTTTTACCAAATCCATATTATGTAGTAGATTTAAGACCTTT +AACAGGATTAGATAAAGACGTTTATAATTATGTTATGAAATGGAAAGAGACGGAGATTTTCTTTGAAAAATTAACTGATT +TGTTAGATTTTATGATACCCGGGTATAAAAAAGAAGGGAAATCTCAATTAGTAATTGCCATCGGTTGTACGGGTGGACAA +CATCGATCTGTAGCATTAGCAGAACGACTAGGTAATTATCTAAATGAAGTATTTGAATATAATGTTTATGTGCATCATAG +GGACGCACATATTGAAAGTGGCGAGAAAAAATGAGACAAATAAAAGTTGTACTTATCGGTGGTGGCACTGGCTTATCAGT +TATGGCTAGGGGATTAAGAGAATTCCCAATTGATATTACGGCGATTGTAACAGTTGCTGATAATGGTGGGAGTACAGGGA +AAATCAGAGATGAAATGGATATACCAGCACCAGGAGACATCAGAAATGTGATTGCAGCTTTAAGTGATTCTGAGTCAGTT +TTAAGCCAACTTTTTCAGTATCGCTTTGAAGAAAATCAAATTAGCGGTCACTCATTAGGTAATTTATTAATCGCAGGTAT +GACTAATATTACGAATGATTTCGGACATGCCATTAAAGCATTAAGTAAAATTTTAAATATTAAAGGTAGAGTCATTCCAT +CTACAAATACAAGTGTGCAATTAAATGCTGTTATGGAAGATGGAGAAATTGTTTTTGGAGAAACAAATATTCCTAAAAAA +CATAAAAAAATTGATCGTGTGTTTTTAGAACCTAACGATGTGCAACCAATGGAAGAAGCAATCGATGCTTTAAGGGAAGC +AGATTTAATCGTTCTTGGACCAGGGTCATTATATACGAGCGTTATTTCTAACTTATGTGTGAATGGTATTTCAGATGCGT +TAATTCATTCTGATGCGCCTAAGCTATATGTTTCTAATGTGATGACGCAACCTGGGGAAACAGATGGTTATAGCGTGAAA +GATCATATCGATGCGATTCATAGACAAGCTGGACAACCGTTTATTGATTATGTCATTTGTAGTACACAAACTTTCAATGC +TCAAGTTTTGAAAAAATATGAAGAAAAACATTCTAAACCAGTTGAAGTTAATAAGGCTGAACTTGAAAAAGAAAGCATAA +ATGTAAAAACATCTTCAAATTTAGTTGAAATTTCTGAAAATCATTTAGTAAGACATAATACTAAAGTGTTATCGACAATG +ATTTATGACATAGCTTTAGAATTAATTAGTACTATTCCTTTCGTACCAAGTGATAAACGTAAATAATATAGAACGTAATC +ATATTATGATATGATAATAGAGCTGTGAAAAAAATGAAAATAGACAGTGGTTCTAAGGTGAATCATGTTTTAAATAAGAA +AGGAATGACTGTACGATGAGCTTTGCATCAGAAATGAAAAATGAATTAACTAGAATAGACGTCGATGAAATGAATGCAAA +AGCAGAGCTCAGTGCACTGATTCGAATGAATGGTGCACTTAGTCTTTCAAATCAACAATTTGTTATAAATGTTCAAACGG +AAAATGCAACAACGGCAAGACGTATTTATTCGTTGATTAAACGTGTCTTTAATGTGGAAGTTGAAATATTAGTCCGTAAA +AAAATGAAACTTAAAAAAAATAATATTTATATTTGTCGTACAAAGATGAAAGCGAAAGAAATTCTTGATGAATTAGGAAT +TTTAAAAGACGGCATTTTTACGCATGAAATTGATCATTCAATGATTCAAGATGACGAAATGAGACGCAGTTACTTGAGAG +GAGCTTTTCTGGCAGGTGGCTCAGTGAATAACCCTGAAACATCTTCGTACCATTTGGAAATTTTTTCTCAAAATGAGAGT +CATGCAGAAGGCTTAACGAAACTAATGAATAGTTATGAGTTGAATGCCAAACATTTAGAGCGAAAAAAAGGAAGTATTAC +GTATTTAAAAGAAGCGGAAAAGATTTCGGATTTTCTTAGTTTGATAGGTGGCTATCAAGCGTTATTAAAATTTGAAGACG +TACGTATTGTAAGAGATATGCGTAATTCTGTTAACCGACTCGTTAATTGTGAAACGGCCAATCTAAATAAAACAGTTAGT +GCTGCGATGAAACAAGTTGAGAGCATTAAATTGATTGATAAAGAAATTGGTATTGAAAATTTACCAGACAGGTTGAGAGA +GATTGCTAGAATTCGAGTAGAACATCAAGAAATTTCGTTGAAAGAGCTTGGAGAAATGGTATCAACTGGTCCAATTTCAA +AATCAGGTGTAAATCACCGATTAAGAAAACTGAATGATTTAGCCGATAAGATTAGAAATGGTGAACAAATAGAATTATAA +GTAAGAAGGTGTTTTTGGTAGTGAATTATCAAAAACACTTTTTTATGATTAAAAAGTGTTTAATGAATAATAGTCGACTA +ACTATTAAATTGAGCTGGAAAGTTTAATGAAGGAAATTCAATAACCAAGTGATGCCTAATTTTCTCATTAAATAGCATCA +TATGTATCAATTTGGTCATAAGAAAATATGAATGTGATAATTAATTGAGTCCCTGAAAGTCCCTGATCATAGACAAAAAC +AAAATCAGCATAAATAAGAATCCCGACATTGCGGGATTCTCTGTATTGAAAAGTATTGTATTTTATTAAAACAGCCTCCT +TGAAGGGAATTGAACCCCTATCTTAAGAACCGGAATCTTACGTGTTATCCATTACACCACAAGGAGCAAATATGTAATCA +TATCTCTGTACTATGAGAAGTAAAATAAGTACAATCTAAAATAGATATTATCAGTTTACAAAGGAAAGAGAAAAGCGTCA +AACAATGTAACTATTTAAAGTCAAAGTGTTTGACCAAATTTGACTTAATATGTAAAATAATGAGTAACAGTTATTACAAG +GAGGAAATATAGATGAATTTAATTCCTACAGTTATTGAAACAACAAACCGCGGTGAACGTGCATATGATATATACTCACG +TTTATTAAAAGACCGTATTATTATGTTAGGTTCACAAATTGATGACAACGTAGCAAATTCAATCGTATCACAGTTATTAT +TCTTACAAGCGCAAGACTCAGAGAAAGATATTTATTTATACATTAATTCACCAGGTGGAAGTGTAACAGCTGGTTTTGCG +ATTTATGATACAATTCAACACATTAAACCTGATGTTCAAACAATTTGTATCGGTATGGCTGCATCAATGGGATCATTCTT +ATTAGCAGCTGGTGCAAAAGGTAAACGTTTCGCGTTACCAAATGCAGAAGTAATGATTCACCAACCATTAGGTGGTGCTC +AAGGACAAGCAACTGAAATCGAAATTGCTGCAAATCACATTTTAAAAACACGTGAAAAATTAAACCGCATTTTATCAGAG +CGTACTGGTCAAAGTATTGAAAAAATACAAAAAGACACAGATCGTGATAACTTCTTAACTGCAGAAGAAGCTAAAGAATA +TGGCTTAATTGATGAAGTGATGGTACCTGAAACAAAATAATTCAAAGTAAAGAGTAGACTAAGCTGTCTGCTCTTTTTGT +ATGAGTAAACCAAGGTGTCAATAATTTGTTTACTATACTTTGAGCGGAAATATGATTGAATGAAGCTAGTTGAACCGTAA +CTATATGAAATGTTCCCTTCAAAGTAGACATTGAAAGGAACATTTCAATCCTTTGTTTGTAAGTCGCTCTAGACATTACA +TTTAGTACATATGTTGTTTCTAATGCTCATTAATGGTATTGATTATTCTTTAATTAAATCTTCAAGTGCCATTTTTAAAT +TACTATATTTAAATTGGAATCCCAATGCTTGAATTTTATTAGGTAATACTTTTTGAGTATCCAATACTACTGTTGACATT +TGACCAAGTATGAGACGCATTGCAAGACTTGGTGCCCAAGTTTCATGAGGCTTATGCATAGCTCTTGCTAAAGTGTAGCC +AAATAAATTTTGACGTTCAGGTATAGGTGCAGTTAAATTAAACGGACCACTAGCTGACTCGTTATTTATTAAAAATAAAA +TAGCTTGAATTAAATCATTGATATGAATCCATGAATACCATTGTTGACCAGAACCTAATTTACCACCAATGTAATATTCG +TATGGTAGTTTCATTGTTTGTAACGCACCGCCTTCATTCGATAAAATTATACCGAAACGACCGATGACAACTCGTGTACC +TAATTGTTCAAATTGTTGTGCGAAACGTTCCCATTGATACACAATATCTGATAAGAAATCAAAAGGTAAAGTTTTATAAA +CTTCTGTGTAACTCATAAATAAATCAGGAGGATAGTAACCAGTGGCACTAGCATTAAATAAAGCTTTAGGTGCTTTATTA +CGTGATTTAAACAATTCATATAAAGCTTGCGTAGATTGAATTCTACTTAGCATTAGCGTTTGTTTATATTCCGGTGTCCA +TCGTTTATTCAATGTAGCACCTGCTAAGTTGATGACCACATCGATATTTTGAGGAACTTTGTGTTCCCACCCAGATTTAG +CCCAGTTGACATATGAAATTTTCTTATCATTTGAAATTTGGTCGTGTCGCGTTAATATCGTGATATGTGAATCTGATTTT +TTAATTTCATTAACTAATTGAGATCCAACCATACCAGTCCCACCAGTAATTAAGTATTGTTTCATTATCATTCACCCCAT +GTAATTTTGTATTTAGTTACTATTTAATAATCATTATTGTTGTTCAAAGGTTATACATTATTTAGACAATAATATGTCAA +TAACTTTTTTTGAATTGTATTTTAATAAAAATGATATAAGTTATAATAAAGGTGTACCTTCGATAACGAATAAACATCTC +TTAAAAGTATGTGTAAAACGCTGCATGATACAAACGAAGGTAAAAATTTGACTCCCTTTAGTAGTGGACCCGTACGTTAA +TCGTGCGGGTCGTTTTTATTTATTTCTTATTCCCATTATACATCAATTTAAAGCATTAATTTTTTAAACAAATTTAAGAA +TACATAGTAATATAACAATCTAAACATAAAAACTTTTAACACAACACTTAAACCAATGCTTTAATTTTCAATACGTAGCT +ATAATTTGTTGTAAAATCAAAAAGGTTAAAATGTTAATTTTCAAAAAAAGGCTCAAAATATGTTTGATTTAGTTATTAAA +TGTTAAGATATATAAGACTACTATTTCTTTGTAAAAATGAATCCGATTTACGAGTGAGTAATAGTGAAGGCAGTTTTAAG +TTGAAGAAGGCAAAAAGAGTAAATGTTTATTAATATTTGTAGAAACTAGGTAAGCAAATTAGTTGTGAAAATGTTAATGG +TTGCGTGATAATTTCTATATTTAAATTAGTTTGAAGTGAGGGAGAGTATGTCGAATCAAAATTACGACTACAATAAAAAT +GAAGATGGAAGTAAGAAGAAAATGAGTACAACAGCGAAAGTAGTTAGCATTGCGACGGTATTGCTATTACTCGGAGGATT +AGTATTTGCAATTTTTGCATATGTAGATCATTCGAATAAAGCTAAAGAACGTATGTTGAACGAACAAAAGCAGGAACAAA +AAGAAAAGCGTCAAAAAGAAAATGCAGAAAAAGAGAGAAAGAAAAAGCAACAAGAGGAAAAAGAGCAGAATGAGCTAGAT +TCACAAGCAAACCAATATCAGCAATTGCCACAGCAGAATCAATATCAATATGTGCCACCTCAGCAACAAGCACCTACAAA +GCAACGTCCTGCTAAAGAAGAGAATGATGATAAAGCATCAAAGGATGAGTCGAAAGATAAGGATGACAAAGCATCTCAAG +ATAAATCAGATGATAATCAGAAGAAAACTGATGATAATAAACAACCAGCTCAGCCTAAACCACAGCCGCAACAACCAACA +CCAAAGCCAAATAATAATCAACAAAACAATCAATCAAATCAGCAAGCAAAACCACAAGCACCACAACAAAATAGCCAATC +AACAACAAATAAACAAAATAATGCTAATGATAAGTAGTATTTAGTCAAACAAAAATGAACCAGTATGACAGACAACACAA +TTAATTAGGTTGTCTCGAATATTGGTTCTTATTTTTATAATTGTTAATTAGGGGAGAGATGATACTTAAATAGTTAGTTG +TTTATTTTACGGATAGTGAAATTTATTTTGAGTGAGGTGGGACAGAAATGATATTTTTGCAAAATTTATTTCGTCGTCCC +ACCCCAACTCGCATTGCCTGTAGAATTTCATTTCGAAATTCTCTATGTTGGGGCCCCTGACTTTAATTGAAAAAAGCTTG +TTACAAGTGCATTTTCGTTCGGTTAACTACTACTAATGTGACTTTTTGGATTCTAGAGCATTGATTTATGTCCTAGTCTC +AAATAATAAGCAAATGATTATCGAGTCAGTATAAGGGTATACATTTGACTACAGCGAATAAAATAAAACGTTTTGTTGAA +TAACAAATCGAAAATATATTGCAAGCGCTTTATCAAATTATTTAGAAAATTTAAGTTTTATGCTTGCAATTTTTGAAATA +GAATAGTACTATTGCAAGTGTAAAGAGGTTAATTTTTGTCCCACGCGGGACTTAAAAAGGCAACCACTGGTTGTGACATA +TCCTTATTTACATTTATAAATATAAGGAGGAGGTAGTAGTGAAAGACTTATTGCAAGCACAGCAAAAGCTTATACCGGAT +CTCATAGATAAAATGTATAAACGTTTTTCTATTCTTACTACTATCTCAAAAAATCAGCCTGTCGGACGTCGAAGTTTAAG +CGAACATATGGATATGACTGAACGTGTACTGCGTTCTGAAACAGATATGCTTAAGAAACAAGATTTGATAAAAGTTAAGC +CTACCGGAATGGAAATTACAGCTGAAGGTGAGCAACTGATTTCGCAATTGAAAGGTTACTTTGATATCTATGCAGATGAT +AATCGTCTGTCAGAAGGTATTAAGAATAAATTTCAAATTAAGGAAGTTCATGTTGTTCCTGGTGATGCTGATAATAGTCA +ATCTGTTAAAACAGAATTAGGTAGACAAGCAGGTCAATTACTTGAAGGCATATTACAAGAAGACGCGATAGTTGCTGTAA +CTGGCGGATCCACGATGGCATGTGTTAGTGAAGCAATTCATTTATTACCATATAATGTATTCTTCGTACCAGCCAGAGGT +GGACTAGGCGAAAATGTTGTCTTTCAGGCAAACACAATTGCAGCCAGTATGGCACAACAAGCTGGCGGTTATTATACGAC +GATGTATGTACCTGATAATGTCAGTGAAACAACATATAATACATTGTTGTTAGAGCCATCAGTCATAAACACTTTAGACA +AAATTAAACAAGCAAACGTTATATTACACGGCATTGGTGATGCGCTGAAGATGGCGCATCGACGTCAATCACCTGAAAAG +GTCATTGAACAACTTCAACATCATCAAGCTGTCGGAGAGGCATTTGGTTATTATTTTGATACACAAGGTCAAATTGTCCA +TAAGGTTAAAACAATTGGACTTCAATTAGAAGACCTTGAATCAAAAGACTTTATTTTTGCAGTTGCAGGAGGCAAATCGA +AAGGTGAAGCAATTAAAGCATACTTGACGATTGCACCCAAGAATACAGTGTTAATCACTGATGAAGCCGCAGCAAAGATA +ATACTTGAATAAGAGATAAAAAGTTTAATACTTTTTAAATATCATTTTAAAGGAGGCCATTATAATGGCAGTAAAAGTAG +CAATTAATGGTTTTGGTAGAATTGGTCGTTTAGCATTCAGAAGAATTCAAGAAGTAGAAGGTCTTGAAGTTGTAGCAGTA +AACGACTTAACAGATGACGACATGTTAGCGCATTTATTAAAATATGACACTATGCAAGGTCGTTTCACAGGTGAAGTAGA +GGTAGTTGATGGTGGTTTCCGCGTAAATGGTAAAGAAGTTAAATCATTCAGTGAACCAGATGCAAGCAAATTACCTTGGA +AAGACTTAAATATCGATGTAGTATTAGAATGTACTGGTTTCTACACTGATAAAGATAAAGCACAAGCTCATATTGAAGCA +GGCGCTAAAAAAGTATTAATCTCAGCACCAGCTACTGGTGACTTAAAAACAATCGTATTCAACACTAACCACCAAGAGTT +AGACGGTTCTGAAACAGTTGTTTCAGGTGCTTCATGTACTACAAACTCATTAGCACCAGTTGCTAAAGTTTTAAACGATG +ACTTTGGTTTAGTTGAAGGTTTAATGACTACAATTCACGCTTACACAGGTGATCAAAATACACAAGACGCACCTCACAGA +AAAGGTGACAAACGTCGTGCTCGTGCAGCGGCAGAAAACATCATCCCTAACTCAACAGGTGCTGCTAAAGCTATCGGTAA +AGTTATTCCTGAAATCGATGGTAAATTAGATGGTGGTGCACAACGTGTTCCTGTAGCTACAGGTTCATTAACTGAATTAA +CAGTAGTATTAGAAAAACAAGACGTAACAGTTGAACAAGTTAACGAAGCTATGAAAAATGCTTCAAACGAATCATTCGGT +TACACTGAAGACGAAATCGTTTCTTCAGACGTTGTAGGTATGACTTACGGTTCATTATTCGACGCTACACAAACTCGTGT +AATGTCAGTTGGCGACCGTCAATTAGTTAAAGTTGCAGCTTGGTATGATAACGAAATGTCATATACTGCACAATTAGTTC +GTACATTAGCATACTTAGCTGAACTTTCTAAATAATTTTAGTATAGTTTTTATTCAAATACGCTAGTGCTCAGAACTATT +TAGCATTAATTAAAGCTTATGAGTAAGCGGGGAGCACAAACGCTTCTCCGCTTATTTTTATATAAAATTTCCTAATTACA +AGGAGGAAACACCATGGCTAAAAAAATTGTTTCTGATTTAGATCTTAAAGGTAAAACAGTCCTAGTACGTGCTGATTTTA +ACGTACCTTTAAAAGACGGTGAAATTACTAATGACAACCGTATCGTTCAAGCTTTACCTACAATTCAATACATCATCGAA +CAAGGTGGTAAAATCGTACTATTTTCACATTTAGGTAAAGTGAAAGAAGAAAGTGATAAAGCAAAATTAACTTTACGTCC +AGTTGCTGAAGACTTATCTAAGAAATTAGATAAAGAAGTTGTTTTCGTACCAGAAACACGCGGCGAAAAACTTGAAGCTG +CTATTAAAGACCTTAAAGAAGGCGACGTATTATTAGTTGAAAATACACGTTATGAAGATTTAGACGGTAAAAAAGAATCT +AAAAATGATCCAGAATTAGGTAAATACTGGGCATCTTTAGGTGATGTGTTTGTAAATGATGCTTTTGGTACTGCGCATCG +TGAGCATGCATCTAATGTTGGTATTTCTACACATTTAGAAACTGCAGCTGGATTCTTAATGGATAAAGAAATTAAGTTTA +TTGGCGGCGTAGTTAACGATCCACATAAACCAGTTGTTGCTATTTTAGGTGGAGCAAAAGTATCTGACAAAATTAATGTC +ATCAAAAACTTAGTTAACATAGCTGATAAAATTATCATCGGCGGAGGTATGGCTTATACTTTCTTAAAAGCGCAAGGTAA +AGAAATTGGTATTTCATTATTAGAAGAAGATAAAATCGACTTCGCAAAAGATTTATTAGAAAAACATGGTGATAAAATTG +TATTACCAGTAGACACTAAAGTTGCTAAAGAATTTTCTAATGATGCCAAAATCACTGTAGTACCATCTGATTCAATTCCA +GCAGACCAAGAAGGTATGGATATTGGACCAAACACTGTAAAATTATTTGCAGATGAATTAGAAGGTGCGCACACTGTTGT +ATGGAATGGACCTATGGGTGTATTCGAGTTCAGTAACTTTGCACAAGGTACAATTGGTGTATGTAAAGCAATTGCAAACC +TTAAAGATGCAATTACGATTATCGGTGGCGGTGATTCAGCTGCAGCAGCAATCTCTTTAGGTTTTGAAAATGACTTCACT +CATATTTCAACTGGTGGCGGCGCGTCATTAGAGTACCTAGAAGGTAAAGAATTGCCTGGTATCAAAGCAATCAATAATAA +ATAATAAAGTGATAGTTTAAAGTGATGTGGCATGTTTGTTTAACATTGTTACGGGAAAACAGTCACAAGATGACATCGTG +TTTCATCACTTTTCAAAAATATTTACAAAACAAGGAGTGTCTTTAATGAGAACACCAATTATAGCTGGTAACTGGAAAAT +GAACAAAACAGTACAAGAAGCAAAAGACTTCGTCAATACATTACCAACACTACCAGATTCAAAAGAAGTAGAATCAGTAA +TTTGTGCACCAGCAATTCAATTAGATGCATTAACTACTGCAGTTAAAGAAGGAAAAGCACAAGGTTTAGAAATCGGTGCT +CAAAATACGTATTTCGAAGATAATGGTGCGTTCACAGGTGAAACGTCTCCAGTTGCATTAGCAGATTTAGGCGTTAAATA +CGTTGTTATCGGTCATTCTGAACGTCGTGAATTATTCCACGAAACAGATGAAGAAATTAACAAAAAAGCGCACGCTATTT +TCAAACATGGAATGACTCCAATTATATGTGTTGGTGAAACAGACGAAGAGCGTGAAAGTGGTAAAGCTAACGATGTTGTA +GGTGAGCAAGTTAAGAAAGCTGTTGCAGGTTTATCTGAAGATCAACTTAAATCAGTTGTAATTGCTTATGAACCAATCTG +GGCAATCGGAACTGGTAAATCATCAACATCTGAAGATGCAAATGAAATGTGTGCATTTGTACGTCAAACTATTGCTGACT +TATCAAGCAAAGAAGTATCAGAAGCAACTCGTATTCAATATGGTGGTAGTGTTAAACCTAACAACATTAAAGAATACATG +GCACAAACTGATATTGATGGGGCATTAGTAGGTGGCGCATCACTTAAAGTTGAAGATTTCGTACAATTGTTAGAAGGTGC +AAAATAATCATGGCTAAGAAACCAACTGCGTTAATTATTTTAGATGGTTTTGCGAACCGCGAAAGCGAACATGGTAATGC +GGTAAAATTAGCAAACAAGCCTAATTTTGATCGTTATTACAACAAATATCCAACGACTCAAATCGAAGCGAGTGGCTTAG +ATGTTGGACTACCTGAAGGACAAATGGGTAACTCAGAAGTTGGTCATATGAATATCGGTGCAGGACGTATCGTTTATCAA +AGTTTAACTCGAATCAATAAATCAATTGAAGACGGTGATTTCTTTGAAAATGATGTTTTAAATAATGCAATTGCACACGT +GAATTCACATGATTCAGCGTTACACATCTTTGGTTTATTGTCTGACGGTGGTGTACACAGTCATTACAAACATTTATTTG +CTTTGTTAGAACTTGCTAAAAAACAAGGTGTTGAAAAAGTTTACGTACACGCATTTTTAGATGGCCGTGACGTAGATCAA +AAATCCGCTTTGAAATACATCGAAGAGACTGAAGCTAAATTCAATGAATTAGGCATTGGTCAATTTGCATCTGTGTCTGG +TCGTTATTATGCAATGGATCGTGACAAACGTTGGGAACGTGAAGAAAAAGCTTACAATGCTATTCGTAATTTTGATGCCC +CAACTTATGCAACTGCCAAAGAAGGTGTAGAAGCAAGCTATAATGAGGGCTTAACTGACGAATTCGTAGTACCATTCATC +GTTGAGAATCAAAATGACGGTGTTAATGATGGAGATGCAGTGATCTTCTATAATTTCCGACCTGATAGAGCAGCGCAATT +ATCGGAAATTTTTGCGAACAGAGCATTCGAAGGCTTTAAAGTTGAACAAGTTAAAGACTTATTCTATGCAACATTCACTA +AGTATAATGACAATATCGATGCGGCTATCGTCTTCGAAAAAGTTGATTTAAATAATACAATTGGTGAAATTGCACAAAAT +AACAATTTAACTCAATTACGTATTGCAGAAACTGAAAAATACCCTCACGTTACTTACTTTATGAGTGGTGGACGTAACGA +GGAATTTAAAGGTGAACGCCGTCGTTTAATTGATTCACCTAAAGTTGCAACGTATGACTTGAAACCAGAAATGAGTGCTT +ATGAAGTTAAAGATGCATTATTAGAAGAGTTAAATAAAGGTGACTTGGACTTAATTATTTTAAACTTTGCTAACCCTGAT +ATGGTTGGACATAGTGGTATGCTTGAGCCGACAATCAAAGCAATCGAAGCGGTTGATGAATGTTTAGGAGAAGTGGTTGA +TAAGATTTTAGACATGGACGGTTATGCAATTATTACTGCTGACCATGGTAACTCTGATCAAGTATTGACGGATGATGATC +AACCAATGACTACGCATACAACGAACCCAGTACCAGTGATTGTAACAAAAGAAGGCGTTACACTTAGAGAAACTGGTCGC +TTAGGTGACTTAGCACCTACATTATTAGATTTATTAAATGTAGAACAACCTGAAGATATGACAGGTGAATCTTTAATTAA +ACACTAATATTGTAAAAGATGTTAAGTAAACGCTTAATGACACTTATTTTTTGAAAATAATAGTAATATCATTTTGTTAA +ATGAAAGAATAAAGCTATAATAATTATAGAATAACTATTTAAAGGAGATTATAAACATGCCAATTATTACAGATGTTTAC +GCTCGCGAAGTCTTAGACTCTCGTGGTAACCCAACTGTTGAAGTAGAAGTATTAACTGAAAGTGGCGCATTTGGTCGTGC +ATTAGTACCATCAGGTGCTTCAACTGGTGAACACGAAGCTGTTGAATTACGTGATGGAGACAAATCACGTTATTTAGGTA +AAGGTGTTACTAAAGCAGTTGAAAACGTTAATGAAATCATCGCACCAGAAATTATTGAAGGTGAATTTTCAGTATTAGAT +CAAGTATCTATTGATAAAATGATGATCGCATTAGACGGTACTCCAAACAAAGGTAAATTAGGTGCAAATGCTATTTTAGG +TGTATCTATCGCAGTAGCACGTGCAGCAGCTGACTTATTAGGTCAACCACTTTACAAATATTTAGGTGGATTTAATGGTA +AGCAGTTACCAGTACCAATGATGAACATCGTTAATGGTGGTTCTCACTCAGATGCTCCAATTGCATTCCAAGAATTCATG +ATTTTACCTGTAGGTGCTACAACGTTCAAAGAATCATTACGTTGGGGTACTGAAATTTTCCACAACTTAAAATCAATTTT +AAGCAAACGTGGTTTAGAAACTGCAGTAGGTGACGAAGGTGGTTTCGCTCCTAAATTTGAAGGTACTGAAGATGCTGTTG +AAACAATTATCCAAGCAATCGAAGCAGCTGGTTACAAACCAGGTGAAGAAGTATTCTTAGGATTTGACTGTGCATCATCA +GAATTCTATGAAAATGGTGTATATGACTACAGTAAGTTCGAAGGCGAACACGGTGCAAAACGTACAGCTGCAGAACAAGT +TGACTACTTAGAACAATTAGTAGACAAATATCCTATCATTACAATTGAAGACGGTATGGACGAAAACGACTGGGATGGTT +GGAAACAACTTACAGAACGTATCGGTGACCGTGTACAATTAGTAGGTGACGATTTATTCGTAACAAACACTGAAATTTTA +GCAAAAGGTATTGAAAACGGAATTGGTAACTCAATCTTAATTAAAGTTAACCAAATCGGTACATTAACTGAAACATTTGA +TGCAATCGAAATGGCTCAAAAAGCTGGTTACACAGCAGTAGTTTCTCACCGTTCAGGTGAAACAGAAGATACAACAATTG +CTGATATTGCTGTTGCTACAAACGCTGGTCAAATTAAAACTGGTTCATTATCACGTACTGACCGTATTGCTAAATACAAT +CAATTATTACGTATCGAAGATGAATTATTTGAAACTGCTAAATATGACGGTATCAAATCATTCTATAACTTAGATAAATA +ATTTTCTTTATAATCAAATGCTGACATAATTTTAGTTGAGGATTATTATGACGGTATAAATTAAATAAAGATTTTGAGTT +CACGCTTAAATAAGTTCACGCTTAAATTTATAGCCTGCCACAGAGTTGAGACTGTGGTAGGTTTTTTATTTTGAAGTATT +AATCATAACAGACTAATAATCATGAGGTAACTAATAACACATATTTAACTTGTATTCTTAAACTGGTATAATAAATTTAT +GTTGAAATGAATATTGTATGACAGGGTATTCACTTTTATTAAAAGGTAAAATTAAATAAAGGTTTTATAGAACGTATTTA +AATATATGAGGAGTAAACAAATGGCTGATAGAACGAATAAAGAAATTAAAACAGGACGCTTTATTGCAACTGCATCAATC +GTATTCTCAATATTATTGATTATTCATTACTTTGTTTCGTTGGATAATGCGACTGCCAAAGCATTACTTAATTTAACGAA +TCAAAACACTTCAGATAAAGCGATTGATTACATTTTAAACAGCTTTAGATTCACTGGTATTATGTATATTTTGGCTTATC +TAGCAGGCTTCATCACTTTTTGGAATCGACATACTTATGTGTGGTGGTTTATGTTTGCAGTTTATGTATCAAATAGTTTG +TTTACGTTGATTAATTTATCAATCACAATTCAAGCAATAAAAGCTGCACACGGTGCGTACTTAACATTGCCAATTTTAAT +TGTTATTATAGGTTCGGTTGCATTAGCGATTTATATGCTTGTTGTTTCTATCAAACGTAAAAGTACATTTAATCGCTAGA +AAATTGATTTTAACAATAAAAATATGATATACTACTTGTCGTATATAAGGAACGGAGGACAATTTATGCATACATTTTTA +ATCGTATTATTAATCATTGATTGTATTGCATTAATAACTGTTGTACTACTCCAAGAAGGTAAAAGCAGTGGACTTTCAGG +TGCCATCAGTGGTGGTGCTGAGCAGTTATTCGGTAAACAAAAACAACGTGGCGTCGATTTATTCTTAAATAGATTAACAA +TTATTTTATCAATATTATTTTTTGTACTTATGATTTGCATAAGTTATCTTGGTATGTAAGGTCCGGCGATGTAAATGTCG +GGCTTTTTTATTTATAATTAAGAATGTAATAGTTTAACAATAAGCTATGTAAAATATATAGCCTAGTTAAGTATGCAAAG +GGAGCGTTAGATTTATGCAGATAAAATTACCAAAACCTTTCTTTTTTGAGGAAGGTAAACGTGCCGTGTTATTACTACAT +GGTTTTACAGGCAATTCGTCTGATGTTCGTCAATTAGGTCGATTTTTACAAAAGAAAGGTTATACATCATATGCACCGCA +ATATGAAGGCCACGCGGCACCACCAGATGAAATACTGAAATCTAGTCCTTTCGTTTGGTTTAAAGATGCGTTAGATGGTT +ATGATTATCTTGTTGAACAAGGTTATGATGAAATTGTTGTTGCTGGTCTATCATTAGGTGGGGATTTTGCTTTAAAATTA +AGCTTAAATAGAGATGTAAAGGGTATTGTAACGATGTGTGCTCCTATGGGTGGCAAAACTGAAGGTGCCATTTATGAAGG +CTTTTTAGAATATGCACGCAATTTTAAAAAGTATGAAGGTAAAGATCAAGAGACTATTGATAATGAAATGGATCATTTTA +AACCAACTGAAACTTTAAAAGAACTAAGTGAAGCATTAGATACGATTAAAGAGCAAGTTGATGAAGTGTTGGATCCTATT +TTAGTGATTCAAGCAGAAAACGACAATATGATTGATCCACAATCCGCAAATTATATATATGACCATGTAGATTCTGATGA +CAAAAATATCAAGTGGTACAGTGAATCTGGACATGTTATTACGATTGATAAAGAGAAAGAACAAGTATTTGAAGATATTT +ATCAATTTTTAGAGTCATTAGACTGGTCAGAATAAAAAGAGATTTTAACATTAGAAAGGAGGGGCATAATGAATTTAAAG +CAATCTATAGAAGAGATTATTAATCAACCTGAATATGAACCTATGTCAGTGTCAGATTTTCAAGATGCATTAGGTTTAAG +CAGTGCCGACTCGTTTAGAGATTTAATTAAGGTGCTTGTGGAGTTAGAACAATCAGGATTAATCGAACGTACAAAAACAG +ACAGATACCAAAAAAAGCATAGTTATAGAGGTCAATCAAAATTGATAAAAGGAACGTTAAGTCAAAATAAAAAAGGCTTT +GCATTCTTAAGACCTGAAGATGAGGATATGGAAGATATATTTATTCCCCCGACGAAAATTAATCGTGCCTTGGATGGAGA +TACTGTTATTGTAGAAATCCATCAATCAAAAGGTGAACATAAAGGTAAAATCGAAGGGGAAGTTAAGTCGATTGAGAAGC +ATTCTGTAACTCAAGTTGTTGGTACGTATAGTGAAGCTAGACATTTTGGCTTTGTTATTCCGGATGATAAACGTATTATG +CAAGATATTTTCATTCCTAAAGGTCAAAGTTTAGGCGCAGTCGATGGTCATAAGGTACTTGTACAAATTACTAAGTATGC +TGATGGTTCAGATAATCCAGAAGGACATATTTCTGCTATTTTAGGACATAAAAATGATCCTGGCGTAGATATTTTATCTA +TTATCTATCAACATGGCATAGAAATTGAATTTCCTGATGAAGTGTTACAAGAAGCTGAAGCAGTACCTGATCATATTGAA +AATACTGAAATTAAAGGCCGTCATGATTTACGTGATGAATTGACAATCACAATTGATGGTGCTGATGCTAAAGACTTAGA +TGACGCAATTAGTGTTAAAAAGTTAGCGAACGGTAATACGCAATTAACTGTAAGTATTGCTGATGTCAGCTATTATGTAA +CAGAAGGTTCTGCATTGGATAAAGAGGCATATGATAGAGCGACAAGTGTATATCTTGTTGACCGTGTAATTCCAATGATT +CCACATCGATTAAGTAATGGTATTTGTTCATTGAATCCTAATGTTGATCGTTTAACTCTAAGCTGTCGCATGGAAATCGA +TGCTAGTGGTCGCGTTGTTAAACATGAAATTTTTGATAGTGTTATACATTCTGATTATCGAATGACGTATGATGCGGTAA +ATCAGATTATTACTGAAAAGGATCCTAACATTCGCGAACAATATAATGAAATTACGCCTATGCTAGATTTAGCACAAGAT +TTATCTAATCGTTTGATTCAAATGAGAAAACGACGTGGTGAAATCGATTTTGATATTAGTGAAGCAAAAGTATTAGTTAA +CGAAGACGGTATACCAACAGATGTTCAATTAAGACAACGTGGCGAGGGTGAACGTCTAATTGAATCATTTATGTTAATTG +CAAATGAAACAGTTGCTGAACATTTTAGTAAGTTAGATGTACCTTTTATTTACCGAGTGCATGAGCAACCTAAATCAGAT +CGCTTAAGACAATTCTTTGATTTTATTACAAACTTTGGCATCATGATTAAGGGTACTGGCGAAGATATTCATCCAACAAC +ACTTCAAAAGGTTCAAGAAGAAGTAGAAGGTCGACCTGAACAAATGGTCATTTCAACAATGATGTTGCGTTCAATGCAAC +AAGCGCATTATGATGATGTGAACTTGGGACATTTTGGCTTATCAGCTGAATATTATACGCATTTTACATCACCAATTAGA +CGTTATCCTGATTTAACAGTTCATCGTTTAATCCGTAAGTATTTAATTGAGAAATCAATGGATAACAAAGAAGTGAAGCG +TTGGGAAGACAAATTGCCTGAGTTAGCTGAACATACTTCTAAACGTGAACGTCGTGCTATTGAGGCAGAACGTGATACTG +ATGAATTGAAAAAAGCAGAATATATGATTCAACATATTGGTGATGAATTTGAAGGTATTGTCAGCTCAGTAGCTAACTTC +GGTATGTTCATTGAATTGCCAAATACGATAGAAGGTATGGTTCATATTGCGAATATGACTGATGATTATTACCGTTTTGA +AGAGCGTCAAATGGCATTAATTGGTGAGCGTCAAGCTAAAGTATTTAGAATTGGTGACACAGTTAAGGTTAAAGTGACGC +ATGTTGATGTAGATGAACGATTAATTGATTTTCAAATTGTAGGTATGCCTTTACCGAAAAATGATCGATCACAGCGCCCA +GCGCGAGGTAAGACAATTCAAGCCAAAACGCGTGGTAAATCATTAGATAAATCAAAATCTGATGATAAGGGTCGTAAGAA +AAAAGGTAAGCAACGTAAAGGTAAAAACCAACGTAATAATGATAAATCAGGTAATAGTAAGCATAAGCCATTTTATAAAG +ATAAAAGTGTGAAAAAGAAAGCACGTCGTAAGAAAAAATAAGCAGCAATGAGGTGAGTATGAATGGCTAAGAAGAAATCA +CCAGGTACATTAGCGGAAAATCGTAAGGCAAGACATGATTATAATATTGAAGATACGATTGAAGCGGGAATTGTATTGCA +AGGCACAGAAATAAAATCAATTCGCCGAGGTAGTGCTAACCTTAAAGATAGTTATGCGCAAGTTAAAAACGGTGAAATGT +ATTTGAATAATATGCATATAGCACCATACGAAGAAGGGAATCGTTTTAATCACGATCCTCTTCGTTCTCGAAAATTATTA +TTGCACAAGCGTGAAATCATTAAATTGGGTGATCAAACACGTGAGATTGGTTATTCGATTGTGCCGTTAAAGCTTTATTT +GAAGCATGGACATTGTAAAGTATTACTTGGTGTTGCACGAGGTAAGAAAAAATATGATAAACGTCAAGCTTTGAAAGAAA +AAGCAGTCAAACGAGATGTTGCGCGCGATATGAAAGCCCGTTATTAAGCGATTTAGTTGCTTAATCGGGCTATATTTGAT +ATAGTTATATGTGCTTTTGTAAATTACAAAAGTATGATTTGTTTGATTTATTATTTCGGGGACGTTCATGGATTCGACAG +GGGTCCCCCGAGCTCATTAAGCGTGTCGGAGGGTTGTCTTCGTCATCAACACACACAGTTTATAATAACTGGCAAATCAA +ACAATAATTTCGCAGTAGCTGCCTAATCGCACTCTGCATCGCCTAACAGCATTTCCTATGTGCTGTTAACGCGATTCAAC +CTTAATAGGATATGCTAAACACTGCCGTTTGAAGTCTGTTTAGAAGAAACTTAATCAAACTAGCATCATGTTGGTTGTTT +ATCACTTTTCATGATGCGAAACCTATCGATAAACTACACACGTAGAAAGATGTGTATCAGGACCTTTGGACGCGGGTTCA +AATCCCGCCGTCTCCATATTTGTAGCCTACAGCCTTTGTGGTTGTGGGCTTTTTTATTTTGTGTTTTTCAGGGGATAATG +CATTGCAGAATTTGTTGTGAGTATTGATATAGCAGTGTTTGTATAGGTGTTTATTTGATGGAGGAAAGAGTAATAAGTGA +TTATGAATTAGTTTTTGAGATATAAGGGGACAGTGATGTGTGTCAAATAAGTGTCAAAAAAGTTGGATTCTGAGTTTTAC +ATTCAACATTGTTCATGAAGAAACTTCTTTATACGCAAAAAATTCTCCATGTTATATATGTCAATATAAAAATGTGAATC +GTCTACACTTAATTGGATAAATGGCTACTGAAAAAGAACTTTTCATTTTTGTTACGTCACTAAGTGGGTGTAGTTATAAA +GAGATGAGCCGAGTTTTGATATTTTCATTAGAATCAATATGCCTATTAACACAATCAGCAATAGTTGACGAGACGGAAAT +AAAAGAAGTCGTAGTTAAGAAATGCATTTCACAACATACCATTGTAGCCATTTTTATTGTTTTGGATGATAAACTCTTTT +TGGAATTTTTAGTTTTTATAATTTGCAACTACACTACTTCTTTTACTAATATTAATGTCTAAGTAATCGATAAAAAATTT +TCCATTGAATAAATGAGAAGTTAAAAACTTTACTTAACCTTTCTCATTGCATTTTCCTATTCACGATTTTAAGAACCCAA +CATACTACAAACGAATTTTAAAAGGCGAGAGTAAAGCTTACTTGTTTATTATACATATTTAAAATCCAAGAGTCAGAACA +GACTACTCCTCTTTATAACTATAAAAAATAGCTATGAAAAAATCTATCGTCATAGATTCCTTCATAGCTAATCTTAGTAT +GTTTATTTTTATTTTAGGATGCTATTTATCAACTCAACATATAACTCACTATTTTTATAACCTTCTAATATATCATTAAC +TTGTCTAATAGGTATTTCTGGTACTTCTCTAATGTTTTCCAATTTTGTTTTAAATTGTTTTTTTGTTATTTGCTCTTTAT +TTGTAGCCAATTGGAACAAGTAAGAATCTAGCATATTAATTTCTTTATATGAATACATATATCTTAATAACACTAAATCT +CTAGTTTTTAAGTTAGGCGCTAGTTCTTCTTGTAATTGTTCTATTGATTGTTTCATTAATAACAATCTCATTTCTAATTC +TTCATTATTCATTTTATCACACTCTTTTTATATTAATGCTTGACCAACTTGGGAAACCCAAAACCCTATGCTTCTTGCAG +TAGAATCTTTAATACCAGTTCCCATCAATGCTTGTGAAACTTGACCTTGTACATTTCCCCATGTAGCCTCTTCTTGTTTT +AATGCATTATTCAATGCGGGATTTACAAATTTATCCCATCTTTTTTTTATGATTTTCCGGCACGGGGACTGATTTCTTTA +ACACCATTAAACACAGATTTTTTATTTTTAATCATAGCTTTATAGTATCATGTTGGCTAAGCTATAAATAAGTCAGTTTC +TCTAAAAATTAAATAACTGAATGTAAGACAATCAACAAACCAAATTTATACTTCATCTAAACCACTGTGGTCGTCATCTT +TTTGCTTTTCTTTTTCTTTCTCTCGTTCTTGTTCTTTTTTGTACTCTTCTTCAAATTCTTTTTCTTTCTTTTCTACTTCT +TCTCTTGTTTCCGCTCTATGAGAAAAATCTTCGGTTTTAAGTTTACTAAATTTGAATGATTTAGAATCAACTGTTTTATC +TTCTGAGTATTTATGGACATTTAAATTAATATTTCCATCACCTCTTAACTCATAGATAAACATGGCTTGTGCAGTTTTGC +CTTTTTTAATTTGATCTTGGTTATGTTCTGTCCAATCTTTATATTTTTTATCACTTAAAAGATAACCATCTCTTAATTTA +TTTACTGTATTTTTATCATCTTGAGTGATATTAATATAGTCATGAGAAATAGAAGATGGATTTAAATCTTTATCGTCTTT +TTTAGCAGTAATTTCCATTTTAAAAGCGATATATTTCTTTTTCTCATCTTTTTCATTGATGATAAACGGTTCTTTTATTT +TAGCTTCAAATTTGTCACTAACAATAGTATCGCCTTTAATTTTTATATCCATATTTTTTTTGCTTTTAAATTCTTTAAGT +TCTTCATTTAATTCTTCATTGTCATTTTCTTTCTTTTTGTGACTAGTGCTCTCTTTTTTTGCACTATCTTGATGATGTCC +ACAAGCACCTAAGATAAGTGTACTTGCTAATAATATCCCCATTACTTTTTTCATTTAACATGTCTCCTTTATTTCGCAAA +AATTTATTTTAAAAACTCTAAATGACTTATCATTTTGAGTAATTAAACAAAGTTGATATTTTGTGAGATTCTAAGATGAT +ATTAAATAATTCTTGTAATAATGATCCTATGTATTGTTGCAATAAATTAATGAAACTATAATTACTAATATTATATTACT +TTTATTGATAGAAATATATTACTTTTTTAAAAAAACTTGTAATATATCGAAAGATTTAAATGTAAAATTTTGATTTGTTA +AGAAATTACGTTTGTAAAAATAAAAAAATCAACTTATTTGTATGAGATAAATATGTATTGAAGAAGACGTGTTATTAATT +TGGAAAATACTGGTCAAAGATGGGAGCTCTTAAAAGCGTTATTGTATTTTTTAGTCAATACAAATAGATTGCCGTAATAA +TAATCGTACTTGATGGTTAAAAAATTACTTAAGGCTATAAAGCAAAACTTTTTATATGAGCAGTCGAATATAACGTTTAA +AATGATTGTTTTTGGATATAAACGATTAAGTAAAATGCTTTTTCAGTTTGAAATTAATCATATAAATTTCTTATGGGAGG +GTTGATATCTTAATGATTAACATTATTTCAGCTATAGGATCTATTGGAACATTTATTATGGCTTTATTTTATTTTGTATC +AGTTTCAGTTCAACTTTATCAAATGAAAATTAGCTTTCTGCCAGCTTTAGGTTTTAACCAAATTTTATTAGAAAGGGAGG +AGGATCAACTTAATATAATGAATTCGGCAACAGAAGAGCATCATCATAAAGATTATATTAAACTATATAATTTAGGTGGC +GGTGCTGCTAAAAAAATTGCAATAGAGGTTTTATTGGGGAAGGATAAAGTCATTCAGAAAAAATACGTGCATATTTTACC +TAGTAAAGAAGGGTACATGTTACCAATTAATAAAAATGTGTACGAAGAATTAGAAAGAACGATTGAGAACAATGGTCATG +AAGCTGATTTGAATGTACGTATGACTTATTATCATAATGTAAGTCGCAAACAACAGGAAGTTATATTAAAAGGTCAAATC +GACCGTTTTAATACTTATAATAATAAAGAAATTTATGATTTGCAGTTTATCTAAAAATTGATTTAAGAGGGTAGTTGTTT +ATTGCGAAAAATATCATTCAATTTTAATGAAATAATGGCGTCATTACTATAAAATATTACTTTATGTTGTAATGCATTTT +TCTATAAGATAGAACTAAAAGGAGGGGCAAAGATGCAAATTAGACAAATACATCAACATGACTTTGCTCAAGTGGACCAG +TTAATTAGAACGGCATTTGAAAATAGTGAACATGGTTATGGTAATGAATCAGAGCTAGTAGACCAAATTCGTCTAAGTGA +TACGTATGACAATACCTTAGAATTAGTAGCTGTTCTTCAAAATGAAGTTGTAGGGCACGGTTTACTAAGTGAAGTTTATC +TTGATAACGAGGCACAACGGGAAATTGGATTAGTGTTAGCACCTGTATCTGTTGATATTCATCATCAAAATAAAGGTATT +GGGAAGCGATTGATTCAAGCATTAGAACGAGAAGCAATATTAAAAGGATATAATTTTATCAGTGTATTAGGATGGCCGAC +GTATTATGCCAATCTAGGATATCAACGCGCAAGTATGTACGACATTTATCCACCATATGATGGTATACCAGACGAAGCGT +TTTTAATTAAAGAATTAAAAGTGAACAGTTTAGCGGGAAAAACAGGTACCATAAATTACACATCTGCTTTTGAAAAAATA +TGATTTCAAGCTAGGATTACATTAGGTAGAGTTCATATTAATAATAAAAAATGTTTGCAATCAAATCGTACGTTGTCGTT +TGTAATTCTTAAAATAGCAATAAATAAAATGTTTGTTAGTAAAGTATTATTGTGGATAATAAAATATCGATACAAATTAA +TTGCTATAATGCAATTTTAGTGTATAATTCCATTGACAGAGATTAAATATATCTTTAAAGGGTATATAGTTAATATAAAA +TGACTTTTTAAAAAGAGGGAATAAAATGAATATGAAGAAAAAAGAAAAACACGCAATTCGGAAAAAATCGATTGGCGTGG +CTTCAGTGCTTGTAGGTACGTTAATCGGTTTTGGACTACTCAGCAGTAAAGAAGCAGATGCAAGTGAAAATAGTGTTACG +CAATCTGATAGCGCAAGTAACGAAAGCAAAAGTAATGATTCAAGTAGCGTTAGTGCTGCACCTAAAACAGACGACACAAA +CGTGAGTGATACTAAAACATCGTCAAACACTAATAATGGCGAAACGAGTGTGGCGCAAAATCCAGCACAACAGGAAACGA +CACAATCATCATCAACAAATGCAACTACGGAAGAAACGCCGGTAACTGGTGAAGCTACTACTACGACAACGAATCAAGCT +AATACACCGGCAACAACTCAATCAAGCAATACAAATGCGGAGGAATTAGTGAATCAAACAAGTAATGAAACGACTTCTAA +TGATACTAATACAGTATCATCTGTAAATTCACCTCAAAATTCTACAAATGCGGAAAATGTTTCAACAACGCAAGATACTT +CAACTGAAGCAACACCTTCAAACAATGAATCAGCTCCACAGAGTACAGATGCAAGTAATAAAGATGTAGTTAATCAAGCG +GTTAATACAAGTGCGCCTAGAATGAGAGCATTTAGTTTAGCGGCAGTAGCTGCAGATGCACCGGTAGCTGGCACAGATAT +TACGAATCAGTTGACGAATGTGACAGTTGGTATTGACTCTGGTACGACTGTGTATCCGCACCAAGCAGGTTATGTCAAAC +TGAATTATGGTTTTTCAGTGCCTAATTCTGCTGTTAAAGGTGACACATTCAAAATAACTGTACCTAAAGAATTAAACTTA +AATGGTGTAACTTCAACTGCTAAAGTGCCACCAATTATGGCTGGAGATCAAGTATTGGCAAATGGTGTAATCGATAGTGA +TGGTAATGTTATTTATACATTTACAGACTATGTAAATACTAAAGATGATGTAAAAGCAACTTTGACCATGCCCGCTTATA +TTGACCCTGAAAATGTTAAAAAGACAGGTAATGTGACATTGGCTACTGGCATAGGTAGTACAACAGCAAACAAAACAGTA +TTAGTAGATTATGAAAAATATGGTAAGTTTTATAACTTATCTATTAAAGGTACAATTGACCAAATCGATAAAACAAATAA +TACGTATCGTCAGACAATTTATGTCAATCCAAGTGGAGATAACGTTATTGCGCCGGTTTTAACAGGTAATTTAAAACCAA +ATACGGATAGTAATGCATTAATAGATCAGCAAAATACAAGTATTAAAGTATATAAAGTAGATAATGCAGCTGATTTATCT +GAAAGTTACTTTGTGAATCCAGAAAACTTTGAGGATGTCACTAATAGTGTGAATATTACATTCCCAAATCCAAATCAATA +TAAAGTAGAGTTTAATACGCCTGATGATCAAATTACAACACCGTATATAGTAGTTGTTAATGGTCATATTGATCCGAATA +GCAAAGGTGATTTAGCTTTACGTTCAACTTTATATGGGTATAACTCGAATATAATTTGGCGCTCTATGTCATGGGACAAC +GAAGTAGCATTTAATAACGGATCAGGTTCTGGTGACGGTATCGATAAACCAGTTGTTCCTGAACAACCTGATGAGCCTGG +TGAAATTGAACCAATTCCAGAGGATTCAGATTCTGACCCAGGTTCAGATTCTGGCAGCGATTCTAATTCAGATAGCGGTT +CAGATTCGGGTAGTGATTCTACATCAGATAGTGGTTCAGATTCAGCGAGTGATTCAGATTCAGCAAGTGATTCAGACTCA +GCGAGTGATTCAGATTCAGCAAGCGATTCCGACTCAGCGAGCGATTCCGACTCAGACAATGACTCGGATTCAGATAGCGA +TTCTGACTCAGACAGTGACTCAGATTCCGACAGTGACTCAGATTCAGATAGCGATTCTGACTCAGACAGTGACTCGGATT +CAGATAGCGATTCAGATTCAGATAGCGATTCAGATTCCGACAGTGATTCCGACTCAGACAGCGATTCTGACTCCGACAGT +GATTCCGACTCAGACAGCGATTCAGATTCCGACAGTGATTCCGACTCAGATAGCGATTCCGACTCAGATAGCGACTCAGA +TTCAGACAGCGATTCAGATTCAGACAGCGATTCAGATTCAGATAGCGATTCAGATTCCGACAGTGACTCAGATTCCGACA +GTGACTCGGATTCAGATAGCGATTCAGATTCCGACAGTGACTCAGATTCCGACAGTGACTCAGACTCAGACAGTGATTCG +GATTCAGCGAGTGATTCGGATTCAGATAGTGATTCCGACTCCGACAGTGACTCGGATTCAGATAGCGACTCAGACTCGGA +TAGCGACTCGGATTCAGATAGCGATTCGGACTCAGATAGCGATTCAGAATCAGACAGCGATTCAGATTCAGACAGCGACT +CAGACAGTGACTCAGATTCAGATAGTGACTCGGATTCAGCGAGTGATTCAGACTCAGGTAGTGACTCCGATTCATCAAGT +GATTCCGACTCAGAAAGTGATTCAAATAGCGATTCCGAGTCAGTTTCTAACAATAATGTAGTTCCGCCTAATTCACCTAA +AAATGGTACTAATGCTTCTAATAAAAATGAGGCTAAAGATAGTAAAGAACCATTACCAGATACAGGTTCTGAAGATGAAG +CAAATACGTCACTAATTTGGGGATTATTAGCATCAATAGGTTCATTACTACTTTTCAGAAGAAAAAAAGAAAATAAAGAT +AAGAAATAAGTAATAATGATATTAAATTAATCATATGATTCATGAAGAAGCCACCTTAAAAGGTGGCTTTTTTACTTGGA +TTTTCCAAATATATTGTTTGAATATAATTAATAATTAATTCATCAACAGTTAATTATTTTAAAAAGGTAGATGTTATATA +ATTTGGCTTGGCGAAAAAATAGGGTGTAAGGTAGGTTGTTAATTAGGGAAAATTAAGGAGAAAATACAGTTGAAAAATAA +ATTGCTAGTTTTATCATTGGGAGCATTATGTGTATCACAAATTTGGGAAAGTAATCGTGCGAGTGCAGTGGTTTCTGGGG +AGAAGAATCCATATGTATCTAGTCGTTGAAACTGACTAATAATAAAAATAAATCTAGAACAGTAGAAGAGTATAAGAAAA +GCTTGGATGATTTAATATGGTCCTTTCCAAACTTAGATAATGAAAGATTTGATAATCCTGAATATAAAGAAGCTATGAAA +AAATATCAACAGAGATTTATGGCTGAAGATGAGGCTTTGAAGAAATTTTTTAGTGAAGAGAAAAAAATAAAAAATGGAAA +TACTGATAATTTAGATTATCTAGGATTATCTCATGAAAGATATGAAAGTGTATTTAATACTTTGAAAAAACAAAGTGAGG +AGTTCTTAAAAGAAATTGAAGATATAAAAAAAGATAACCCTGAATTGAAAGACTTTAATGAAGAGGAGCAATTAAAGTGC +GACTTAGAATTAAACAAATTAGAAAATCAGATATTAATGTTAGGTAAAACATTTTATCAAAACTATAGAGATGATGTTGA +AAGTTTATATAGTAAGTTAGATTTAATTATGGGATATAAAGATGAAGAAAGAGCAAATAAAAAAGCAGTTAACAAAAGGA +TGTTAGAAAATAAAAAAGAAGACTTAGAAACCATAATTGATGAATTTTTTAGTGATATAGATAAAACAAGACCTAATAAT +ATTCCTGTTTTAGAAGATGAAAAACAAGAAGAGAAAAATCATAAAAATATGGCTCAATTAAAATCTGACACTGAAGCAGC +AAAAAGTGATGAATCAAAAAGAAGCAAGAGAAGTAAAAGAAGTTTAAATACTCAAAATCACAAACCTGCATCTCAAGAAG +TTTCTGAACAACAAAAAGCTGAATATGATAAAAGAGCAGAAGAAAGAAAAGCGAGATTTTTGGATAATCAAAAAATTAAG +AAAACACCTGTAGTGTCATTAGAATATGATTTTGAGCATAAACAACGTATTGACAACGAAAACGACAAGAAACTTGTGGT +TTCTGCACCAACAAAGAAACCAACATCACCGACTACATATACTGAAACAACGACACAGGTACCAATGCCTACAGTTGAGC +GTCAAACTCAGCAACAAATTATTTATAATGCACCAAAACAATTGGCTGGATTAAATGGTGAAAGTCATGATTTCACAACA +ACGCATCAATCACCAACAACTTCAAATCACACGCATAATAATGTTGTTGAATTTGAAGAAACGTCTGCTTTACCTGGTAG +AAAATCAGGATCACTGGTTGGTATAAGTCAAATTGATTCTTCTCATCTAACTGAACGTGAGAAGCGTGTAATTAAGCGTG +AACACGTTAGAGAAGCTCAAAAGTTAGTTGATAATTATAAAGATACACATAGTTATAAAGACCGAATAAATGCACAACAA +AAAGTAAATACTTTAAGTGAAGGTCATCAAAAACGTTTTAATAAACAAATCAATAAAGTATATAATGGCAAATAATTAAT +GCATGGCTGCAAAGCAAATAATGAGTTTGTCGTAAAAATAACAACATTTTAAACTAGCAATAAATAATATCAAAGTCATC +ATTTCAATGATGCAATCTAGTATAGTCCACATTCTAAACAGGTGTGGACTATTACTTTTTTCACTTTATATTACGAAAAA +ATTATTATGCTTAACTATCAATATCAATAATTAATTTTAAGCTGAAAAACAATAAAAATGTTAAGACAACGTTTACTTCA +AGTTAATTATTATACTGAAAATTCTGGTATATAATGCTGTTAGTGAATATAACAGGGAAATTATATTGGTTATAATATTG +AGTCTATATAAAGGAGAAATAACAGATGAAAAAGAAATTATTAGTTTTAACTATGAGCACGCTATTTGCTACACAAATTA +TGAATTCAAATCACGCTAAAGCATCAGTGACAGAGAGTGTTGACAAAAAATTTGTAGTTCCAGAATCAGGAATTAATAAA +ATTATTCCAGCTTACGATGAATTTAAGAATTCGCCAAAAGTAAATGTTAGTAATTTAACTGACAATAAAAACTTTGTAGC +TTCTGAAGATAAATTGAATAAGATTGCAGATTCATCGGCAGCTAGTAAAATTGTAGATAAAAACTTTGTCGTACCAGAAT +CAAAGTTAGGAAACATTGTGCCAGAGTACAAAGAAATCAATAATCGCGTGAATGTAGCAACAAACAATCCAGCTTCACAA +CAAGTTGATAAGCATTTTGTTGCTAAAGGCCCAGAAGTAAATAGATTTATTACGCAAAACAAAGTAAACCACCACTTCAT +TACTACGCAAACCCACTACAAGAAAGTTATTACTTCATACAAATCAACACATGTACATAAACATGTAAATCATGCAAAGG +ATTCTATTAATAAACACTTTATTGTTAAACCATCAGAATCGCCTAGATATACACATCCATCTCAATCTTTAATTATCAAG +CATCATTTTGCAGTTCCTGGATATCACGCGCATAAATTTGTTACACCAGGGCATGCTAGCATTAAAATTAATCACTTTTG +TGTTGTGCCACAAATAAATAGTTTCAAGGTAATTCCACCATATGGTCACAATTCACATCGTATGCATGTACCAAGTTTCC +AAAATAACACAACAGCAACACATCAAAATGCTAAAGTAAATAAAGCATATGACTATAAATACTTCTATTCTTATAAAGTA +GTTAAAGGTGTGAAGAAATATTTCTCATTTTCACAATCAAATGGTTATAAAATTGGGAAACCATCATTAAATATCAAAAA +TGTAAATTATCAATATGCTGTTCCAAGTTATAGCCCTACACACTACGTTCCTGAATTTAAGGGTAGCTTACCAGCACCAC +GAGTATAAAAATTGGCACTAAGTTTACGAGATATGATAAATACCTATTATTTTAAATATAGTCTACAATCTATGTGGTTG +TAGGCTGTATTTTTTGCAGTTTATCAATAAACACCCATCAACAAATTATACCGTTTTTCTACTTTGAAAGTTGGAAGTAA +CATAATCTTAAATAAATATATTATTAATTAAGATAAATATAATACTCAAGATTATTGTTAATAGTTTGTTCATCGCAAGT +TAATTATTGTTTCTAAAATATTGGTATATAATTTTCAATGGCGAAGAAAACAGGGTGAAAAAGTCGGTTTTTATATCAAA +GCAAATAAGGGAGCATAAACAAATGAAAAGGAAAGTATTAGTACTAACAATGGGTGTAATTTGTGCAACTCAATTATGGC +ATTCTAATCACGCAAACGCATTAGTAACAGAGAGTGGCGCAAATGATACTAAGCAATTTACTGAAATTGTATCGGAAGAG +AAAGTTATAACAGTTGAACATGCTCAAATTAATATTTTTCAATCTAATAGCAATTCAAACTTGATGGAGTTCAACATATT +AACAATGGGCGGTAAATCAGGAGCTATGGTTGGTTATAGTGAAATTGACTCATCACATTTCACAGACCGTGACAAACGCG +TTATTAGACGTGATCATGTTAAAGAAGCACAAAGCTTAGTAGAGAACTATAAAGATACACAAAGTGCTGATGCTAGGATG +AAAGCCAAACAAAAAGTTAACACATTAAGCAAACCGCATCAAAACTATTTCAATAAACAAATTGATAAGGTTTATAATGG +ATTACAACGCTAATCCAAAGTAAATTATAAGTTATACATCTCGTTTTTAAATGACAATTTATCCCCGTAAATATTATAAA +TAATCTTTTCAAATTCCACATAGATATAGAGACACTAATAAACCTCTTTGTCTCGATATGATAGTCTGCAACGATTCATG +TTGTAGGCTTTTTAATTTTACAAATAAGGCTAAATATATAAGTTCTGGCACCTAAAATATAGAAAATACATAAAAGTAAG diff --git a/src/busco/busco_run/test_data/protein.fasta b/src/busco/busco_run/test_data/protein.fasta new file mode 100644 index 00000000..3224f32e --- /dev/null +++ b/src/busco/busco_run/test_data/protein.fasta @@ -0,0 +1,64 @@ +>341721at2759_1001832_1:000010 +MASRPVKKRKLTPPGDDEASSRKSGGKIQKAFLKNAANWDLEQDYETRARKGKKKEKESTRLPLKLPGGRVQHVSAPDNDFQAIESDEDWLDGAEDVSEDEESKDKKAPEEPEKPEHEQILEAKEELAKIALMLNESPDENTGAFKALAKIGQSRIITIKKLALATQLTVYKDVIPGYRIRPVAEDGPEEKLSKDVRKLRTYETCLISGYQAYVKELTKHAKTGHANGLASVAITCACNLLTAVPHFNFRSDLVKILVGKLSTRRVDDDFNKCLQALETLFEEDEEGRPSMEAVSLLSKMMKAREYQVNESVVNLFLHLRLLSDFSGKGSKDSVDRMDDGPSKKPKSKREFRTKRERKQIKEQKALQKDMAQADALVQHEERDRMEGETLKLVFGTYFRVLKMRVPHLMGAVLEGLSKYAHLINQNFFGDLLEALKDLIRHSDASEKDDAEEKEDEEADDDAPVRNPSREALLCTTTAFALLAGQDAHNARADLHLDLSFFTTHLYQSLFPLSLHPDLELGARSLHLPDPDKPSQNRKSNSSNKVNLQTTTVLLIRCLTAVLLPPWNVRSVPPVRLAAFAKQLMTAALHVPEKSAQALLALLADVAGTHGRRIAALWNTEERKGDGAFNPLAESAEASNPFAATVWEGEILRRHYCPAVRRGVGIVEKSLSLAER +>296129at2759_1069680_1:000010 +MMKKKQIDSRIPTLIKNGVQEKKRTLFVIVGDRGRDQIVNLHWLLSQTRIASRPSVLWMYKKDLLGFTSHRKKREAKIKKEIKKGIRDPNEATTPFELFISVTNIRYTYYKESEKILGQTFGMLVLQDFEAITPNLLARTIETVEGGGIIVILFKTMENLKQLYTMTMDIHSRYRTEAHQDVVARFNGRFILSLGHCSSCLFVDDELNVLPISEAKKVKPLPKPQLEEPKKELEELKQKYEDKQLLRSLIDVAKTVDQARALITFVEAISEKTLRSTVALTAARGRGKSAALGLAISAAVAYGYSNIFITSPNPENLKTLFEFTFKGFNSLKYEEHIDYDIIQSLNPSFNKSIVRVNIFRNHRQTIQYIHPSDAYVLGQAELLVIDEAAAIPMPLVKKLLGPYLTFMASTVNGYEGTGRSLSLKLIQQLREQSRGFAHENTKSGNSEKSMINRSEKLNKESGINSIGGRKLREITLEEPIRYSYGDPVEEWLNKLLCLDINISLKQFLEQGCPHPSQCELYYVNRDTLFSYHPVSESFLQMMMSLYVASHYKNSPNDLQLMADAPAHQLFVLLPPVKEDDNKLPEPLCVIQVALEGEISRESVVNNLTRGYRTGGDLIPWVITEQFQDDKFASLSGARIVRIATNPEYIRMGYGSHALKLLENFYEGKYLNLSEETISESNENIKIINNNLESSLLTDDIKIKDLKIMPPLLLKLSEKKPGLIHYLGVSYGLTPQLYKFWKRAEFIPVYLRQTPNDLTGEHTCLMLKLLQDKSETWLNEFSNDFRKRFLSLLSFSFRSFPTILCLNIIESINNDLIQKDNVHVITKSEIDINLSPFDLKRLESYANNMLDYHTIIDMLPYIADLYFKGRFGKDLKMTGVQSAILLALGLQKRLLEDIEKELNLPSNQVLAMLVKILRKLSSFFKDIYYKAIDNTLPIERKNLKNQLQTHADENDNFRGFIPLKATLKEELDHLSSEMEDSIKEKQRELINSLDLQKYIIKGQEEDWDKAEQHIKNGIYSGKSSVVSIQSHSLKREHESLTDIPHIKKKHQKKHKRKV +>1217666at2759_1073089_1:000010 +MPINQPSNQIKFTNVSVVRLKKGKKRFELACYKNKLLEYRSGAEKDLDNVLQVPTIFLSVSKAQTAPSAELTKAFGANIPADEIRQEILRKGEVQVGERERKEISERVEKELLDIVSGRLVDPTTKRVYTPGMISKALDQLSSASGQMQQTQGEGSGATDEKGAAQPRKPMWTGVAPNKSAKSQALDAMKALIAWQPIPVMRARMRLRVTCPVSILKHSVKAPSGGGASKEKEAPSGNSKSNKGKKGPKSRAARQQDSDAEDGKSDAEAAPKTPSNVKDKILGYIESIESQEVIGGDEWEVVGFAEPGAYKGLNEFVGNETRGRGRVEVLDMTVTHEE +>513979at2759_1159556_1:000010 +MAVVDIQARFSPHHPLEPDLLYEIQSILRLHGLSVDDLFFKWDAYCIRMDLDAQAALSLANVRSLKQSIQDDLEKSHRSTTQVRSERKVAAAPKAVSGGDVYGMLDGLVPSTPAAGGKRSRGVAAGGGGSGLKKKMDSLKMNSSPAGMKEQLSAFNGLPATSFAERANAGDVVEILNAQLPPCEAPLAPFPEPRIKLTAASDQKKMAYKPLAVKLSEASEVLDDRIDEFAALVQDYHGLEDSAFGSAASQGTTEVVAVGRIASDAMEGKLNAAALVLETSRRTGMGLRVPLKMHKVPSWSFFPGQVVALRGTNATGGEFVVEQVLDVPLLPSAASTPSALEAHRARMSGVPPGGGAAAATTDSDAAAPAPAPAPLTILYAAGPYTADDNLDYEPLHALCSQAADALADALVLAGPFLDIDHPLVAAGDFDLPPEDEAALDPDTATMSAVFRHLVAPALNRACAANPHLTVVLVPSVRDVLARHVSWPQDAIARKELGLAKAARIVSNPMTLSMNEVVVGVSSQDVLHELRNEECSRACPPGDLMGRLCRYLVEQRHYFPLFPPTDRARLPRTGTQSGLATGAVLDPSYLRLGEMVNVRPDVMVVPSSLPPFAKASSVVESVLAINPGPLSKRKGAGTFARMTLHAPPVGGGSEMTSHRVFDRARVEIVRI +>543764at2759_1165861_1:000010 +MALGRAARPVGWTDCCAAVEKKPNYKSGMTQPARTITAGDNLLLKLPSGQTRTIKNVTSDSSISLGKFGKFQTNELIDQPFGLTFDILEDGKLVRNEQINLALELNPMLDELNSFESIKGMANGISNVEDIEATNEMIKESDGAQKLTNVEIEELKKSGLSGREIILRQIQQHSAFELKSEFSKAKYIKRKEKKFLKMFTCIDPTIHNMSQYLFENHNFAIKGLRPDTLSQMLSLSNVRPGWKGIVVDDIGGLLVAAVLIRMGGEGTIFVLNNADSPPDLHLLELFNLPKSVLGPLKSLNWAQTEADWTTSDIEELLLLHRDPPQPLPILDSTLPDPQLKQLSQRTKKQPNNRSKSMRKFERVQELLSMRQEFLDTQFEGLLTCSEYEPESIVTKLVNKLSGSSTIVIYSCHLRPLSDLQTLLKKSSMPSTSSSSLGGSSSLVEQNELTKRMKENKTEFIQITISEPWLRAYQVLVGRTHPEMAGTHHGGFVFSAIKVFNSCS +>1558822at2759_1266660_1:000010 +MSIAEILPLEIIDKTVGQPVLVMLTSHREFSGTLVGYDDFVNVVLEEVVEYDHDQEIKRHAGKMLLSGNNIAMLVPGGKRVQ +>1287094at2759_1291522_0:000010 +MGNILVKKNRVTITEADRAILTLRTQRRKMEEHRRRVEALMERETTVARTLVAKQQRPAALLALKKKRLHETQLEGLDNCLLTLEETLTQVESAQRTARLMAALKQGADVLSALQRAMPLESVEQLMEQGAESREYEMRLQALLGESLGEDQSAAAERELDEMEAQLIEEDVLDLPKVPSHAVARPASARAIGQAASERQLEPEIAA +>83779at2759_1296121_1:000010 +MCGLTLTIRPLSLSLSSPSVSDCSSSDSTEDADLALLDSFRSTNAQRGPDSQRTFKHTVTLDDDDNGVTTTTTTKSTTKSKVEICLTATVLGLRGDLTAQPLVGNRGVLGWNGQVFEGIDIGTEENDTRKIFERLEKGERVEDVLSGVEGPFAFIYLDLENDILHYQLDPLSRRSLLIHPAEVAVDSNPSVTRHFILSSSRSTLAREHGVDMRALLGGEGGTIDLRRIKVVQNQGFLTMDMSDALKHRHTLSPDQDASCSSSSGSWTKVAPINTALPPDNLPLDNPKIKEEVPKFIEQLKESVKRRVENIPNPEKGCSRVAVLFSGGIDCTFLAYLIHLCLPPEDPIDLINVAFSPAPKLSSLSSNGADKGKGKSPALPAAPTYDVPDRLSGRDALVELKQVCPDREWRFVEIDVPYDEARAHRQNVLDLMYPSSTEMDHSLALPLYFASRGYGSVRKEGSNHSEPYRVKAKVYISGLGADEQLGGYARHRHAYQREGWQGLISETQMDIARLPTRNLSRDDRMLSSHARDARYPYLSLSFISYLSSLPVHLKCDPRLGEGQGDKILLRKAVESVGLVRASGRVKRAMQFGTRSSKLGGRGSGVKGPKAGERQVE +>1057950at2759_1314783_1:000010 +MSSRQATHADSWYVGDGRRLDSELSKNLAAVEGDANYSPPIKGCKAVIAPHAGYSYSGRAAAWAYKSIDTTGIKRIFILGPSHHVYLDGCALSKCEKYETPLGELPIDLDTVKELRATGEFQDMDIQTDEDEHSIEMHLPYVRKVFEGLDIAIVPILIGAINLNKENKFGTVLAPYLAKDDTFFVISSDFCHWGTRFQYTFYYPRPPPTSTPAIRLSKADPNPSTLATHPIHASISAIDHEAMDLMTMPPQTAQQAHIDFAEYLRTTKNTICGRHPIGVLLGALAVLQSQGRVPHLKFVRYEQSSQCQTVRDSSVSYASAYITV +>453044at2759_1330018_1:000010 +MPAAPQDPFFKSIGSAAADTEALREQPDEQDEQETDLEPIDEDRPLQEVESLCMSCGEQGVTRMLLTSIPYFREVIVMSFRCEHCGNQNNEIQSASTIREHGAMYTVKILNQGDLNRQLVKSEAATVTIPEFELTIPPLRGQLTTVEGTLRDTIQDLAADQPLRRIQDPPTFDKIEALLAKLKEVVPDDEDEAAPTMKERHPEDPVRPFTVILDDPTGNSFIEFSGSMSDPKWSLREYARSMDQNITLGLSQPEDEEKEKVTQKGGPFTEEDEDGLPAEEVFIFPGICSSCGHPVDTRMKKVNIPYFKDIIIMSTNCSACGYRDNEVKSGGAISDKGKRITLKVEDAEDLSRDILKSETCGLEIPEIDLALHAGTLGGRFTTVEGILTQVYDELSEKVFRGDSVGSANSKDNQEFETFLGSMKEVMTAARPFTLILDDPLANSYLQNLYAPDPDPNMEIVTYDRTFDQNEDLGLNDMKVEGYEAPS +>1323575at2759_1392248_1:000010 +MSQPQPPPLRYIRYEPSREDEYVAAMRQLISKDLSEPYSIYVYRYFLYQWGDLCFMTVDDSRPEDPIVGVVVSKLEPHRGGPMRGYIAMLAVREEYRGRGIATKLVRMAIDAMIARDADEIALETEITNTAAMKLYERLGFLRSKRLHRYYLNGNSAYRLVLYLKEGVGNMRTSFDPYAAPAEARPEMSGAAAVPAAPAPPPLLQGNGR +>160593at2759_139723_0:000010 +MADAELAKALKDLPNRVLNVPVEERPELFQNVIAVLPNPGINATIVRGICKVIGTTLTKYKDPESQTLVKELLVAVLKQHPDLTYEHFNAVLKALLAKDLAGAPPIKAAQASALALGWANLIALHADHETAVGKKEFPKLLEVQAGLYQLSLTSGIQKISDKAYSFLRDFFASDESLAQRYFDKLLAMEPSSGVIVMLCTIVRYLHQEQGTVELLDQHKPKLLDHLVKGLITVKTKPHASDIVACSILLKAITKDELRTIIVPALQRSMLRSAEVILRAVGAIVNEIELDVSDYALDLGKPLVQNLASKEETVRQEAVESLKQVALKCGTPNAIETLLKEVFAVLNGSGGKITVAELRINLLQGAGNLSYNKIPSQKIQTILPAACDHFTKVIEAEIQEKVVCHALEMFGLWTVNHRGEIPAKIVQLFKKGLDAKAQTIRTSYLQWFLSCLHDGKLPNGIDFTTTLSKIVERAAQSPTQTPVVSEGVGAACILLLTNPSVSEKLKDFWNIVLDTNKSPFLSERFLSTTNAETRCYVMVICEQLLIKHRNELKGSSTTDPLIRAATVCVMSAQAKVRRYCLPLVTKIVNSEDGVSLAKFLLAELTRYVECTKILSEGEPAEEGIAPAQALVDAVCTVCNVEKVANPDAQSLALSALLCSHHPAAVSVRGDLWESILERYGLYGKQFIALNTAQIEEVFFNSYKATAMYENTLATLSRISPELILSVLVKNVTDQLNNSRMSNVTDEEYFTYLTPDGELYDKSVIPNTDEQVQTAHLKRENKAYSYKEQLEELQLRRELEEKRRKEGKWKPPQLTPKQKEVIDKQREKENAIKARLQALHDTITTLISQIEGAAKGTPKQLPLFFPALLPAILRVFSSPLAAPAMVKLYYRLKDICFGEERVELGRDIAIATIRLSKPHCDLEESWCTANLVELVSDILVALYDETIDMYNVHREEEASKRYLLDAPAFSYTFEFLKRALTLPEAKKDESLLINGVQIIAYHAQLKGDTVDGKDLGDVYHPLYMPRLEMIRLLLRLIQQHRGRVQTQAVAALLDVAESCSGREYTTRAEQREIEALLVALQEELDAVRDVALRALAIMIDVLPSIADDYEFGLRLTRRLWVAKHDLSADIKQLATGIWQDGAYEVPIVMADELMKDIIHPELCVQKAAAAALVSILVEDSSTIDGVVEQLLEIYREKVVMIPAKLDQFDREVEPAIDPWGPRRGVAITLGSISPFLTPELVKSVIQFMVRSGLRDRQEIVHKEMLAASLAIVEHHGKDSVTYLLPTFEYFLDKAPSKGAYDNIRQAVVILMGSLARHLDREDERIQPIIDRLLAALETPSQQVQEAVANCIPHLIPSVKDKAPEIVKKLLQQLVKSEKYGVRRGAAYGIAGVVKGLGILSLKQLDIMSKLTHYIQDKKNYKSREGALFAFEMLCSTLGRLFEPYIVHVLPHLLQCFGDSSVYVRQAADECAKTVMAKLSAHGVKLVLPSLLNALDEDSWRTKTASVELLGSMAFCAPKQLSSCLPSIVPKLMEVLGDSHIKVQEAGANALRVIGSVIKNPEIQAIVPVLLTALEDPSSKTSACLQSLLETKFVHFIDAPSLALIMPVVQRAFMDRSTETRKMAAQIIGNMYSLTDQKDLTPYLPNIIPGLKTSLLDPVPEVRGVSARALGAMVRGMGESSFEDLLPWLMQTLTSESSSVDRSGAAQGLSEVVGGLGVEKLHKLMPEIIATAERTDIAPHVKDGYIMMFIYMPSAFPNDFTPYIGQIINPILKALADENEYVRDTALKAGQRIVNLYAESAITLLLPELEKGLFDDNWRIRYSSVQLLGDLLYKISGVSGKMTTQTASEDDNFGTEQSHKAIIRSLGADRRNRVLAGLYMGRSDVSLMVRQAALHVWKVVVTNTPRTLREILPTLFSLLLGCLASTSYDKRQVAARTLGDLVRKLGERVLPEIIPILERGLSSDQADQRQGVCIGLSEIMASTSRDMVLTFVNSLVPTVRKALADPLPEVRHAAAKTFDSLHTTVGARALEDILPSMLESLADPDPDVAEWTLDGLRQVMAIKSRVVLPYLIPQLTAKPVNTKALSILASVAGEALTKYLPKILPALLAALAAAQGTPEEVQELEYCQAVILSVSDEVGIRTIMDTVMESTKSEIPETRRAAATLLCAFCTHSPGDYSQYVPQLLRGLLWLLSDGDREVLQRSWDALNAVTKTLDSAQQIAHVTDVRQAVKFASSDLPKGGELPGFCLPKGITPLLPVFREAILNGLPEEKENAAQGLGEVIKLTSPASLQPSVVHITGPLIRILGDRFNAGVKAAVLETLAILLHKVGIMLKQFLPQLQTTFLKALHDPSRTVRIKAGHALAELIVIHTRPDPLFVEMHNGIKSADDSAVRETMLQALRGIVTPAGDKMTEPLRKQIYATLAGMLAHPEDVSRAAAAGCFGALCRWLTPEQVDDALTSHMLNEDYGDDATLRHGRTAALFVALKEHPGGIVTTKYEPKICKVITGALVSDKISVAMNGVRAGGYLLQYGMTDGTAKLSTAVIGPFVKSMNHSSNEVKQLLAKTCTYLARVVPAERIAPEYLKLAIPMLVNGTKEKNGYVRSNSEIALVHVLRLRDGEEFHQRCITLLEPGARESLSEVVSKVLRKVAMQAVGKEEELDDTILT +>1346432at2759_1447883_1:000010 +MSSMRNAVQRRVHRERAQPANREKWGILEKHKDYSLRARDYSVKKAKLQRLREKADTRNPDEFAFGMMSGKSRTQGKHGARDTESAALSLETVKLLKTQDAGYLRVVGERIRRQMMAVDEEVRVQEGISGVSANGAAAGGGGGGGRKVVFVDSVEEQRERALEDEGKSDDDEEQGDFDEVDEEEQRQQKTQPKSKKQLEAEKLAQKEMLKARKLKIKAAEARSKKLQALTDQHKNIVAAEQELDWQRGKMENSVGGVNKHGLRWKVRERKR +>761109at2759_198730_1:000010 +MAMTFTEDSIKELRLRLEDAVVKCSERCLYQSAKWAAEMLNSLVSTDGNDTDAESPMETDLQPTVNPFSLQSDPTEATLELQEAHKYLLAKSYFDTREYDRCAAVFLPPTIPPVPLSTVSPNVKSRASLTPQKGKRKSFIRPGLKSGQALPRNPYPNLSQKSLFLALYAKYLAGEKRRDEETEMVLGPADGGMTVNRELPDLARGLEGWFEERRERGLQDQGQGWLEYLYAVILIKGKNEEEAKKWLVRSVHLFPFHWGAWQELNDLLPSVDDLKQVAETLPQNIMSFIFQVHCSQELYQATDETHQTLNGLESIFPTSAFLKTERALLYYHSRDFEDASAIFADILIDSPHRLDSLDHYSNILYVMGARPQLAFVAQLATATDKFRPETCCVVGNYYSLKSEHEKAVMYFRRALTLDRNFLSAWTLMGHEYIEMKNTHAAIESYRRAVDVNRKDYRAWYGLGQAYEVLDMCFYALYYYQRTAALKPYDPKMWQAVGTCYAKMNQIPQSIKAMKRALVAGAYYEQRADAATADHPAAGRKILDPDLLHQIALLYEKMNNEDEAAAYMELTLQQESGEIERTETDSDDDDGDDNSDDGTTQRRSRRQRRRQKSRDDDNEIEAVGGTGVTATTSKARLWLARWALKHGDLNRADQLAGELCQDGVEVEEAKALMRDVRARREGGGG +>1617752at2759_2004952_1:000010 +MPSSFVTPGQQRYLRACMVCSIVMTYSRFRDEGCPNCDEFLHLAGSQDQIESCTSQVFEGLITLANPAKSWIAKWQRLDGYVGGVYAIKVSGQLPDEIRTTLEDEYRIQYIPRDGTQTEADA +>1588798at2759_215358_0:000010 +MTLPPTQQEPHTPEAFSLFVSFNHREPQNDDVMADLGIKAGDKVMMVWTQPSAPEGLKQHAEELAAIVGADGKVSVENLERLLLSSHSASSFDCVLSCLLADSSPVHTSETLEELARVLKPGGKLVLDEAVTGAETSQVRTAEKLISALKLSGFMSVTEVSKAELTAEALSALRTATGYQGNTLSRVRVSASKPNFEVGSSSQIKLSFGKKTPKPAEKPALDPNTVKMWTLSANDMGDDDVDLVDSDALLDEEDLKKPDPASLKVSCRDSGKKKACKNCSCGLAEELEQESTGKQKTNLPKSACGSCYLGDAFRCASCPYAGMPAFKPGEKIVLDKKTLTDA +>1275837at2759_28005_1:000010 +MSSRDKASPSSPKETKGEHHLNEESDNDNNERRDEQQVTASAYLPSASRVDVHPLVLLSLVDHFARMNTKVRQKKRVVGLLLGRYKTDAAGTQVLDINNSFAVPFDEDPHNSDVWFFDTNYAEEMFVMHRRVHPKTKIVGWYASGPTVQQNDMLLHLLVADRFCANPVYCVVNTDPSHKGVPVLAYTTVQGREGARSLEFRNIPTHVGAEEAEEIGVEHLLRDLTDSTVTTLSSQLEERERSLEHMARVLVQIEEYLSDVASGALPASEDVLEALQELISLQPETYLKKKSLELNRFTNDRTIATFLGSIARCIGGLHEVILNRRVLARELKEIKARRAEAEEQRMDNEKNKIAEASPERKQ +>1264469at2759_29058_0:000010 +MRPPLAIVRTYCTTAAPKSSNFIDEMKRNFIATNTFQKTLLSCGSAAISLLNPHRGDMIACLGEVTGESAIKYMRQKMTETEEGTEILKEKPRINSGTVSFDKLSQMPDNTLGRVYADFMTENNITADSRLPVQFIEDPELAYVMQRYREVHDLVHATLFMRTSMLGEVTVKWVEGIQTRLPMCISGGIWGAARLKPKHRQMYLKYYLPWAIKTGNNAKFMQGIYFEKRWDQDIDDFHKEMNIVRLVKK +>673132at2759_326594_0:000010 +MTLLTVFKQFKKFQDAGKSVARSLSIKDDQESKKTCLYDLHIENNGKMVNFSGWLLPIQYRDSITASHQHTRTHASLFDVGHMLQSHVSGCDSGEFLESLTTADLQNLAQGGAALTVFTNKSGGILDDLIITKDRNDRFFVVSNAGRRNEDIELMLGRQAEMKSQGKNVTIEFLDPLEQGLIALQGPSAATTLQTLVKIDLTKLKFMNSVETKINQKSVRISRCGYTGEDGFEISVNGKDARTISEMILEVPDIKLAGLGARDSLRLEAGFCLYGHDINESITPVEASLQWLIAKRRREAANFPGAEFILEQIKNGPKKKRVGLILGQGPPARENATILTSAGERVGIVTSGGPSPTLGKPIAMGYVPLEHVHTGTPVLTEIRGKTYKALITKMPFVKPHYYSDKR +>887370at2759_331117_1:000010 +MVVRSFLPLLSLLIALATFTSAASDYHEALVLQPLPQSSLLASFNFRGNTSQEAFDQRHFRYFPRALGQILQHTHTKELHIRFTTGRWDAESWGTRPWNGTKEGNTGVELWAWIDAPDSESAFARWISLTQSLSGLFCASLNFIDSTRTTRPVVSFEPIGDHSPSSDLHLLHGTLPGEVVCTENLTPFLKLLPCKGKAGVSSLLDGHKLFDASWQSMSVDVRPVCPQGGECLMQIEQTVDIVLDIERSKRPRDNPIPRPVPNDQLNCDNSKPYHSDDTCYPLERGSGKGWSLNEIFGRTLNGVCSLDEGQRPGEEAICLRVPHEQGVYTTSGVEETKRPDGYTRCFTLQPSGTFDLVIPEQSHTSLAPRDEPVLSAERTIVGHGQERGGMRIIFDNPSDAHPVDFIYFETLPWFLRPYVHTLRATITGRDGATRSVPVSHIVKETFYRPAIDRERGTQLELALSVPAASIVTLTYDFEKAILRYTEYPPDANRGFNVAPAVIKLSSANGNTIAHDTPIYMRTTSLLLPLPTPDFSMPYNVIILTSTVIALAFGSIFNLLVRRFVAADQAAALTAQTLKGRLLGKIVALRDRISGKRSKVE +>166920at2759_38123_0:000010 +MAFLDFVFPLSKDELLERSDSQYYVRDQVTTSELPEKLKGCFESLHDDGPLFILENFDTLYGLLAHFKSVDFNQLHKVYTKLLIKSITEFIPILENYFSKETPDDELQNKYLNVIKMTVYILTEFIISFESRLQKEYQKVVIDVRARKVKVRAAIKHKEKYNWDWDFHLSNGLNSIHQLLKAKINKLWDPPVVEEEFVNTIANCCYKIIEDPCIASVKHKELRIFIFQVIGYLIKKYNHGISCTVKIVQLLKNCDHLVSPLAQAVTMFIRNHGCKSLVREIVREISEMDDGNEAAGQGQDNSKMVAAFLNEIAAEGPEYVIPAMDELLLNLEKESYMMRNCTLTILTELLLQVYKKENLSSEAKDQRDEYLNSLMEHIYDVHTFVRTKVLQLFQKLVIEKALPLAFTLQLVDRAIGRLMDKSSNVVKYAVQLLRTMIVSNPFAAKLGVEELKKKLAEAKATLTELEKNLPETSAQLSLVDEWNNIHYPVLLKIIREILEDGMYGCFLFYFL +>1275837at2759_402676_1:000010 +MESMNDMFKKINAREKLVGWYHTGPQLRSSDLEINNLFKKYIPNPVLVIIDVQSKAVGLPTSAYFAVDEIKDDGTKSSLTFVHLPSSIEAEEAEEIGVEHLLRDTRDITAGTLATRVTEQVQSLRALEQRLDEIAVYLRKVVDGQLPINHTILGELQGVFNLLPNIFKTSNENDPLGLENGDERSFNINSNDQLMTVYLSSIVRSVIALHDLLDSLAASKAAEQEQDKLDLKQESTDSEKRATTAAVDEDPFMPN +>1284731at2759_42254_0:000010 +MAEAGAVAAEYPSGGRARAARTLLDQVVLPGEELLLPEQEDADGPGGAGERPLQARDPYLKWGVRRACCEIPYVPVRGDHVIGIVTAKSGDTFKVDVGGSEPASLSYLAFEGATKRNRPNVQVGDLIYGQFVVANKDMEPEMVCIDGCGRANGMGVIGQDGLLFKVTLGLIRKLLAPDCEIIQELGKLYPLEIVFGMNGRIWVKAKTIQQTLILANILEACEHMTTDQRKQIFSRLAES +>1228942at2759_45354_1:000010 +MNHDPFQWGRPRDEIYGHYDHKIAQASTSEFPSMHTQQPIITGTSVLGLKFDTGVVIAADHMGSYGSLLRFNNLERLICVGSETIVGVSGDISDFQHIERLLHELETEEEVYDTDGGHNLRAPNIHEYLSRVLYNRRLKMDPLWNAILVAGFNDDRTPFIRYVDLLGVTYGALALATGFGAHLAIPLLRKLVPYDLDYVKVKEADAREAVVNAMRVLYYRDARASDKYTLAVLSFKDGKVDVHFDQELKVTNQSWKFAEKVIGYGSKQQ +>759498at2759_502779_1:000010 +MDGSRGSRKRKAVTRDLGEEPGVVSGNELHLDSADGSLADHSEDLDGSSDSEIELADDLNSDDDEEEEEEEEEDEDEINSDEVPSDIEPKVVGKKSGPGGEVDIIVRGDDTASDDDDDDDDDFESDDRPNYRVVKDANGNERYVYDEINPDDNSDYSETDENANTIGNIPLSFYDQYPHIGYNINGKKIMRPAKGQALDALLDSIELPKGFTGLTDPATGKPLELTQDELELLRKVQMNEITEEGYDPYQPTIEYFTSKLEVMPLSAAPEPKRRFVPSKHEAKRVMKLVKAIREGRILPYKQPAEEDEAEEGVQTYDIWANETPRADHPMHIPAPKLPPPGYEESYHPPPEYLPDEKEKSAWLNTDPEDRETEYLPTDHDALRKVPGYESFVKEKFERCLDLYLAPRVRRSKLNIDPESLLPKLPSPEELKPFPSTCATLFRGHQGRVRTLAIDPTGVWLASGGDDGTVRVWDILTGRQFWSVALSGDDAINVVRWRPGKDAVVLAAAAGDSIFLMVPPVLDPEMEKASFEVVDAGWGYAKTSPSTFTSTDSTKTSPVQWTRPSSSLLDSGVQAVISLGYVAKSLSWHRRGDYFVTVCPGTSTPVSLAIAIHTLSKHLTQQPFRRRLKGGGPPQTAHFHPSKPILFVANQRTIRAYDLSRQTLVKILQPGARWISSFDIHPTSSSTSGGDNLIVGSYDRRLLWHDVDLSPRPYKTLRYHQKAIRAVRYHANYPLFADASDDGSLQIFHGSVTGDLLSNASIVPLKVLRGHKVTGELGVLDLDWHPKEAWCVSAGADGTCRLWM +>375960at2759_51337_0:000010 +MFFREHIFNIIGAFDIPRFVYNSERKKFLPLLMTNHPAPNLLGTAKDKAELYRERYTLLHQRTHRHELFTPPVIGSYPNESGSKFQLKTIETLLGSTTKIGDVIVLGMITQLKEGKFFLEDPTRTVQLDLSQAQFHSGLYTEACFVLAEGKAYYGSINFFGGPSNTSVKTSTKLKQLEEENKDAMFVFVSDVWLDRAEVLEKLHIMFSGYSPAPPSCFILCGNFSSAPYGKNQIQALKDSLKTLADIICEYPNIHQSSRFVFVPGPKDPGFGSILPRPPLAESITSEFRQKIPFSVFTTNPCRIQYCTEEIIIFREDIVNKMCRNCVRFPSSNLDIPNHFVKTILSQGHLTPLPLYVCPVYWARFPSSNLDIPNHGSFPRSGFSFKVFYPSSKTVEDSKLQGF +>919955at2759_5643_1:000010 +MAAPMAVDKAKAPKIDVDEFLTLAISETPAELHPFFESFRSLYSRKLWHQLTNKLFEFFDHPLSKPYRVDVFNKFVRDFGLRLNQLRLVEMGVKVSKEIDNPVTHLQFLTDLLERVNIEKSPEAHVLLLSSLAHAKLLYGDHEGTKNDIDAAWKVLDELSSVDPSVNAAYYGVAADYYKSKAEYAPYYKNSLLYLACIDPAKDLTAEERLLRAHDLGIAAFLGDTIYNFGELPILQENYPFLRQKICLMALIESVFKRGSYDRTMSFQTIAEETHLPLDEVEHLVMKALSLKLIKGSLDQVDQKAQITWVQPRVLSREQIGQLAQRLAAWNSKLHQVEERIAPEVLVNS +>817008at2759_5849_1:000010 +MDKLKTIYIDSALSIIKGALCVILQIPTGRTTESIKKKQNNVGIITVKSIFKEPTISQYNDIKQLIKTKIEENCPFYNYQINRTIAEKIYGDTIYDNYGLSKEINEVNLIILEEWNINCNRNRVLKHSGLIKNIEINKFKYLNNKESLEVHFLVNPKYTFEELNTIYKNEEELNNFLLSPIIKVTNKKIYEIEDKKSEFSYLYEEDILPKNKVLPPSGIENVNYESSKVVTPWDVNIGEEGINYNKLIKEFGCSKISDEHIRKIEKLTNRKAHHFIRRGIFFSHRDLDFLLNYYEQNGYFYIYTGRGPSSLSMHLGHLIPFYFCKYLQDAFNVPLIIQLSDDEKFLFNQNYSLDDINRFTKENVKDIIAVGFNPELTFIFKNTEYANHLYPTVLAIHKKTTLNQSMNVFGFNNSDNIGKISYPSFQIAPCFSQCFPNFLKKNIPCLVPQGIDQDPYFRLSRDIAVKLALYKPVVIHSVFMPGLQGVNTKMSSTKKKDNKNMDSKQDINNSVIFLTDSPEQIKNKINKYAFSGGGATIAEHKEKGADLEKDISYQYLRYFLVDDEKLNEIGEKYKKGEMLSGEIKKILIDILTDLVQKHQEKRNSLTDEDILYFFNDNKSSLKKFKDM +>1426075at2759_61621_0:000010 +MTASQPNPQLPQSLPALKTSGTCARLPSTGRKLHLRIARAHPRVSRELFRRSGCGCGAGLSSAETDIAFLFSASGYRSHILKTMSGSFYFVIVGHHDNPVLKWSFXPAGKAESKDDHRHLNQFIAHAALDLVDENMWLSNNMYLKTVDKFNEWFVSAFVTAGHMRFIMLHDIRQEDGIKNFFTDVYDLYIKFSMNPFYEPNSPIRSSAFDRKVQFLGKKHLLS +>655400at2759_688394_1:000010 +MAASRSPRLSSLLLRTTPLSRPTWQRTLSTRGFATAISNKLDNVYDMVIVGGGIAGTALACSLATNPSMKDYRIALIEAMDLSNTNNWAPATGRYSNRVVSLTPASMQFFEKIGVADELYRDRIQPYNCMKVSDGVTNASIEFDTNLLSSSTNPDDLPIAYMIENVHLQHSILKTLQTSKGKGATVDILQKARVASIRMQEQDAKETKDTLDLSDWPIIEMENGQSLQARLLVGADGVNSPVRSFAKIESLGWDYNMHGVVATFKTDPSRKNDTAYQRFLPTGPIAMLPLGDGHASMVWSMPPDMAHKVKKIPAQAFCTLVNSAFRLSMEDLDYLRSKIDPTTFEPLCDFDSEYNWRQGVAKHGLGDMEMMERELAFPPIVESVDETSRASFPLRMRNSQQYFADRVVLVGDAAHTVHPLAGQGLNQGILDVACLSDILQRGASEGQDIGNLHLLREYASVRYLRNLLMISACDKLHRLYSTDFAPITWIRSLGLSSVNQLDFVKAEIMKYAMGIEQ +>946128at2759_765440_1:000010 +MPTTVCTAKASYKKTPGQLELTETHLQWFADGKKAPSVRVLYAEAASLFCSKEGAAQIRLKLGLVGDDTGHNFTFTSPQSVAYKERETFKKELTNIISRNRSVPNVTTPRPPLNTSISSTTPAISNAPTPRSVVPPSRASTSRAPSVSSDGRTPIVPGSDPTSDFRLRKQVLVSNPELGALHRDLVMSGQITEAEFWEGREHLLLAQTATESQKRGRPGQLVDPRPETVEGGEVKIVITPQLVHDIFEEYPVVAKAYNDNVPNKLSEAEFWKRYFQSKLFNAHRASIRSSAAQHVVKDDKIFDKYLEKDDDELEPRRQRDEGINLFVNLGATREDHGETGNEQDITMQAGRQRGALPLIRKFNEHSERLLNSALGDEPTAKRRRIDAGKEDAYSQIDLDDLHDPEASAGIILEMQDRQRYFEGQMASAASAEAAAGKNLDIRAILGETKVNLHDWETNLAQLKINKKSGDAALLSMTENVSARLEIKMKKNDIPPELFSQMTTCQTAANEFLRQFWLSMYPPAADHQVLAPATPAQKAAKAAKMIGYLGKTHEKVDALIRTAQVEAVDAAKVEIVRAVCFVYIITVNFNANLQAMKPILDAVDRALAFYRSRKPPK +>1287401at2759_870435_1:000010 +MSSSIVGSLTRGCRTPSVNINPHPFFRCRTSLYHGIGKPPSWLHSRTQLWRTIGTSSSKHTPPSSASVSARRPTAIPSYNASREQMYKTRNRNLLMYTSAVVILGVGITYAAVPLYRMFCSATGFAGTPSVVSTSSGRFDPSRLTPDTDARRIRVHFNADRAEALPWKFFPQQKYVEVLPGESSLAFYKARNESKKDIIGIATYNVTPDRVAPYFSKVECFCFEEQKLLAGEEVDMPLLFFIDKDILDDPSCRGVNDVVLSYTFFKARRNAQGHLEPDAEEDVVQRSLGFEGYEHSPRAETKKVEGSKANS diff --git a/src/busco/busco_run/test_data/script.sh b/src/busco/busco_run/test_data/script.sh new file mode 100644 index 00000000..3c4eb763 --- /dev/null +++ b/src/busco/busco_run/test_data/script.sh @@ -0,0 +1,12 @@ +# busco test data + +# Test data from https://github.com/snakemake/snakemake-wrappers/tree/master/bio/busco/test + +if [ ! -d /tmp/snakemake-wrappers ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers /tmp/snakemake-wrappers +fi + +cp -r /tmp/snakemake-wrappers/bio/busco/test/protein.fasta src/busco/test_data + +# Test data from busco test data at https://gitlab.com/ezlab/busco/-/tree/master/test_data?ref_type=heads +wget -O src/busco/test_data/genome.fna "https://gitlab.com/ezlab/busco/-/raw/master/test_data/eukaryota/genome.fna?ref_type=heads&inline=false" \ No newline at end of file diff --git a/src/cutadapt/config.vsh.yaml b/src/cutadapt/config.vsh.yaml new file mode 100644 index 00000000..a62f0aa9 --- /dev/null +++ b/src/cutadapt/config.vsh.yaml @@ -0,0 +1,463 @@ +name: cutadapt +description: | + Cutadapt removes adapter sequences from high-throughput sequencing reads. +keywords: [RNA-seq, scRNA-seq, high-throughput] +links: + homepage: https://cutadapt.readthedocs.io + documentation: https://cutadapt.readthedocs.io + repository: https://github.com/marcelm/cutadapt +references: + doi: 10.14806/ej.17.1.200 +license: MIT +argument_groups: + #################################################################### + - name: Specify Adapters for R1 + arguments: + - name: --adapter + alternatives: [-a] + type: string + multiple: true + description: | + Sequence of an adapter ligated to the 3' end (paired data: + of the first read). The adapter and subsequent bases are + trimmed. If a '$' character is appended ('anchoring'), the + adapter is only found if it is a suffix of the read. + required: false + - name: --front + alternatives: [-g] + type: string + multiple: true + description: | + Sequence of an adapter ligated to the 5' end (paired data: + of the first read). The adapter and any preceding bases + are trimmed. Partial matches at the 5' end are allowed. If + a '^' character is prepended ('anchoring'), the adapter is + only found if it is a prefix of the read. + required: false + - name: --anywhere + alternatives: [-b] + type: string + multiple: true + description: | + Sequence of an adapter that may be ligated to the 5' or 3' + end (paired data: of the first read). Both types of + matches as described under -a and -g are allowed. If the + first base of the read is part of the match, the behavior + is as with -g, otherwise as with -a. This option is mostly + for rescuing failed library preparations - do not use if + you know which end your adapter was ligated to! + required: false + + #################################################################### + - name: Specify Adapters using Fasta files for R1 + arguments: + - name: --adapter_fasta + type: file + multiple: true + description: | + Fasta file containing sequences of an adapter ligated to the 3' end (paired data: + of the first read). The adapter and subsequent bases are + trimmed. If a '$' character is appended ('anchoring'), the + adapter is only found if it is a suffix of the read. + required: false + - name: --front_fasta + type: file + description: | + Fasta file containing sequences of an adapter ligated to the 5' end (paired data: + of the first read). The adapter and any preceding bases + are trimmed. Partial matches at the 5' end are allowed. If + a '^' character is prepended ('anchoring'), the adapter is + only found if it is a prefix of the read. + required: false + - name: --anywhere_fasta + type: file + description: | + Fasta file containing sequences of an adapter that may be ligated to the 5' or 3' + end (paired data: of the first read). Both types of + matches as described under -a and -g are allowed. If the + first base of the read is part of the match, the behavior + is as with -g, otherwise as with -a. This option is mostly + for rescuing failed library preparations - do not use if + you know which end your adapter was ligated to! + required: false + + #################################################################### + - name: Specify Adapters for R2 + arguments: + - name: --adapter_r2 + alternatives: [-A] + type: string + multiple: true + description: | + Sequence of an adapter ligated to the 3' end (paired data: + of the first read). The adapter and subsequent bases are + trimmed. If a '$' character is appended ('anchoring'), the + adapter is only found if it is a suffix of the read. + required: false + - name: --front_r2 + alternatives: [-G] + type: string + multiple: true + description: | + Sequence of an adapter ligated to the 5' end (paired data: + of the first read). The adapter and any preceding bases + are trimmed. Partial matches at the 5' end are allowed. If + a '^' character is prepended ('anchoring'), the adapter is + only found if it is a prefix of the read. + required: false + - name: --anywhere_r2 + alternatives: [-B] + type: string + multiple: true + description: | + Sequence of an adapter that may be ligated to the 5' or 3' + end (paired data: of the first read). Both types of + matches as described under -a and -g are allowed. If the + first base of the read is part of the match, the behavior + is as with -g, otherwise as with -a. This option is mostly + for rescuing failed library preparations - do not use if + you know which end your adapter was ligated to! + required: false + + #################################################################### + - name: Specify Adapters using Fasta files for R2 + arguments: + - name: --adapter_r2_fasta + type: file + description: | + Fasta file containing sequences of an adapter ligated to the 3' end (paired data: + of the first read). The adapter and subsequent bases are + trimmed. If a '$' character is appended ('anchoring'), the + adapter is only found if it is a suffix of the read. + required: false + - name: --front_r2_fasta + type: file + description: | + Fasta file containing sequences of an adapter ligated to the 5' end (paired data: + of the first read). The adapter and any preceding bases + are trimmed. Partial matches at the 5' end are allowed. If + a '^' character is prepended ('anchoring'), the adapter is + only found if it is a prefix of the read. + required: false + - name: --anywhere_r2_fasta + type: file + description: | + Fasta file containing sequences of an adapter that may be ligated to the 5' or 3' + end (paired data: of the first read). Both types of + matches as described under -a and -g are allowed. If the + first base of the read is part of the match, the behavior + is as with -g, otherwise as with -a. This option is mostly + for rescuing failed library preparations - do not use if + you know which end your adapter was ligated to! + required: false + + #################################################################### + - name: Paired-end options + arguments: + - name: --pair_adapters + type: boolean_true + description: | + Treat adapters given with -a/-A etc. as pairs. Either both + or none are removed from each read pair. + - name: --pair_filter + type: string + choices: [any, both, first] + description: | + Which of the reads in a paired-end read have to match the + filtering criterion in order for the pair to be filtered. + - name: --interleaved + type: boolean_true + description: | + Read and/or write interleaved paired-end reads. + + #################################################################### + - name: Input parameters + arguments: + - name: --input + type: file + required: true + description: | + Input fastq file for single-end reads or R1 for paired-end reads. + - name: --input_r2 + type: file + required: false + description: | + Input fastq file for R2 in the case of paired-end reads. + - name: --error_rate + alternatives: [-E, --errors] + type: double + description: | + Maximum allowed error rate (if 0 <= E < 1), or absolute + number of errors for full-length adapter match (if E is an + integer >= 1). Error rate = no. of errors divided by + length of matching region. Default: 0.1 (10%). + example: 0.1 + - name: --no_indels + type: boolean_false + description: | + Allow only mismatches in alignments. + + - name: --times + type: integer + alternatives: [-n] + description: | + Remove up to COUNT adapters from each read. Default: 1. + example: 1 + - name: --overlap + alternatives: [-O] + type: integer + description: | + Require MINLENGTH overlap between read and adapter for an + adapter to be found. The default is 3. + example: 3 + - name: --match_read_wildcards + type: boolean_true + description: | + Interpret IUPAC wildcards in reads. + - name: --no_match_adapter_wildcards + type: boolean_false + description: | + Do not interpret IUPAC wildcards in adapters. + - name: --action + type: string + choices: + - trim + - retain + - mask + - lowercase + - none + description: | + What to do if a match was found. trim: trim adapter and + up- or downstream sequence; retain: trim, but retain + adapter; mask: replace with 'N' characters; lowercase: + convert to lowercase; none: leave unchanged. + The default is trim. + example: trim + - name: --revcomp + alternatives: [--rc] + type: boolean_true + description: | + Check both the read and its reverse complement for adapter + matches. If match is on reverse-complemented version, + output that one. + + #################################################################### + - name: Read modifications + arguments: + - name: --cut + alternatives: [-u] + type: integer + multiple: true + description: | + Remove LEN bases from each read (or R1 if paired; use --cut_r2 + option for R2). If LEN is positive, remove bases from the + beginning. If LEN is negative, remove bases from the end. + Can be used twice if LENs have different signs. Applied + *before* adapter trimming. + - name: --cut_r2 + type: integer + multiple: true + description: | + Remove LEN bases from each read (for R2). If LEN is positive, remove bases from the + beginning. If LEN is negative, remove bases from the end. + Can be used twice if LENs have different signs. Applied + *before* adapter trimming. + - name: --nextseq_trim + type: string + description: | + NextSeq-specific quality trimming (each read). Trims also + dark cycles appearing as high-quality G bases. + - name: --quality_cutoff + alternatives: [-q] + type: string + description: | + Trim low-quality bases from 5' and/or 3' ends of each read + before adapter removal. Applied to both reads if data is + paired. If one value is given, only the 3' end is trimmed. + If two comma-separated cutoffs are given, the 5' end is + trimmed with the first cutoff, the 3' end with the second. + - name: --quality_cutoff_r2 + alternatives: [-Q] + type: string + description: | + Quality-trimming cutoff for R2. Default: same as for R1 + - name: --quality_base + type: integer + description: | + Assume that quality values in FASTQ are encoded as + ascii(quality + N). This needs to be set to 64 for some + old Illumina FASTQ files. The default is 33. + example: 33 + - name: --poly_a + type: boolean_true + description: Trim poly-A tails + - name: --length + alternatives: [-l] + type: integer + description: | + Shorten reads to LENGTH. Positive values remove bases at + the end while negative ones remove bases at the beginning. + This and the following modifications are applied after + adapter trimming. + - name: --trim_n + type: boolean_true + description: Trim N's on ends of reads. + - name: --length_tag + type: string + description: | + Search for TAG followed by a decimal number in the + description field of the read. Replace the decimal number + with the correct length of the trimmed read. For example, + use --length-tag 'length=' to correct fields like + 'length=123'. + example: "length=" + - name: --strip_suffix + type: string + description: | + Remove this suffix from read names if present. Can be + given multiple times. + - name: --prefix + alternatives: [-x] + type: string + description: | + Add this prefix to read names. Use {name} to insert the + name of the matching adapter. + - name: --suffix + alternatives: [-y] + type: string + description: | + Add this suffix to read names; can also include {name} + - name: --rename + type: string + description: | + Rename reads using TEMPLATE containing variables such as + {id}, {adapter_name} etc. (see documentation) + - name: --zero_cap + alternatives: [-z] + type: boolean_true + description: Change negative quality values to zero. + + #################################################################### + - name: Filtering of processed reads + description: | + Filters are applied after above read modifications. Paired-end reads are + always discarded pairwise (see also --pair_filter). + arguments: + - name: --minimum_length + alternatives: [-m] + type: string + description: | + Discard reads shorter than LEN. Default is 0. + When trimming paired-end reads, the minimum lengths for R1 and R2 can be specified separately by separating them with a colon (:). + If the colon syntax is not used, the same minimum length applies to both reads, as discussed above. + Also, one of the values can be omitted to impose no restrictions. + For example, with -m 17:, the length of R1 must be at least 17, but the length of R2 is ignored. + example: "0" + - name: --maximum_length + alternatives: [-M] + type: string + description: | + Discard reads longer than LEN. Default: no limit. + For paired reads, see the remark for --minimum_length + - name: --max_n + type: string + description: | + Discard reads with more than COUNT 'N' bases. If COUNT is + a number between 0 and 1, it is interpreted as a fraction + of the read length. + - name: --max_expected_errors + alternatives: [--max_ee] + type: long + description: | + Discard reads whose expected number of errors (computed + from quality values) exceeds ERRORS. + - name: --max_average_error_rate + alternatives: [--max_aer] + type: long + description: | + as --max_expected_errors (see above), but divided by + length to account for reads of varying length. + - name: --discard_trimmed + alternatives: [--discard] + type: boolean_true + description: | + Discard reads that contain an adapter. Use also -O to + avoid discarding too many randomly matching reads. + - name: --discard_untrimmed + alternatives: [--trimmed_only] + type: boolean_true + description: | + Discard reads that do not contain an adapter. + - name: --discard_casava + type: boolean_true + description: | + Discard reads that did not pass CASAVA filtering (header + has :Y:). + + #################################################################### + - name: Output parameters + arguments: + - name: --report + type: string + choices: [full, minimal] + description: | + Which type of report to print: 'full' (default) or 'minimal'. + example: full + - name: --json + type: boolean_true + description: | + Write report in JSON format to this file. + - name: --output + type: file + description: | + Glob pattern for matching the expected output files. + Should include `$output_dir`. + example: "fastq/*_001.fast[a,q]" + direction: output + required: true + must_exist: true + multiple: true + - name: --fasta + type: boolean_true + description: | + Output FASTA to standard output even on FASTQ input. + - name: --info_file + type: boolean_true + description: | + Write information about each read and its adapter matches + into info.txt in the output directory. + See the documentation for the file format. + # - name: -Z + # - name: --rest_file + # - name: --wildcard-file + # - name: --too_short_output + # - name: --too_long_output + # - name: --untrimmed_output + # - name: --untrimmed_paired_output + # - name: too_short_paired_output + # - name: too_long_paired_output + - name: Debug + arguments: + - type: boolean_true + name: --debug + description: Print debug information +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + +engines: + - type: docker + image: python:3.12 + setup: + - type: python + pip: + - cutadapt + - type: docker + run: | + cutadapt --version | sed 's/\(.*\)/cutadapt: "\1"/' > /var/software_versions.txt +runners: + - type: executable + - type: nextflow diff --git a/src/cutadapt/help.txt b/src/cutadapt/help.txt new file mode 100644 index 00000000..2280c3e2 --- /dev/null +++ b/src/cutadapt/help.txt @@ -0,0 +1,218 @@ +cutadapt version 4.6 + +Copyright (C) 2010 Marcel Martin and contributors + +Cutadapt removes adapter sequences from high-throughput sequencing reads. + +Usage: + cutadapt -a ADAPTER [options] [-o output.fastq] input.fastq + +For paired-end reads: + cutadapt -a ADAPT1 -A ADAPT2 [options] -o out1.fastq -p out2.fastq in1.fastq in2.fastq + +Replace "ADAPTER" with the actual sequence of your 3' adapter. IUPAC wildcard +characters are supported. All reads from input.fastq will be written to +output.fastq with the adapter sequence removed. Adapter matching is +error-tolerant. Multiple adapter sequences can be given (use further -a +options), but only the best-matching adapter will be removed. + +Input may also be in FASTA format. Compressed input and output is supported and +auto-detected from the file name (.gz, .xz, .bz2). Use the file name '-' for +standard input/output. Without the -o option, output is sent to standard output. + +Citation: + +Marcel Martin. Cutadapt removes adapter sequences from high-throughput +sequencing reads. EMBnet.Journal, 17(1):10-12, May 2011. +http://dx.doi.org/10.14806/ej.17.1.200 + +Run "cutadapt --help" to see all command-line options. +See https://cutadapt.readthedocs.io/ for full documentation. + +Options: + -h, --help Show this help message and exit + --version Show version number and exit + --debug Print debug log. Use twice to also print DP matrices + -j CORES, --cores CORES + Number of CPU cores to use. Use 0 to auto-detect. Default: + 1 + +Finding adapters: + Parameters -a, -g, -b specify adapters to be removed from each read (or from + R1 if data is paired-end. If specified multiple times, only the best matching + adapter is trimmed (but see the --times option). Use notation 'file:FILE' to + read adapter sequences from a FASTA file. + + -a ADAPTER, --adapter ADAPTER + Sequence of an adapter ligated to the 3' end (paired data: + of the first read). The adapter and subsequent bases are + trimmed. If a '$' character is appended ('anchoring'), the + adapter is only found if it is a suffix of the read. + -g ADAPTER, --front ADAPTER + Sequence of an adapter ligated to the 5' end (paired data: + of the first read). The adapter and any preceding bases + are trimmed. Partial matches at the 5' end are allowed. If + a '^' character is prepended ('anchoring'), the adapter is + only found if it is a prefix of the read. + -b ADAPTER, --anywhere ADAPTER + Sequence of an adapter that may be ligated to the 5' or 3' + end (paired data: of the first read). Both types of + matches as described under -a and -g are allowed. If the + first base of the read is part of the match, the behavior + is as with -g, otherwise as with -a. This option is mostly + for rescuing failed library preparations - do not use if + you know which end your adapter was ligated to! + -e E, --error-rate E, --errors E + Maximum allowed error rate (if 0 <= E < 1), or absolute + number of errors for full-length adapter match (if E is an + integer >= 1). Error rate = no. of errors divided by + length of matching region. Default: 0.1 (10%) + --no-indels Allow only mismatches in alignments. Default: allow both + mismatches and indels + -n COUNT, --times COUNT + Remove up to COUNT adapters from each read. Default: 1 + -O MINLENGTH, --overlap MINLENGTH + Require MINLENGTH overlap between read and adapter for an + adapter to be found. Default: 3 + --match-read-wildcards + Interpret IUPAC wildcards in reads. Default: False + -N, --no-match-adapter-wildcards + Do not interpret IUPAC wildcards in adapters. + --action {trim,retain,mask,lowercase,none} + What to do if a match was found. trim: trim adapter and + up- or downstream sequence; retain: trim, but retain + adapter; mask: replace with 'N' characters; lowercase: + convert to lowercase; none: leave unchanged. Default: trim + --rc, --revcomp Check both the read and its reverse complement for adapter + matches. If match is on reverse-complemented version, + output that one. Default: check only read + +Additional read modifications: + -u LEN, --cut LEN Remove LEN bases from each read (or R1 if paired; use -U + option for R2). If LEN is positive, remove bases from the + beginning. If LEN is negative, remove bases from the end. + Can be used twice if LENs have different signs. Applied + *before* adapter trimming. + --nextseq-trim 3'CUTOFF + NextSeq-specific quality trimming (each read). Trims also + dark cycles appearing as high-quality G bases. + -q [5'CUTOFF,]3'CUTOFF, --quality-cutoff [5'CUTOFF,]3'CUTOFF + Trim low-quality bases from 5' and/or 3' ends of each read + before adapter removal. Applied to both reads if data is + paired. If one value is given, only the 3' end is trimmed. + If two comma-separated cutoffs are given, the 5' end is + trimmed with the first cutoff, the 3' end with the second. + --quality-base N Assume that quality values in FASTQ are encoded as + ascii(quality + N). This needs to be set to 64 for some + old Illumina FASTQ files. Default: 33 + --poly-a Trim poly-A tails + --length LENGTH, -l LENGTH + Shorten reads to LENGTH. Positive values remove bases at + the end while negative ones remove bases at the beginning. + This and the following modifications are applied after + adapter trimming. + --trim-n Trim N's on ends of reads. + --length-tag TAG Search for TAG followed by a decimal number in the + description field of the read. Replace the decimal number + with the correct length of the trimmed read. For example, + use --length-tag 'length=' to correct fields like + 'length=123'. + --strip-suffix STRIP_SUFFIX + Remove this suffix from read names if present. Can be + given multiple times. + -x PREFIX, --prefix PREFIX + Add this prefix to read names. Use {name} to insert the + name of the matching adapter. + -y SUFFIX, --suffix SUFFIX + Add this suffix to read names; can also include {name} + --rename TEMPLATE Rename reads using TEMPLATE containing variables such as + {id}, {adapter_name} etc. (see documentation) + --zero-cap, -z Change negative quality values to zero. + +Filtering of processed reads: + Filters are applied after above read modifications. Paired-end reads are + always discarded pairwise (see also --pair-filter). + + -m LEN[:LEN2], --minimum-length LEN[:LEN2] + Discard reads shorter than LEN. Default: 0 + -M LEN[:LEN2], --maximum-length LEN[:LEN2] + Discard reads longer than LEN. Default: no limit + --max-n COUNT Discard reads with more than COUNT 'N' bases. If COUNT is + a number between 0 and 1, it is interpreted as a fraction + of the read length. + --max-expected-errors ERRORS, --max-ee ERRORS + Discard reads whose expected number of errors (computed + from quality values) exceeds ERRORS. + --max-average-error-rate ERROR_RATE, --max-aer ERROR_RATE + as --max-expected-errors (see above), but divided by + length to account for reads of varying length. + --discard-trimmed, --discard + Discard reads that contain an adapter. Use also -O to + avoid discarding too many randomly matching reads. + --discard-untrimmed, --trimmed-only + Discard reads that do not contain an adapter. + --discard-casava Discard reads that did not pass CASAVA filtering (header + has :Y:). + +Output: + --quiet Print only error messages. + --report {full,minimal} + Which type of report to print: 'full' or 'minimal'. + Default: full + --json FILE Dump report in JSON format to FILE + -o FILE, --output FILE + Write trimmed reads to FILE. FASTQ or FASTA format is + chosen depending on input. Summary report is sent to + standard output. Use '{name}' for demultiplexing (see + docs). Default: write to standard output + --fasta Output FASTA to standard output even on FASTQ input. + -Z Use compression level 1 for gzipped output files (faster, + but uses more space) + --info-file FILE Write information about each read and its adapter matches + into FILE. See the documentation for the file format. + -r FILE, --rest-file FILE + When the adapter matches in the middle of a read, write + the rest (after the adapter) to FILE. + --wildcard-file FILE When the adapter has N wildcard bases, write adapter bases + matching wildcard positions to FILE. (Inaccurate with + indels.) + --too-short-output FILE + Write reads that are too short (according to length + specified by -m) to FILE. Default: discard reads + --too-long-output FILE + Write reads that are too long (according to length + specified by -M) to FILE. Default: discard reads + --untrimmed-output FILE + Write reads that do not contain any adapter to FILE. + Default: output to same file as trimmed reads + +Paired-end options: + The -A/-G/-B/-U/-Q options work like their lowercase counterparts, but are + applied to R2 (second read in pair) + + -A ADAPTER 3' adapter to be removed from R2 + -G ADAPTER 5' adapter to be removed from R2 + -B ADAPTER 5'/3 adapter to be removed from R2 + -U LENGTH Remove LENGTH bases from R2 + -Q [5'CUTOFF,]3'CUTOFF + Quality-trimming cutoff for R2. Default: same as for R1 + -p FILE, --paired-output FILE + Write R2 to FILE. + --pair-adapters Treat adapters given with -a/-A etc. as pairs. Either both + or none are removed from each read pair. + --pair-filter {any,both,first} + Which of the reads in a paired-end read have to match the + filtering criterion in order for the pair to be filtered. + Default: any + --interleaved Read and/or write interleaved paired-end reads. + --untrimmed-paired-output FILE + Write second read in a pair to this FILE when no adapter + was found. Use with --untrimmed-output. Default: output to + same file as trimmed reads + --too-short-paired-output FILE + Write second read in a pair to this file if pair is too + short. + --too-long-paired-output FILE + Write second read in a pair to this file if pair is too + long. + diff --git a/src/cutadapt/script.sh b/src/cutadapt/script.sh new file mode 100644 index 00000000..5e1f9e30 --- /dev/null +++ b/src/cutadapt/script.sh @@ -0,0 +1,237 @@ +#!/bin/bash + +## VIASH START +par_adapter='AGATCGGAAGAGCACACGTCTGAACTCCAGTCAC;GGATCGGAAGAGCACACGTCTGAACTCCAGTCAC' +par_input='src/cutadapt/test_data/se/a.fastq' +par_report='full' +par_json='false' +par_fasta='false' +par_info_file='false' +par_debug='true' +## VIASH END + +function debug { + [[ "$par_debug" == "true" ]] && echo "DEBUG: $@" +} + +output_dir=$(dirname $par_output) +[[ ! -d $output_dir ]] && mkdir -p $output_dir + +# Init +########################################################### + +echo ">> Paired-end data or not?" + +mode="" +if [[ -z $par_input_r2 ]]; then + mode="se" + echo " Single end" + input="$par_input" +else + echo " Paired end" + mode="pe" + input="$par_input $par_input_r2" +fi + +# Adapter arguments +# - paired and single-end +# - string and fasta +########################################################### + +function add_flags { + local arg=$1 + local flag=$2 + local prefix=$3 + [[ -z $prefix ]] && prefix="" + + # This function should not be called if the input is empty + # but check for it just in case + if [[ -z $arg ]]; then + return + fi + + local output="" + IFS=';' read -r -a array <<< "$arg" + for a in "${array[@]}"; do + output="$output $flag $prefix$a" + done + echo $output +} + +debug ">> Parsing arguments dealing with adapters" +adapter_args=$(echo \ + ${par_adapter:+$(add_flags "$par_adapter" "--adapter")} \ + ${par_adapter_fasta:+$(add_flags "$par_adapter_fasta" "--adapter" "file:")} \ + ${par_front:+$(add_flags "$par_front" "--front")} \ + ${par_front_fasta:+$(add_flags "$par_front_fasta" "--front" "file:")} \ + ${par_anywhere:+$(add_flags "$par_anywhere" "--anywhere")} \ + ${par_anywhere_fasta:+$(add_flags "$par_anywhere_fasta" "--anywhere" "file:")} \ + ${par_adapter_r2:+$(add_flags "$par_adapter_r2" "-A")} \ + ${par_adapter_fasta_r2:+$(add_flags "$par_adapter_fasta_r2" "-A" "file:")} \ + ${par_front_r2:+$(add_flags "$par_front_r2" "-G")} \ + ${par_front_fasta_r2:+$(add_flags "$par_front_fasta_r2" "-G" "file:")} \ + ${par_anywhere_r2:+$(add_flags "$par_anywhere_r2" "-B")} \ + ${par_anywhere_fasta_r2:+$(add_flags "$par_anywhere_fasta_r2" "-B" "file:")} \ +) + +debug "Arguments to cutadapt:" +debug "$adapter_args" +debug + +# Paired-end options +########################################################### +echo ">> Parsing arguments for paired-end reads" +[[ "$par_pair_adapters" == "false" ]] && unset par_pair_adapters +[[ "$par_interleaved" == "false" ]] && unset par_interleaved + +paired_args=$(echo \ + ${par_pair_adapters:+--pair-adapters} \ + ${par_pair_filter:+--pair-filter "${par_pair_filter}"} \ + ${par_interleaved:+--interleaved} +) +debug "Arguments to cutadapt:" +debug $paired_args +debug + +# Input arguments +########################################################### +echo ">> Parsing input arguments" +[[ "$par_no_indels" == "true" ]] && unset par_no_indels +[[ "$par_match_read_wildcards" == "false" ]] && unset par_match_read_wildcards +[[ "$par_no_match_adapter_wildcards" == "true" ]] && unset par_no_match_adapter_wildcards +[[ "$par_revcomp" == "false" ]] && unset par_revcomp + +input_args=$(echo \ + ${par_error_rate:+--error-rate "${par_error_rate}"} \ + ${par_no_indels:+--no-indels} \ + ${par_times:+--times "${par_times}"} \ + ${par_overlap:+--overlap "${par_overlap}"} \ + ${par_match_read_wildcards:+--match-read-wildcards} \ + ${par_no_match_adapter_wildcards:+--no-match-adapter-wildcards} \ + ${par_action:+--action "${par_action}"} \ + ${par_revcomp:+--revcomp} \ +) +debug "Arguments to cutadapt:" +debug $input_args +debug + +# Read modifications +########################################################### +echo ">> Parsing read modification arguments" +[[ "$par_poly_a" == "false" ]] && unset par_poly_a +[[ "$par_trim_n" == "false" ]] && unset par_trim_n +[[ "$par_zero_cap" == "false" ]] && unset par_zero_cap + +mod_args=$(echo \ + ${par_cut:+--cut "${par_cut}"} \ + ${par_cut_r2:+--cut_r2 "${par_cut_r2}"} \ + ${par_nextseq_trim:+--nextseq-trim "${par_nextseq_trim}"} \ + ${par_quality_cutoff:+--quality-cutoff "${par_quality_cutoff}"} \ + ${par_quality_cutoff_r2:+--quality-cutoff_r2 "${par_quality_cutoff_r2}"} \ + ${par_quality_base:+--quality-base "${par_quality_base}"} \ + ${par_poly_a:+--poly-a} \ + ${par_length:+--length "${par_length}"} \ + ${par_trim_n:+--trim-n} \ + ${par_length_tag:+--length-tag "${par_length_tag}"} \ + ${par_strip_suffix:+--strip-suffix "${par_strip_suffix}"} \ + ${par_prefix:+--prefix "${par_prefix}"} \ + ${par_suffix:+--suffix "${par_suffix}"} \ + ${par_rename:+--rename "${par_rename}"} \ + ${par_zero_cap:+--zero-cap} \ +) +debug "Arguments to cutadapt:" +debug $mod_args +debug + +# Filtering of processed reads arguments +########################################################### +echo ">> Filtering of processed reads arguments" +[[ "$par_discard_trimmed" == "false" ]] && unset par_discard_trimmed +[[ "$par_discard_untrimmed" == "false" ]] && unset par_discard_untrimmed +[[ "$par_discard_casava" == "false" ]] && unset par_discard_casava + +# Parse and transform the minimum and maximum length arguments +[[ -z $par_minimum_length ]] + +filter_args=$(echo \ + ${par_minimum_length:+--minimum-length "${par_minimum_length}"} \ + ${par_maximum_length:+--maximum-length "${par_maximum_length}"} \ + ${par_max_n:+--max-n "${par_max_n}"} \ + ${par_max_expected_errors:+--max-expected-errors "${par_max_expected_errors}"} \ + ${par_max_average_error_rate:+--max-average-error-rate "${par_max_average_error_rate}"} \ + ${par_discard_trimmed:+--discard-trimmed} \ + ${par_discard_untrimmed:+--discard-untrimmed} \ + ${par_discard_casava:+--discard-casava} \ +) +debug "Arguments to cutadapt:" +debug $filter_args +debug + +# Optional output arguments +########################################################### +echo ">> Optional arguments" +[[ "$par_json" == "false" ]] && unset par_json +[[ "$par_fasta" == "false" ]] && unset par_fasta +[[ "$par_info_file" == "false" ]] && unset par_info_file + +optional_output_args=$(echo \ + ${par_report:+--report "${par_report}"} \ + ${par_json:+--json "report.json"} \ + ${par_fasta:+--fasta} \ + ${par_info_file:+--info-file "info.txt"} \ +) + +debug "Arguments to cutadapt:" +debug $optional_output_args +debug + +# Output arguments +# We write the output to a directory rather than +# individual files. +########################################################### + +if [[ -z $par_fasta ]]; then + ext="fastq" +else + ext="fasta" +fi + +if [ $mode = "se" ]; then + output_args=$(echo \ + --output "$output_dir/{name}_001.$ext" \ + ) +else + output_args=$(echo \ + --output "$output_dir/{name}_R1_001.$ext" \ + --paired-output "$output_dir/{name}_R2_001.$ext" \ + ) +fi + +debug "Arguments to cutadapt:" +debug $output_args +debug + +# Full CLI +# Set the --cores argument to 0 unless meta_cpus is set +########################################################### +echo ">> Running cutadapt" +par_cpus=0 +[[ ! -z $meta_cpus ]] && par_cpus=$meta_cpus + +cli=$(echo \ + $input \ + $adapter_args \ + $paired_args \ + $input_args \ + $mod_args \ + $filter_args \ + $optional_output_args \ + $output_args \ + --cores $par_cpus +) + +debug ">> Full CLI to be run:" +debug cutadapt $cli | sed -e 's/--/\r\n --/g' +debug + +cutadapt $cli diff --git a/src/cutadapt/test.sh b/src/cutadapt/test.sh new file mode 100644 index 00000000..eff997d7 --- /dev/null +++ b/src/cutadapt/test.sh @@ -0,0 +1,256 @@ +#!/bin/bash + +set -e + +############################################# +# helper functions +assert_file_exists() { + [ -f "$1" ] || (echo "File '$1' does not exist" && exit 1) +} +assert_file_doesnt_exist() { + [ ! -f "$1" ] || (echo "File '$1' exists but shouldn't" && exit 1) +} +assert_file_empty() { + [ ! -s "$1" ] || (echo "File '$1' is not empty but should be" && exit 1) +} +assert_file_not_empty() { + [ -s "$1" ] || (echo "File '$1' is empty but shouldn't be" && exit 1) +} +assert_file_contains() { + grep -q "$2" "$1" || (echo "File '$1' does not contain '$2'" && exit 1) +} +assert_file_not_contains() { + grep -q "$2" "$1" && (echo "File '$1' contains '$2' but shouldn't" && exit 1) +} + +############################################# +mkdir test_multiple_output +cd test_multiple_output + +echo "#############################################" +echo "> Run cutadapt with multiple outputs" + +cat > example.fa <<'EOF' +>read1 +MYSEQUENCEADAPTER +>read2 +MYSEQUENCEADAP +>read3 +MYSEQUENCEADAPTERSOMETHINGELSE +>read4 +MYSEQUENCEADABTER +>read5 +MYSEQUENCEADAPTR +>read6 +MYSEQUENCEADAPPTER +>read7 +ADAPTERMYSEQUENCE +>read8 +PTERMYSEQUENCE +>read9 +SOMETHINGADAPTERMYSEQUENCE +EOF + +"$meta_executable" \ + --report minimal \ + --output "out_test/*.fasta" \ + --adapter ADAPTER \ + --input example.fa \ + --fasta \ + --no_match_adapter_wildcards \ + --json + +echo ">> Checking output" +assert_file_exists "report.json" +assert_file_exists "out_test/1_001.fasta" +assert_file_exists "out_test/unknown_001.fasta" + +cd .. +echo + +############################################# +mkdir test_simple_single_end +cd test_simple_single_end + +echo "#############################################" +echo "> Run cutadapt on single-end data" + +cat > example.fa <<'EOF' +>read1 +MYSEQUENCEADAPTER +>read2 +MYSEQUENCEADAP +>read3 +MYSEQUENCEADAPTERSOMETHINGELSE +>read4 +MYSEQUENCEADABTER +>read5 +MYSEQUENCEADAPTR +>read6 +MYSEQUENCEADAPPTER +>read7 +ADAPTERMYSEQUENCE +>read8 +PTERMYSEQUENCE +>read9 +SOMETHINGADAPTERMYSEQUENCE +EOF + +"$meta_executable" \ + --report minimal \ + --output "out_test1/*.fasta" \ + --adapter ADAPTER \ + --input example.fa \ + --fasta \ + --no_match_adapter_wildcards \ + --json + +echo ">> Checking output" +assert_file_exists "report.json" +assert_file_exists "out_test1/1_001.fasta" +assert_file_exists "out_test1/unknown_001.fasta" + +echo ">> Check if output is empty" +assert_file_not_empty "report.json" +assert_file_not_empty "out_test1/1_001.fasta" +assert_file_not_empty "out_test1/unknown_001.fasta" + +echo ">> Check contents" +for i in 1 2 3 7 9; do + assert_file_contains "out_test1/1_001.fasta" ">read$i" +done +for i in 4 5 6 8; do + assert_file_contains "out_test1/unknown_001.fasta" ">read$i" +done + +cd .. +echo + +############################################# +mkdir test_multiple_single_end +cd test_multiple_single_end + +echo "#############################################" +echo "> Run with a combination of inputs" + +cat > example.fa <<'EOF' +>read1 +ACGTACGTACGTAAAAA +>read2 +ACGTACGTACGTCCCCC +>read3 +ACGTACGTACGTGGGGG +>read4 +ACGTACGTACGTTTTTT +EOF + +cat > adapters1.fasta <<'EOF' +>adapter1 +CCCCC +EOF + +cat > adapters2.fasta <<'EOF' +>adapter2 +GGGGG +EOF + +"$meta_executable" \ + --report minimal \ + --output "out_test2/*.fasta" \ + --adapter AAAAA \ + --adapter_fasta adapters1.fasta \ + --adapter_fasta adapters2.fasta \ + --input example.fa \ + --fasta \ + --json + +echo ">> Checking output" +assert_file_exists "report.json" +assert_file_exists "out_test2/1_001.fasta" +assert_file_exists "out_test2/adapter1_001.fasta" +assert_file_exists "out_test2/adapter2_001.fasta" +assert_file_exists "out_test2/unknown_001.fasta" + +echo ">> Check if output is empty" +assert_file_not_empty "report.json" +assert_file_not_empty "out_test2/1_001.fasta" +assert_file_not_empty "out_test2/adapter1_001.fasta" +assert_file_not_empty "out_test2/adapter2_001.fasta" +assert_file_not_empty "out_test2/unknown_001.fasta" + +echo ">> Check contents" +assert_file_contains "out_test2/1_001.fasta" ">read1" +assert_file_contains "out_test2/adapter1_001.fasta" ">read2" +assert_file_contains "out_test2/adapter2_001.fasta" ">read3" +assert_file_contains "out_test2/unknown_001.fasta" ">read4" + +cd .. +echo + +############################################# +mkdir test_simple_paired_end +cd test_simple_paired_end + +echo "#############################################" +echo "> Run cutadapt on paired-end data" + +cat > example_R1.fastq <<'EOF' +@read1 +ACGTACGTACGTAAAAA ++ +IIIIIIIIIIIIIIIII +@read2 +ACGTACGTACGTCCCCC ++ +IIIIIIIIIIIIIIIII +EOF + +cat > example_R2.fastq <<'EOF' +@read1 +ACGTACGTACGTGGGGG ++ +IIIIIIIIIIIIIIIII +@read2 +ACGTACGTACGTTTTTT ++ +IIIIIIIIIIIIIIIII +EOF + +"$meta_executable" \ + --report minimal \ + --output "out_test3/*.fastq" \ + --adapter AAAAA \ + --adapter_r2 GGGGG \ + --input example_R1.fastq \ + --input_r2 example_R2.fastq \ + --quality_cutoff 20 \ + --json \ + ---cpus 1 + +echo ">> Checking output" +assert_file_exists "report.json" +assert_file_exists "out_test3/1_R1_001.fastq" +assert_file_exists "out_test3/1_R2_001.fastq" +assert_file_exists "out_test3/unknown_R1_001.fastq" +assert_file_exists "out_test3/unknown_R2_001.fastq" + +echo ">> Check if output is empty" +assert_file_not_empty "report.json" +assert_file_not_empty "out_test3/1_R1_001.fastq" +assert_file_not_empty "out_test3/1_R2_001.fastq" +assert_file_not_empty "out_test3/unknown_R1_001.fastq" + +echo ">> Check contents" +assert_file_contains "out_test3/1_R1_001.fastq" "@read1" +assert_file_contains "out_test3/1_R2_001.fastq" "@read1" +assert_file_contains "out_test3/unknown_R1_001.fastq" "@read2" +assert_file_contains "out_test3/unknown_R2_001.fastq" "@read2" + +cd .. +echo + +############################################# + +echo "#############################################" +echo "> Test successful" + diff --git a/src/falco/config.vsh.yaml b/src/falco/config.vsh.yaml new file mode 100644 index 00000000..4d9cf656 --- /dev/null +++ b/src/falco/config.vsh.yaml @@ -0,0 +1,196 @@ +name: falco +description: A C++ drop-in replacement of FastQC to assess the quality of sequence read data +keywords: [qc, fastqc, sequencing] +links: + documentation: https://falco.readthedocs.io/en/latest/ + repository: https://github.com/smithlabcode/falco +references: + doi: 10.12688/f1000research.21142.2 +license: GPL-3.0 +requirements: + commands: [falco] + +# Notes: +# - falco as arguments similar to -subsample and we update those to --subsample +# - The outdir argument is not required +# - The input argument in falco is positional but we changed this to --input +argument_groups: + - name: Input arguments + arguments: + - name: --input + required: true + type: file + multiple: true + description: input fastq files + example: input1.fastq;input2.fastq + + - name: Run arguments + arguments: + - name: --nogroup + type: boolean_true + description: | + Disable grouping of bases for reads >50bp. + All reports will show data for every base in + the read. WARNING: When using this option, + your plots may end up a ridiculous size. You + have been warned! + - name: --contaminents + type: file + description: | + Specifies a non-default file which contains + the list of contaminants to screen + overrepresented sequences against. The file + must contain sets of named contaminants in + the form name[tab]sequence. Lines prefixed + with a hash will be ignored. Default: + https://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/contaminant_list.txt + - name: --adapters + type: file + description: | + Specifies a non-default file which contains + the list of adapter sequences which will be + explicity searched against the library. The + file must contain sets of named adapters in + the form name[tab]sequence. Lines prefixed + with a hash will be ignored. Default: + https://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/adapter_list.txt + - name: --limits + type: file + description: | + Specifies a non-default file which contains + a set of criteria which will be used to + determine the warn/error limits for the + various modules. This file can also be used + to selectively remove some modules from the + output all together. The format needs to + mirror the default limits.txt file found in + the Configuration folder. Default: + https://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/limits.txt + - name: --subsample + alternatives: [-s] + type: integer + example: 10 + description: | + [Falco only] makes falco faster (but + possibly less accurate) by only processing + reads that are a multiple of this value (using + 0-based indexing to number reads). + - name: --bisulfite + alternatives: [-b] + type: boolean_true + description: | + [Falco only] reads are whole genome + bisulfite sequencing, and more Ts and fewer + Cs are therefore expected and will be + accounted for in base content. + - name: --reverse_complliment + alternatives: [-r] + type: boolean_true + description: | + [Falco only] The input is a + reverse-complement. All modules will be + tested by swapping A/T and C/G + + - name: Output arguments + arguments: + - name: --outdir + alternatives: [-o] + required: true + type: file + direction: output + description: | + Create all output files in the specified + output directory. FALCO-SPECIFIC: If the + directory does not exists, the program will + create it. + example: output + - name: --format + type: string + choices: [bam, sam, bam_mapped, sam_mapped, fastq, fq, fastq.gz, fq.gz] + alternatives: ["-f"] + description: | + Bypasses the normal sequence file format + detection and forces the program to use the + specified format. Validformats are bam, sam, + bam_mapped, sam_mapped, fastq, fq, fastq.gz + or fq.gz. + - name: --data_filename + alternatives: [-D] + type: file + direction: output + description: | + [Falco only] Specify filename for FastQC + data output (TXT). If not specified, it will + be called fastq_data.txt in either the input + file's directory or the one specified in the + --output flag. Only available when running + falco with a single input. + - name: --report_filename + alternatives: [-R] + type: file + direction: output + description: | + [Falco only] Specify filename for FastQC + report output (HTML). If not specified, it + will be called fastq_report.html in either + the input file's directory or the one + specified in the --output flag. Only + available when running falco with a single + input. + - name: --summary_filename + alternatives: [-S] + type: file + direction: output + description: | + [Falco only] Specify filename for the short + summary output (TXT). If not specified, it + will be called fastq_report.html in either + the input file's directory or the one + specified in the --output flag. Only + available when running falco with a single + input. + +# Arguments not taken into account: +# +# -skip-data [Falco only] Do not create FastQC data text +# file. +# -skip-report [Falco only] Do not create FastQC report +# HTML file. +# -skip-summary [Falco only] Do not create FastQC summary +# file +# -K, -add-call [Falco only] add the command call call to +# FastQC data output and FastQC report HTML +# (this may break the parse of fastqc_data.txt +# in programs that are very strict about the +# FastQC output format). + +resources: + - type: bash_script + path: script.sh + +test_resources: + - type: bash_script + path: test.sh + +engines: + - type: docker + image: debian:trixie-slim + setup: + - type: apt + packages: [wget, build-essential, g++, zlib1g-dev, procps] + - type: docker + run: | + wget https://github.com/smithlabcode/falco/releases/download/v1.2.2/falco-1.2.2.tar.gz -O /tmp/falco.tar.gz && \ + cd /tmp && \ + tar xvf falco.tar.gz && \ + cd falco-1.2.2 && \ + ./configure && \ + make all && \ + make install + - type: docker + run: | + echo "falco: \"$(falco -v | sed -n 's/^falco //p')\"" > /var/software_versions.txt + +runners: + - type: executable + - type: nextflow diff --git a/src/falco/help.txt b/src/falco/help.txt new file mode 100644 index 00000000..eea77972 --- /dev/null +++ b/src/falco/help.txt @@ -0,0 +1,156 @@ +Usage: falco [OPTIONS] ... + +Options: + -h, --help Print this help file and exit + -v, --version Print the version of the program and exit + -o, --outdir Create all output files in the specified + output directory. FALCO-SPECIFIC: If the + directory does not exists, the program will + create it. If this option is not set then + the output file for each sequence file is + created in the same directory as the + sequence file which was processed. + --casava [IGNORED BY FALCO] Files come from raw + casava output. Files in the same sample + group (differing only by the group number) + will be analysed as a set rather than + individually. Sequences with the filter flag + set in the header will be excluded from the + analysis. Files must have the same names + given to them by casava (including being + gzipped and ending with .gz) otherwise they + won't be grouped together correctly. + --nano [IGNORED BY FALCO] Files come from nanopore + sequences and are in fast5 format. In this + mode you can pass in directories to process + and the program will take in all fast5 files + within those directories and produce a + single output file from the sequences found + in all files. + --nofilter [IGNORED BY FALCO] If running with --casava + then don't remove read flagged by casava as + poor quality when performing the QC + analysis. + --extract [ALWAYS ON IN FALCO] If set then the zipped + output file will be uncompressed in the same + directory after it has been created. By + default this option will be set if fastqc is + run in non-interactive mode. + -j, --java [IGNORED BY FALCO] Provides the full path to + the java binary you want to use to launch + fastqc. If not supplied then java is assumed + to be in your path. + --noextract [IGNORED BY FALCO] Do not uncompress the + output file after creating it. You should + set this option if you do not wish to + uncompress the output when running in + non-interactive mode. + --nogroup Disable grouping of bases for reads >50bp. + All reports will show data for every base in + the read. WARNING: When using this option, + your plots may end up a ridiculous size. You + have been warned! + --min_length [NOT YET IMPLEMENTED IN FALCO] Sets an + artificial lower limit on the length of the + sequence to be shown in the report. As long + as you set this to a value greater or equal + to your longest read length then this will + be the sequence length used to create your + read groups. This can be useful for making + directly comaparable statistics from + datasets with somewhat variable read + lengths. + -f, --format Bypasses the normal sequence file format + detection and forces the program to use the + specified format. Validformats are bam, sam, + bam_mapped, sam_mapped, fastq, fq, fastq.gz + or fq.gz. + -t, --threads [NOT YET IMPLEMENTED IN FALCO] Specifies the + number of files which can be processed + simultaneously. Each thread will be + allocated 250MB of memory so you shouldn't + run more threads than your available memory + will cope with, and not more than 6 threads + on a 32 bit machine [1] + -c, --contaminants Specifies a non-default file which contains + the list of contaminants to screen + overrepresented sequences against. The file + must contain sets of named contaminants in + the form name[tab]sequence. Lines prefixed + with a hash will be ignored. Default: + /tmp/falco-1.2.2/Configuration/contaminant_list.txt + -a, --adapters Specifies a non-default file which contains + the list of adapter sequences which will be + explicity searched against the library. The + file must contain sets of named adapters in + the form name[tab]sequence. Lines prefixed + with a hash will be ignored. Default: + /tmp/falco-1.2.2/Configuration/adapter_list.txt + -l, --limits Specifies a non-default file which contains + a set of criteria which will be used to + determine the warn/error limits for the + various modules. This file can also be used + to selectively remove some modules from the + output all together. The format needs to + mirror the default limits.txt file found in + the Configuration folder. Default: + /tmp/falco-1.2.2/Configuration/limits.txt + -k, --kmers [IGNORED BY FALCO AND ALWAYS SET TO 7] + Specifies the length of Kmer to look for in + the Kmer content module. Specified Kmer + length must be between 2 and 10. Default + length is 7 if not specified. + -q, --quiet Supress all progress messages on stdout and + only report errors. + -d, --dir [IGNORED: FALCO DOES NOT CREATE TMP FILES] + Selects a directory to be used for temporary + files written when generating report images. + Defaults to system temp directory if not + specified. + -s, -subsample [Falco only] makes falco faster (but + possibly less accurate) by only processing + reads that are multiple of this value (using + 0-based indexing to number reads). [1] + -b, -bisulfite [Falco only] reads are whole genome + bisulfite sequencing, and more Ts and fewer + Cs are therefore expected and will be + accounted for in base content. + -r, -reverse-complement [Falco only] The input is a + reverse-complement. All modules will be + tested by swapping A/T and C/G + -skip-data [Falco only] Do not create FastQC data text + file. + -skip-report [Falco only] Do not create FastQC report + HTML file. + -skip-summary [Falco only] Do not create FastQC summary + file + -D, -data-filename [Falco only] Specify filename for FastQC + data output (TXT). If not specified, it will + be called fastq_data.txt in either the input + file's directory or the one specified in the + --output flag. Only available when running + falco with a single input. + -R, -report-filename [Falco only] Specify filename for FastQC + report output (HTML). If not specified, it + will be called fastq_report.html in either + the input file's directory or the one + specified in the --output flag. Only + available when running falco with a single + input. + -S, -summary-filename [Falco only] Specify filename for the short + summary output (TXT). If not specified, it + will be called fastq_report.html in either + the input file's directory or the one + specified in the --output flag. Only + available when running falco with a single + input. + -K, -add-call [Falco only] add the command call call to + FastQC data output and FastQC report HTML + (this may break the parse of fastqc_data.txt + in programs that are very strict about the + FastQC output format). + +Help options: + -?, -help print this help message + -about print about message + diff --git a/src/falco/script.sh b/src/falco/script.sh new file mode 100644 index 00000000..43f5efe5 --- /dev/null +++ b/src/falco/script.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -eo pipefail + +[[ "$par_nogroup" == "false" ]] && unset par_nogroup +[[ "$par_bisulfite" == "false" ]] && unset par_bisulfite +[[ "$par_reverse_compliment" == "false" ]] && unset par_reverse_compliment + +IFS=";" read -ra input <<< $par_input + +$(which falco) \ + ${par_nogroup:+--nogroup} \ + ${par_contaminants:+--contaminants "$par_contaminants"} \ + ${par_adapters:+--adapters "$par_adapters"} \ + ${par_limits:+--limits "$par_limits"} \ + ${par_subsample:+-subsample $par_subsample} \ + ${par_bisulfite:+-bisulfite} \ + ${par_reverse_compliment:+-reverse-compliment} \ + ${par_outdir:+--outdir "$par_outdir"} \ + ${par_format:+--format "$par_format"} \ + ${par_data_filename:+-data-filename "$par_data_filename"} \ + ${par_report_filename:+-report-filename "$par_report_filename"} \ + ${par_summary_filename:+-summary-filename "$par_summary_filename"} \ + ${input[*]} diff --git a/src/falco/test.sh b/src/falco/test.sh new file mode 100644 index 00000000..3f1dc47a --- /dev/null +++ b/src/falco/test.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +set -e + +echo "> Prepare test data" + +# We use data from this repo: https://github.com/hartwigmedical/testData +echo ">> Fetching and preparing test data" +fastq1="https://github.com/hartwigmedical/testdata/raw/master/100k_reads_hiseq/TESTX/TESTX_H7YRLADXX_S1_L001_R1_001.fastq.gz" +fastq2="https://github.com/hartwigmedical/testdata/raw/master/100k_reads_hiseq/TESTX/TESTX_H7YRLADXX_S1_L001_R2_001.fastq.gz" +TMPDIR=$(mktemp -d "$meta_temp_dir/$meta_functionality_name-XXXXXX") +function clean_up { + [[ -d "$TMPDIR" ]] && rm -r "$TMPDIR" +} +trap clean_up EXIT + +test_data_dir="$TMPDIR/test_data" + +mkdir $test_data_dir +wget -q $fastq1 -O $test_data_dir/R1.fastq.gz +wget -q $fastq2 -O $test_data_dir/R2.fastq.gz + +echo ">> Run falco on test data, output to dir" +echo ">>> Run falco" +$meta_executable \ + --input "$test_data_dir/R1.fastq.gz;$test_data_dir/R2.fastq.gz" \ + --outdir "$TMPDIR/output1" + +echo ">>> Checking whether output exists" +[ ! -d "$TMPDIR/output1" ] && echo "Output directory not created" && exit 1 +[ ! -f "$TMPDIR/output1/R1.fastq.gz_fastqc_report.html" ] && echo "Report not created" && exit 1 +[ ! -f "$TMPDIR/output1/R1.fastq.gz_summary.txt" ] && echo "Summary not created" && exit 1 +[ ! -f "$TMPDIR/output1/R1.fastq.gz_fastqc_data.txt" ] && echo "fastqc_data not created" && exit 1 +[ ! -f "$TMPDIR/output1/R2.fastq.gz_fastqc_report.html" ] && echo "Report not created" && exit 1 +[ ! -f "$TMPDIR/output1/R2.fastq.gz_summary.txt" ] && echo "Summary not created" && exit 1 +[ ! -f "$TMPDIR/output1/R2.fastq.gz_fastqc_data.txt" ] && echo "fastqc_data not created" && exit 1 + +echo ">>> cleanup" +rm -rf "$TMPDIR/output1" + +echo ">> Run falco on test data, output to individual files" +echo ">>> Please note this is only possible for 1 input fastq file!" +echo ">>> Run falco" +$meta_executable \ + --input "$test_data_dir/R1.fastq.gz" \ + --data_filename "$TMPDIR/output2/data.txt" \ + --report_filename "$TMPDIR/output2/report.html" \ + --summary_filename "$TMPDIR/output2/summary.txt" \ + --outdir "$TMPDIR/output2/" + +echo ">>> Checking whether output exists" +[ ! -d "$TMPDIR/output2" ] && echo "Output directory not created" && exit 1 +[ ! -f "$TMPDIR/output2/report.html" ] && echo "Report not created" && exit 1 +[ ! -f "$TMPDIR/output2/summary.txt" ] && echo "Summary not created" && exit 1 +[ ! -f "$TMPDIR/output2/data.txt" ] && echo "fastqc_data not created" && exit 1 + +echo ">>> cleanup" +rm -rf $TMPDIR/output2/ + +echo ">> Run falco on test data, subsample" +echo ">>> Run falco" +$meta_executable \ + --input "$test_data_dir/R1.fastq.gz" \ + --data_filename "$TMPDIR/output3/data.txt" \ + --report_filename "$TMPDIR/output3/report.html" \ + --summary_filename "$TMPDIR/output3/summary.txt" \ + --subsample 100 \ + --outdir "$TMPDIR/output3" + +echo ">>> Checking whether output exists" +[ ! -d "$TMPDIR/output3" ] && echo "Output directory not created" && exit 1 +[ ! -f "$TMPDIR/output3/report.html" ] && echo "Report not created" && exit 1 +[ ! -f "$TMPDIR/output3/summary.txt" ] && echo "Summary not created" && exit 1 +[ ! -f "$TMPDIR/output3/data.txt" ] && echo "fastqc_data not created" && exit 1 + +echo ">>> cleanup" +rm -rf "$TMPDIR/output3/" + +echo "All tests succeeded!" diff --git a/src/fastp/config.vsh.yaml b/src/fastp/config.vsh.yaml new file mode 100644 index 00000000..b7d9062a --- /dev/null +++ b/src/fastp/config.vsh.yaml @@ -0,0 +1,576 @@ +name: fastp +description: | + An ultra-fast all-in-one FASTQ preprocessor (QC/adapters/trimming/filtering/splitting/merging...). + + Features: + + - comprehensive quality profiling for both before and after filtering data (quality curves, base contents, KMER, Q20/Q30, GC Ratio, duplication, adapter contents...) + - filter out bad reads (too low quality, too short, or too many N...) + - cut low quality bases for per read in its 5' and 3' by evaluating the mean quality from a sliding window (like Trimmomatic but faster). + - trim all reads in front and tail + - cut adapters. Adapter sequences can be automatically detected, which means you don't have to input the adapter sequences to trim them. + - correct mismatched base pairs in overlapped regions of paired end reads, if one base is with high quality while the other is with ultra low quality + - trim polyG in 3' ends, which is commonly seen in NovaSeq/NextSeq data. Trim polyX in 3' ends to remove unwanted polyX tailing (i.e. polyA tailing for mRNA-Seq data) + - preprocess unique molecular identifier (UMI) enabled data, shift UMI to sequence name. + - report JSON format result for further interpreting. + - visualize quality control and filtering results on a single HTML page (like FASTQC but faster and more informative). + - split the output to multiple files (0001.R1.gz, 0002.R1.gz...) to support parallel processing. Two modes can be used, limiting the total split file number, or limitting the lines of each split file. + - support long reads (data from PacBio / Nanopore devices). + - support reading from STDIN and writing to STDOUT + - support interleaved input + - support ultra-fast FASTQ-level deduplication +keywords: [RNA-Seq, Trimming, Quality control] +links: + repository: https://github.com/OpenGene/fastp + documentation: https://github.com/OpenGene/fastp/blob/master/README.md +references: + doi: "10.1093/bioinformatics/bty560" +license: MIT +argument_groups: + - name: Inputs + description: | + `fastp` supports both single-end (SE) and paired-end (PE) input. + + - for SE data, you only have to specify read1 input by `-i` or `--in1`. + - for PE data, you should also specify read2 input by `-I` or `--in2`. + arguments: + - name: --in1 + alternatives: [-i] + type: file + description: Input FastQ file. Must be single-end or paired-end R1. Can be gzipped. + required: true + example: in.R1.fq.gz + - name: --in2 + alternatives: [-I] + type: file + description: Input FastQ file. Must be paired-end R2. Can be gzipped. + required: false + example: in.R2.fq.gz + - name: Outputs + description: | + + - for SE data, you only have to specify read1 output by `-o` or `--out1`. + - for PE data, you should also specify read2 output by `-O` or `--out2`. + - if you don't specify the output file names, no output files will be written, but the QC will still be done for both data before and after filtering. + - the output will be gzip-compressed if its file name ends with `.gz` + arguments: + - name: --out1 + alternatives: [-o] + type: file + description: The single-end or paired-end R1 reads that pass QC. Will be gzipped if its file name ends with `.gz`. + required: true + example: out.R1.fq.gz + direction: output + - name: --out2 + alternatives: [-O] + type: file + description: The paired-end R2 reads that pass QC. Will be gzipped if its file name ends with `.gz`. + required: false + example: out.R2.fq.gz + direction: output + - name: --unpaired1 + type: file + description: Store the reads that `read1` passes filters but its paired `read2` doesn't. + required: false + example: unpaired.R1.fq.gz + direction: output + - name: --unpaired2 + type: file + description: Store the reads that `read2` passes filters but its paired `read1` doesn't. + required: false + example: unpaired.R2.fq.gz + direction: output + - name: --failed_out + type: file + description: | + Store the reads that fail filters. + + If one read failed and is written to --failed_out, its failure reason will be appended to its read name. For example, failed_quality_filter, failed_too_short etc. + For PE data, if unpaired reads are not stored (by giving --unpaired1 or --unpaired2), the failed pair of reads will be put together. If one read passes the filters but its pair doesn't, the failure reason will be paired_read_is_failing. + required: false + example: failed.fq.gz + direction: output + - name: --overlapped_out + type: file + description: | + For each read pair, output the overlapped region if it has no any mismatched base. + direction: output + - name: Report output arguments + arguments: + - name: --json + alternatives: [-j] + type: file + description: | + The json format report file name + example: out.json + direction: output + - name: --html + type: file + description: | + The html format report file name + example: out.html + direction: output + - name: --report_title + type: string + description: | + The title of the html report, default is "fastp report". + example: fastp report + - name: Adapter trimming + description: | + Adapter trimming is enabled by default, but you can disable it by `-A` or `--disable_adapter_trimming`. Adapter sequences can be automatically detected for both PE/SE data. + + - For SE data, the adapters are evaluated by analyzing the tails of first ~1M reads. This evaluation may be inacurrate, and you can specify the adapter sequence by `-a` or `--adapter_sequence` option. If adapter sequence is specified, the auto detection for SE data will be disabled. + - For PE data, the adapters can be detected by per-read overlap analysis, which seeks for the overlap of each pair of reads. This method is robust and fast, so normally you don't have to input the adapter sequence even you know it. But you can still specify the adapter sequences for read1 by `--adapter_sequence`, and for read2 by `--adapter_sequence_r2`. If `fastp` fails to find an overlap (i.e. due to low quality bases), it will use these sequences to trim adapters for read1 and read2 respectively. + - For PE data, the adapter sequence auto-detection is disabled by default since the adapters can be trimmed by overlap analysis. However, you can specify `--detect_adapter_for_pe` to enable it. + - For PE data, `fastp` will run a little slower if you specify the sequence adapters or enable adapter auto-detection, but usually result in a slightly cleaner output, since the overlap analysis may fail due to sequencing errors or adapter dimers. + - The most widely used adapter is the Illumina TruSeq adapters. If your data is from the TruSeq library, you can add `--adapter_sequence=AGATCGGAAGAGCACACGTCTGAACTCCAGTCA --adapter_sequence_r2=AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT` to your command lines, or enable auto detection for PE data by specifing `detect_adapter_for_pe`. + - `fastp` contains some built-in known adapter sequences for better auto-detection. If you want to make some adapters to be a part of the built-in adapters, please file an issue. + + You can also specify --adapter_fasta to give a FASTA file to tell fastp to trim multiple adapters in this FASTA file. Here is a sample of such adapter FASTA file: + + ``` + >Illumina TruSeq Adapter Read 1 + AGATCGGAAGAGCACACGTCTGAACTCCAGTCA + >Illumina TruSeq Adapter Read 2 + AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT + >polyA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ``` + + The adapter sequence in this file should be at least 6bp long, otherwise it will be skipped. And you can give whatever you want to trim, rather than regular sequencing adapters (i.e. polyA). + + `fastp` first trims the auto-detected adapter or the adapter sequences given by `--adapter_sequence | --adapter_sequence_r2`, then trims the adapters given by `--adapter_fasta` one by one. + + The sequence distribution of trimmed adapters can be found at the HTML/JSON reports. + arguments: + - name: --disable_adapter_trimming + alternatives: [-A] + type: boolean_true + description: | + Disable adapter trimming. + - name: --detect_adapter_for_pe + type: boolean_true + description: | + By default, the auto-detection for adapter is for SE data input only, turn on this option to enable it for PE data. + - name: --adapter_sequence + alternatives: [-a] + type: string + description: | + The adapter sequences to be trimmed. For SE data, if not specified, the adapters will be auto-detected. For PE data, this is used if R1/R2 are found not overlapped + - name: --adapter_sequence_r2 + type: string + description: | + The adapter sequences to be trimmed for R2. This is used for PE data if R1/R2 are found overlapped. + - name: --adapter_fasta + type: file + description: | + A FASTA file containing all the adapter sequences to be trimmed. For SE data, if not specified, the adapters will be auto-detected. For PE data, this is used if R1/R2 are found not overlapped. + - name: Base trimming + arguments: + - name: --trim_front1 + alternatives: [-f] + type: integer + description: | + Trimming how many bases in front for read1, default is 0. + example: 0 + - name: --trim_tail1 + alternatives: [-t] + type: integer + description: | + Trimming how many bases in tail for read1, default is 0. + example: 0 + - name: --max_len1 + alternatives: [-b] + type: integer + min: 0 + description: | + If read1 is longer than max_len1, then trim read1 at its tail to make it as long as max_len1. Default 0 means no limitation. + - name: --trim_front2 + alternatives: [-F] + type: integer + description: | + Trimming how many bases in front for read2, default is 0. + example: 0 + - name: --trim_tail2 + alternatives: [-T] + type: integer + description: | + Trimming how many bases in tail for read2, default is 0. + example: 0 + - name: --max_len2 + alternatives: [-B] + type: integer + min: 0 + description: | + If read2 is longer than max_len2, then trim read2 at its tail to make it as long as max_len2. Default 0 means no limitation. + - name: Merging mode + description: Allows merging paired-end reads into a single longer read if they are overlapping. + arguments: + - name: --merge + alternatives: [-m] + type: boolean_true + description: | + For paired-end input, merge each pair of reads into a single read if they are overlapped. The merged reads will be written to the file given by --merged_out, the unmerged reads will be written to the files specified by --out1 and --out2. The merging mode is disabled by default. + - name: --merged_out + type: file + description: | + In the merging mode, specify the file name to store merged output, or specify --stdout to stream the merged output. + direction: output + example: merged.fq.gz + - name: --include_unmerged + type: boolean_true + description: | + In the merging mode, write the unmerged or unpaired reads to the file specified by --merge. Disabled by default. + - name: Additional input arguments + description: Affects how the input is read. + arguments: + - name: --interleaved_in + type: boolean_true + description: | + Indicate that is an interleaved FASTQ which contains both read1 and read2. Disabled by default. + - name: --fix_mgi_id + type: boolean_true + description: | + The MGI FASTQ ID format is not compatible with many BAM operation tools, enable this option to fix it. + - name: --phred64 + alternatives: ["-6"] + type: boolean_true + description: | + Indicate the input is using phred64 scoring (it'll be converted to phred33, so the output will still be phred33) + - name: Additional output arguments + description: Affects how the output is written. + arguments: + - name: --compression + alternatives: ["-z"] + type: integer + description: | + Compression level for gzip output (1 ~ 9). 1 is fastest, 9 is smallest, default is 4. + example: 4 + min: 1 + max: 9 + - name: --dont_overwrite + type: boolean_true + description: | + Don't overwrite existing files. Overwritting is allowed by default. + - name: Logging arguments + arguments: + - name: --verbose + alternatives: [-V] + type: boolean_true + description: Output verbose log information (i.e. when every 1M reads are processed). + - name: Processing arguments + arguments: + - name: --reads_to_process + type: long + description: | + Specify how many reads/pairs to be processed. Default 0 means process all reads. + example: 1000000 + min: 0 + - name: Deduplication arguments + arguments: + - name: --dedup + type: boolean_true + description: | + Enable deduplication to drop the duplicated reads/pairs + - name: --dup_calc_accuracy + type: integer + description: | + Accuracy level to calculate duplication (1~6). Higher level uses more memory (1G, 2G, 4G, 8G, 16G, 24G). Default 1 for no-dedup mode, and 3 for dedup mode. + example: 3 + min: 1 + max: 6 + - name: --dont_eval_duplication + type: boolean_true + description: | + Don't evaluate duplication rate to save time and use less memory. + - name: PolyG tail trimming arguments + arguments: + - name: --trim_poly_g + alternatives: [-g] + type: boolean_true + description: | + Force polyG tail trimming, by default trimming is automatically enabled for Illumina NextSeq/NovaSeq data + - name: --poly_g_min_len + type: integer + description: | + The minimum length to detect polyG in the read tail. 10 by default. + example: 10 + min: 1 + - name: --disable_trim_poly_g + alternatives: [-G] + type: boolean_true + description: | + Disable polyG tail trimming, by default trimming is automatically enabled for Illumina NextSeq/NovaSeq data + - name: PolyX tail trimming arguments + arguments: + - name: --trim_poly_x + alternatives: [-x] + type: boolean_true + description: | + Enable polyX trimming in 3' ends. + - name: --poly_x_min_len + type: integer + description: | + The minimum length to detect polyX in the read tail. 10 by default. + example: 10 + min: 1 + - name: Cut arguments + arguments: + - name: --cut_front + alternatives: ["-5"] + type: integer + description: | + Move a sliding window from front (5') to tail, drop the bases in the window if its mean quality < threshold, stop otherwise. + - name: --cut_tail + alternatives: ["-3"] + type: integer + description: | + Move a sliding window from tail (3') to front, drop the bases in the window if its mean quality < threshold, stop otherwise. + - name: --cut_right + alternatives: ["-r"] + type: integer + description: | + Move a sliding window from front to tail, if meet one window with mean quality < threshold, drop the bases in the window and the right part, and then stop. + - name: --cut_window_size + alternatives: ["-W"] + type: integer + description: | + The window size option shared by cut_front, cut_tail or cut_sliding. Range: 1~1000, default: 4. + example: 4 + min: 1 + - name: --cut_mean_quality + alternatives: ["-M"] + type: integer + description: | + The mean quality requirement option shared by cut_front, cut_tail or cut_sliding. Range: 1~36 default: 20 (Q20) + example: 20 + min: 0 + - name: --cut_front_window_size + type: integer + description: | + The window size option of cut_front, default to cut_window_size if not specified. + example: 4 + min: 1 + - name: --cut_front_mean_quality + type: integer + description: | + The mean quality requirement option of cut_front, default to cut_mean_quality if not specified. + example: 20 + min: 0 + - name: --cut_tail_window_size + type: integer + description: | + The window size option of cut_tail, default to cut_window_size if not specified. + example: 4 + min: 1 + - name: --cut_tail_mean_quality + type: integer + description: | + The mean quality requirement option of cut_tail, default to cut_mean_quality if not specified. + example: 20 + min: 0 + - name: --cut_right_window_size + type: integer + description: | + The window size option of cut_right, default to cut_window_size if not specified. + example: 4 + min: 1 + - name: --cut_right_mean_quality + type: integer + description: | + The mean quality requirement option of cut_right, default to cut_mean_quality if not specified. + example: 20 + min: 0 + - name: Quality filtering arguments + arguments: + - name: --disable_quality_filtering + alternatives: [-Q] + type: boolean_true + description: | + Quality filtering is enabled by default. If this option is specified, quality filtering is disabled. + - name: --qualified_quality_phred + alternatives: [-q] + type: integer + description: | + The quality value that a base is qualified. Default 15 means phred quality >=Q15 is qualified. + example: 15 + min: 0 + - name: --unqualified_percent_limit + alternatives: [-u] + type: integer + description: | + How many percents of bases are allowed to be unqualified (0~100). Default 40 means 40%. + example: 40 + min: 0 + max: 100 + - name: --n_base_limit + alternatives: [-n] + type: integer + description: | + If one read's number of N base is >n_base_limit, then this read/pair is discarded. Default is 5. + example: 5 + min: 0 + - name: --average_qual + alternatives: [-e] + type: integer + description: | + If one read's average quality score =1000), a sequential number prefix will be added to output name ( 0001.out.fq, 0002.out.fq...), disabled by default. + # - name: --split_prefix_digits + # type: integer + # description: | + # The digits for the sequential number padding (1~10), default is 4, so the filename will be padded as 0001.xxx, 0 to disable padding. + # example: 4 +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/fastp:0.23.4--hadf994f_2 + setup: + - type: docker + run: | + fastp --version 2>&1 | sed 's# #: "#;s#$#"#' > /var/software_versions.txt +runners: + - type: executable + - type: nextflow diff --git a/src/fastp/help.txt b/src/fastp/help.txt new file mode 100644 index 00000000..d34917b2 --- /dev/null +++ b/src/fastp/help.txt @@ -0,0 +1,93 @@ +```bash +fastp --help +``` + +usage: fastp [options] ... +options: + -i, --in1 read1 input file name (string [=]) + -o, --out1 read1 output file name (string [=]) + -I, --in2 read2 input file name (string [=]) + -O, --out2 read2 output file name (string [=]) + --unpaired1 for PE input, if read1 passed QC but read2 not, it will be written to unpaired1. Default is to discard it. (string [=]) + --unpaired2 for PE input, if read2 passed QC but read1 not, it will be written to unpaired2. If --unpaired2 is same as --unpaired1 (default mode), both unpaired reads will be written to this same file. (string [=]) + --overlapped_out for each read pair, output the overlapped region if it has no any mismatched base. (string [=]) + --failed_out specify the file to store reads that cannot pass the filters. (string [=]) + -m, --merge for paired-end input, merge each pair of reads into a single read if they are overlapped. The merged reads will be written to the file given by --merged_out, the unmerged reads will be written to the files specified by --out1 and --out2. The merging mode is disabled by default. + --merged_out in the merging mode, specify the file name to store merged output, or specify --stdout to stream the merged output (string [=]) + --include_unmerged in the merging mode, write the unmerged or unpaired reads to the file specified by --merge. Disabled by default. + -6, --phred64 indicate the input is using phred64 scoring (it'll be converted to phred33, so the output will still be phred33) + -z, --compression compression level for gzip output (1 ~ 9). 1 is fastest, 9 is smallest, default is 4. (int [=4]) + --stdin input from STDIN. If the STDIN is interleaved paired-end FASTQ, please also add --interleaved_in. + --stdout stream passing-filters reads to STDOUT. This option will result in interleaved FASTQ output for paired-end output. Disabled by default. + --interleaved_in indicate that is an interleaved FASTQ which contains both read1 and read2. Disabled by default. + --reads_to_process specify how many reads/pairs to be processed. Default 0 means process all reads. (int [=0]) + --dont_overwrite don't overwrite existing files. Overwritting is allowed by default. + --fix_mgi_id the MGI FASTQ ID format is not compatible with many BAM operation tools, enable this option to fix it. + -V, --verbose output verbose log information (i.e. when every 1M reads are processed). + -A, --disable_adapter_trimming adapter trimming is enabled by default. If this option is specified, adapter trimming is disabled + -a, --adapter_sequence the adapter for read1. For SE data, if not specified, the adapter will be auto-detected. For PE data, this is used if R1/R2 are found not overlapped. (string [=auto]) + --adapter_sequence_r2 the adapter for read2 (PE data only). This is used if R1/R2 are found not overlapped. If not specified, it will be the same as (string [=auto]) + --adapter_fasta specify a FASTA file to trim both read1 and read2 (if PE) by all the sequences in this FASTA file (string [=]) + --detect_adapter_for_pe by default, the auto-detection for adapter is for SE data input only, turn on this option to enable it for PE data. + -f, --trim_front1 trimming how many bases in front for read1, default is 0 (int [=0]) + -t, --trim_tail1 trimming how many bases in tail for read1, default is 0 (int [=0]) + -b, --max_len1 if read1 is longer than max_len1, then trim read1 at its tail to make it as long as max_len1. Default 0 means no limitation (int [=0]) + -F, --trim_front2 trimming how many bases in front for read2. If it's not specified, it will follow read1's settings (int [=0]) + -T, --trim_tail2 trimming how many bases in tail for read2. If it's not specified, it will follow read1's settings (int [=0]) + -B, --max_len2 if read2 is longer than max_len2, then trim read2 at its tail to make it as long as max_len2. Default 0 means no limitation. If it's not specified, it will follow read1's settings (int [=0]) + -D, --dedup enable deduplication to drop the duplicated reads/pairs + --dup_calc_accuracy accuracy level to calculate duplication (1~6), higher level uses more memory (1G, 2G, 4G, 8G, 16G, 24G). Default 1 for no-dedup mode, and 3 for dedup mode. (int [=0]) + --dont_eval_duplication don't evaluate duplication rate to save time and use less memory. + -g, --trim_poly_g force polyG tail trimming, by default trimming is automatically enabled for Illumina NextSeq/NovaSeq data + --poly_g_min_len the minimum length to detect polyG in the read tail. 10 by default. (int [=10]) + -G, --disable_trim_poly_g disable polyG tail trimming, by default trimming is automatically enabled for Illumina NextSeq/NovaSeq data + -x, --trim_poly_x enable polyX trimming in 3' ends. + --poly_x_min_len the minimum length to detect polyX in the read tail. 10 by default. (int [=10]) + -5, --cut_front move a sliding window from front (5') to tail, drop the bases in the window if its mean quality < threshold, stop otherwise. + -3, --cut_tail move a sliding window from tail (3') to front, drop the bases in the window if its mean quality < threshold, stop otherwise. + -r, --cut_right move a sliding window from front to tail, if meet one window with mean quality < threshold, drop the bases in the window and the right part, and then stop. + -W, --cut_window_size the window size option shared by cut_front, cut_tail or cut_sliding. Range: 1~1000, default: 4 (int [=4]) + -M, --cut_mean_quality the mean quality requirement option shared by cut_front, cut_tail or cut_sliding. Range: 1~36 default: 20 (Q20) (int [=20]) + --cut_front_window_size the window size option of cut_front, default to cut_window_size if not specified (int [=4]) + --cut_front_mean_quality the mean quality requirement option for cut_front, default to cut_mean_quality if not specified (int [=20]) + --cut_tail_window_size the window size option of cut_tail, default to cut_window_size if not specified (int [=4]) + --cut_tail_mean_quality the mean quality requirement option for cut_tail, default to cut_mean_quality if not specified (int [=20]) + --cut_right_window_size the window size option of cut_right, default to cut_window_size if not specified (int [=4]) + --cut_right_mean_quality the mean quality requirement option for cut_right, default to cut_mean_quality if not specified (int [=20]) + -Q, --disable_quality_filtering quality filtering is enabled by default. If this option is specified, quality filtering is disabled + -q, --qualified_quality_phred the quality value that a base is qualified. Default 15 means phred quality >=Q15 is qualified. (int [=15]) + -u, --unqualified_percent_limit how many percents of bases are allowed to be unqualified (0~100). Default 40 means 40% (int [=40]) + -n, --n_base_limit if one read's number of N base is >n_base_limit, then this read/pair is discarded. Default is 5 (int [=5]) + -e, --average_qual if one read's average quality score =1000), a sequential number prefix will be added to output name ( 0001.out.fq, 0002.out.fq...), disabled by default (long [=0]) + -d, --split_prefix_digits the digits for the sequential number padding (1~10), default is 4, so the filename will be padded as 0001.xxx, 0 to disable padding (int [=4]) + --cut_by_quality5 DEPRECATED, use --cut_front instead. + --cut_by_quality3 DEPRECATED, use --cut_tail instead. + --cut_by_quality_aggressive DEPRECATED, use --cut_right instead. + --discard_unmerged DEPRECATED, no effect now, see the introduction for merging. + -?, --help print this message diff --git a/src/fastp/script.sh b/src/fastp/script.sh new file mode 100644 index 00000000..4bb37c87 --- /dev/null +++ b/src/fastp/script.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +# disable flags +[[ "$par_disable_adapter_trimming" == "false" ]] && unset par_disable_adapter_trimming +[[ "$par_detect_adapter_for_pe" == "false" ]] && unset par_detect_adapter_for_pe +[[ "$par_merge" == "false" ]] && unset par_merge +[[ "$par_include_unmerged" == "false" ]] && unset par_include_unmerged +[[ "$par_interleaved_in" == "false" ]] && unset par_interleaved_in +[[ "$par_fix_mgi_id" == "false" ]] && unset par_fix_mgi_id +[[ "$par_phred64" == "false" ]] && unset par_phred64 +[[ "$par_dont_overwrite" == "false" ]] && unset par_dont_overwrite +[[ "$par_verbose" == "false" ]] && unset par_verbose +[[ "$par_dedup" == "false" ]] && unset par_dedup +[[ "$par_dont_eval_duplication" == "false" ]] && unset par_dont_eval_duplication +[[ "$par_trim_poly_g" == "false" ]] && unset par_trim_poly_g +[[ "$par_disable_trim_poly_g" == "false" ]] && unset par_disable_trim_poly_g +[[ "$par_trim_poly_x" == "false" ]] && unset par_trim_poly_x +[[ "$par_disable_quality_filtering" == "false" ]] && unset par_disable_quality_filtering +[[ "$par_disable_length_filtering" == "false" ]] && unset par_disable_length_filtering +[[ "$par_low_complexity_filter" == "false" ]] && unset par_low_complexity_filter +[[ "$par_umi" == "false" ]] && unset par_umi +[[ "$par_overrepresentation_analysis" == "false" ]] && unset par_overrepresentation_analysis + +# run command +fastp \ + -i "$par_in1" \ + -o "$par_out1" \ + ${par_in2:+--in2 "${par_in2}"} \ + ${par_out2:+--out2 "${par_out2}"} \ + ${par_unpaired1:+--unpaired1 "${par_unpaired1}"} \ + ${par_unpaired2:+--unpaired2 "${par_unpaired2}"} \ + ${par_failed_out:+--failed_out "${par_failed_out}"} \ + ${par_overlapped_out:+--overlapped_out "${par_overlapped_out}"} \ + ${par_json:+--json "${par_json}"} \ + ${par_html:+--html "${par_html}"} \ + ${par_report_title:+--report_title "${par_report_title}"} \ + ${par_disable_adapter_trimming:+--disable_adapter_trimming} \ + ${par_detect_adapter_for_pe:+--detect_adapter_for_pe} \ + ${par_adapter_sequence:+--adapter_sequence "${par_adapter_sequence}"} \ + ${par_adapter_sequence_r2:+--adapter_sequence_r2 "${par_adapter_sequence_r2}"} \ + ${par_adapter_fasta:+--adapter_fasta "${par_adapter_fasta}"} \ + ${par_trim_front1:+--trim_front1 "${par_trim_front1}"} \ + ${par_trim_tail1:+--trim_tail1 "${par_trim_tail1}"} \ + ${par_max_len1:+--max_len1 "${par_max_len1}"} \ + ${par_trim_front2:+--trim_front2 "${par_trim_front2}"} \ + ${par_trim_tail2:+--trim_tail2 "${par_trim_tail2}"} \ + ${par_max_len2:+--max_len2 "${par_max_len2}"} \ + ${par_merge:+--merge} \ + ${par_merged_out:+--merged_out "${par_merged_out}"} \ + ${par_include_unmerged:+--include_unmerged} \ + ${par_interleaved_in:+--interleaved_in} \ + ${par_fix_mgi_id:+--fix_mgi_id} \ + ${par_phred64:+--phred64} \ + ${par_compression:+--compression "${par_compression}"} \ + ${par_dont_overwrite:+--dont_overwrite} \ + ${par_verbose:+--verbose} \ + ${par_reads_to_process:+--reads_to_process "${par_reads_to_process}"} \ + ${par_dedup:+--dedup} \ + ${par_dup_calc_accuracy:+--dup_calc_accuracy "${par_dup_calc_accuracy}"} \ + ${par_dont_eval_duplication:+--dont_eval_duplication} \ + ${par_trim_poly_g:+--trim_poly_g} \ + ${par_poly_g_min_len:+--poly_g_min_len "${par_poly_g_min_len}"} \ + ${par_disable_trim_poly_g:+--disable_trim_poly_g} \ + ${par_trim_poly_x:+--trim_poly_x} \ + ${par_poly_x_min_len:+--poly_x_min_len "${par_poly_x_min_len}"} \ + ${par_cut_front:+--cut_front "${par_cut_front}"} \ + ${par_cut_tail:+--cut_tail "${par_cut_tail}"} \ + ${par_cut_right:+--cut_right "${par_cut_right}"} \ + ${par_cut_window_size:+--cut_window_size "${par_cut_window_size}"} \ + ${par_cut_mean_quality:+--cut_mean_quality "${par_cut_mean_quality}"} \ + ${par_cut_front_window_size:+--cut_front_window_size "${par_cut_front_window_size}"} \ + ${par_cut_front_mean_quality:+--cut_front_mean_quality "${par_cut_front_mean_quality}"} \ + ${par_cut_tail_window_size:+--cut_tail_window_size "${par_cut_tail_window_size}"} \ + ${par_cut_tail_mean_quality:+--cut_tail_mean_quality "${par_cut_tail_mean_quality}"} \ + ${par_cut_right_window_size:+--cut_right_window_size "${par_cut_right_window_size}"} \ + ${par_cut_right_mean_quality:+--cut_right_mean_quality "${par_cut_right_mean_quality}"} \ + ${par_disable_quality_filtering:+--disable_quality_filtering} \ + ${par_qualified_quality_phred:+--qualified_quality_phred "${par_qualified_quality_phred}"} \ + ${par_unqualified_percent_limit:+--unqualified_percent_limit "${par_unqualified_percent_limit}"} \ + ${par_n_base_limit:+--n_base_limit "${par_n_base_limit}"} \ + ${par_average_qual:+--average_qual "${par_average_qual}"} \ + ${par_disable_length_filtering:+--disable_length_filtering} \ + ${par_length_required:+--length_required "${par_length_required}"} \ + ${par_length_limit:+--length_limit "${par_length_limit}"} \ + ${par_low_complexity_filter:+--low_complexity_filter} \ + ${par_complexity_threshold:+--complexity_threshold "${par_complexity_threshold}"} \ + ${par_filter_by_index1:+--filter_by_index1 "${par_filter_by_index1}"} \ + ${par_filter_by_index2:+--filter_by_index2 "${par_filter_by_index2}"} \ + ${par_filter_by_index_threshold:+--filter_by_index_threshold "${par_filter_by_index_threshold}"} \ + ${par_correction:+--correction} \ + ${par_overlap_len_require:+--overlap_len_require "${par_overlap_len_require}"} \ + ${par_overlap_diff_limit:+--overlap_diff_limit "${par_overlap_diff_limit}"} \ + ${par_overlap_diff_percent_limit:+--overlap_diff_percent_limit "${par_overlap_diff_percent_limit}"} \ + ${par_umi:+--umi} \ + ${par_umi_loc:+--umi_loc "${par_umi_loc}"} \ + ${par_umi_len:+--umi_len "${par_umi_len}"} \ + ${par_umi_prefix:+--umi_prefix "${par_umi_prefix}"} \ + ${par_umi_skip:+--umi_skip "${par_umi_skip}"} \ + ${par_umi_delim:+--umi_delim "${par_umi_delim}"} \ + ${par_overrepresentation_analysis:+--overrepresentation_analysis} \ + ${par_overrepresentation_sampling:+--overrepresentation_sampling "${par_overrepresentation_sampling}"} \ + ${meta_cpus:+--thread "${meta_cpus}"} diff --git a/src/fastp/test.sh b/src/fastp/test.sh new file mode 100644 index 00000000..1b1f6f0c --- /dev/null +++ b/src/fastp/test.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +set -e + +## VIASH START +meta_executable="target/docker/fastp/fastp" +meta_resources_dir="src/fastp" +## VIASH END + +######################################################################################### +mkdir fastp_se +cd fastp_se + +echo "> Run fastp on SE" +"$meta_executable" \ + --in1 "$meta_resources_dir/test_data/se/a.fastq" \ + --out1 "trimmed.fastq" \ + --failed_out "failed.fastq" \ + --json "report.json" \ + --html "report.html" \ + --adapter_sequence ACGGCTAGCTA + +echo ">> Check if output exists" +[ ! -f "trimmed.fastq" ] && echo ">> trimmed.fastq does not exist" && exit 1 +[ ! -f "failed.fastq" ] && echo ">> failed.fastq does not exist" && exit 1 +[ ! -f "report.json" ] && echo ">> report.json does not exist" && exit 1 +[ ! -f "report.html" ] && echo ">> report.html does not exist" && exit 1 + +######################################################################################### +cd .. +mkdir fastp_pe_minimal +cd fastp_pe_minimal + +echo ">> Run fastp on PE with minimal parameters" +"$meta_executable" \ + --in1 "$meta_resources_dir/test_data/pe/a.1.fastq" \ + --in2 "$meta_resources_dir/test_data/pe/a.2.fastq" \ + --out1 "trimmed_1.fastq" \ + --out2 "trimmed_2.fastq" + +echo ">> Check if output exists" +[ ! -f "trimmed_1.fastq" ] && echo ">> trimmed_1.fastq does not exist" && exit 1 +[ ! -f "trimmed_2.fastq" ] && echo ">> trimmed_2.fastq does not exist" && exit 1 + +######################################################################################### +cd .. +mkdir fastp_pe_many +cd fastp_pe_many + +echo ">> Run fastp on PE with many parameters" +"$meta_executable" \ + --in1 "$meta_resources_dir/test_data/pe/a.1.fastq" \ + --in2 "$meta_resources_dir/test_data/pe/a.2.fastq" \ + --out1 "trimmed_1.fastq" \ + --out2 "trimmed_2.fastq" \ + --failed_out "failed.fastq" \ + --json "report.json" \ + --html "report.html" \ + --adapter_sequence ACGGCTAGCTA \ + --adapter_sequence_r2 AGATCGGAAGAGCACACGTCTGAACTCCAGTCAC \ + --merge \ + --merged_out "merged.fastq" + +echo ">> Check if output exists" +[ ! -f "trimmed_1.fastq" ] && echo ">> trimmed_1.fastq does not exist" && exit 1 +[ ! -f "trimmed_2.fastq" ] && echo ">> trimmed_2.fastq does not exist" && exit 1 +[ ! -f "failed.fastq" ] && echo ">> failed.fastq does not exist" && exit 1 +[ ! -f "report.json" ] && echo ">> report.json does not exist" && exit 1 +[ ! -f "report.html" ] && echo ">> report.html does not exist" && exit 1 +[ ! -f "merged.fastq" ] && echo ">> merged.fastq does not exist" && exit 1 + +######################################################################################### + +echo "> Test successful" \ No newline at end of file diff --git a/src/fastp/test_data/pe/a.1.fastq b/src/fastp/test_data/pe/a.1.fastq new file mode 100644 index 00000000..42735560 --- /dev/null +++ b/src/fastp/test_data/pe/a.1.fastq @@ -0,0 +1,4 @@ +@1 +ACGGCAT ++ +!!!!!!! diff --git a/src/fastp/test_data/pe/a.2.fastq b/src/fastp/test_data/pe/a.2.fastq new file mode 100644 index 00000000..42735560 --- /dev/null +++ b/src/fastp/test_data/pe/a.2.fastq @@ -0,0 +1,4 @@ +@1 +ACGGCAT ++ +!!!!!!! diff --git a/src/fastp/test_data/script.sh b/src/fastp/test_data/script.sh new file mode 100755 index 00000000..725eef6d --- /dev/null +++ b/src/fastp/test_data/script.sh @@ -0,0 +1,10 @@ +# fastp test data + +# Test data was obtained from https://github.com/snakemake/snakemake-wrappers/tree/master/bio/fastp/test + +if [ ! -d /tmp/snakemake-wrappers ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers /tmp/snakemake-wrappers +fi + +cp -r /tmp/snakemake-wrappers/bio/fastp/test/reads/* src/fastp/test_data + diff --git a/src/fastp/test_data/se/a.fastq b/src/fastp/test_data/se/a.fastq new file mode 100644 index 00000000..42735560 --- /dev/null +++ b/src/fastp/test_data/se/a.fastq @@ -0,0 +1,4 @@ +@1 +ACGGCAT ++ +!!!!!!! diff --git a/src/featurecounts/config.vsh.yaml b/src/featurecounts/config.vsh.yaml new file mode 100644 index 00000000..8697b1fe --- /dev/null +++ b/src/featurecounts/config.vsh.yaml @@ -0,0 +1,336 @@ +name: featurecounts +description: | + featureCounts is a read summarization program for counting reads generated from either RNA or genomic DNA sequencing experiments by implementing highly efficient chromosome hashing and feature blocking techniques. It works with either single or paired-end reads and provides a wide range of options appropriate for different sequencing applications. +keywords: ["Read counting", "Genomic features"] +links: + homepage: https://subread.sourceforge.net/ + documentation: https://subread.sourceforge.net/SubreadUsersGuide.pdf + repository: https://github.com/ShiLab-Bioinformatics/subread +references: + doi: "10.1093/bioinformatics/btt656" +license: GPL-3.0 +requirements: + commands: [ featureCounts ] + +argument_groups: + - name: Inputs + arguments: + - name: --annotation + alternatives: ["-a"] + type: file + description: | + Name of an annotation file. GTF/GFF format by default. See '--format' option for more format information. + required: true + example: annotation.gtf + - name: --input + alternatives: ["-i"] + type: file + multiple: true + description: | + A list of SAM or BAM format files separated by semi-colon (;). They can be either name or location sorted. Location-sorted paired-end reads are automatically sorted by read names. + required: true + example: input_file1.bam + + - name: Outputs + arguments: + - name: --counts + alternatives: ["-o"] + type: file + direction: output + description: | + Name of output file including read counts in tab delimited format. + required: true + example: features.tsv + - name: --summary + type: file + direction: output + description: | + Summary statistics of counting results in tab delimited format. + required: false + example: summary.tsv + - name: --junctions + type: file + direction: output + description: | + Count number of reads supporting each exon-exon junction. Junctions were identified from those exon-spanning reads in the input (containing 'N' in CIGAR string). + example: junctions.txt + required: false + + - name: Annotation + arguments: + - name: --format + alternatives: ["-F"] + type: string + description: | + Specify format of the provided annotation file. Acceptable formats include 'GTF' (or compatible GFF format) and 'SAF'. 'GTF' by default. + choices: [GTF, GFF, SAF] + example: "GTF" + required: false + - name: --feature_type + alternatives: ["-t"] + type: string + description: | + Specify feature type(s) in a GTF annotation. If multiple types are provided, they should be separated by ';' with no space in between. 'exon' by default. Rows in the annotation with a matched feature will be extracted and used for read mapping. + example: "exon" + required: false + multiple: true + - name: --attribute_type + alternatives: ["-g"] + type: string + description: | + Specify attribute type in GTF annotation. 'gene_id' by default. Meta-features used for read counting will be extracted from annotation using the provided value. + example: "gene_id" + required: false + - name: --extra_attributes + type: string + description: | + Extract extra attribute types from the provided GTF annotation and include them in the counting output. These attribute types will not be used to group features. If more than one attribute type is provided they should be separated by semicolon (;). + required: false + multiple: true + - name: --chrom_alias + alternatives: ["-A"] + type: file + description: | + Provide a chromosome name alias file to match chr names in annotation with those in the reads. This should be a two-column comma-delimited text file. Its first column should include chr names in the annotation and its second column should include chr names in the reads. Chr names are case sensitive. No column header should be included in the file. + required: false + example: chrom_alias.csv + + - name: Level of summarization + arguments: + - name: --feature_level + alternatives: ["-f"] + type: boolean_true + description: | + Perform read counting at feature level (eg. counting reads for exons rather than genes). + + - name: Overlap between reads and features + arguments: + - name: --overlapping + alternatives: ["-O"] + type: boolean_true + description: | + Assign reads to all their overlapping meta-features (or features if '--feature_level' is specified). + - name: --min_overlap + type: integer + description: | + Minimum number of overlapping bases in a read that is required for read assignment. 1 by default. Number of overlapping bases is counted from both reads if paired end. If a negative value is provided, then a gap of up to specified size will be allowed between read and the feature that the read is assigned to. + required: false + example: 1 + - name: --frac_overlap + type: double + description: | + Minimum fraction of overlapping bases in a read that is required for read assignment. Value should be within range [0,1]. 0 by default. Number of overlapping bases is counted from both reads if paired end. Both this option and '--min_overlap' option need to be satisfied for read assignment. + required: false + min: 0 + max: 1 + example: 0 + - name: --frac_overlap_feature + type: double + description: | + Minimum fraction of overlapping bases in a feature that is required for read assignment. Value should be within range [0,1]. 0 by default. + required: false + min: 0 + max: 1 + example: 0 + - name: --largest_overlap + type: boolean_true + description: | + Assign reads to a meta-feature/feature that has the largest number of overlapping bases. + - name: --non_overlap + type: integer + description: | + Maximum number of non-overlapping bases in a read (or a read pair) that is allowed when being assigned to a feature. No limit is set by default. + required: false + - name: --non_overlap_feature + type: integer + description: | + Maximum number of non-overlapping bases in a feature that is allowed in read assignment. No limit is set by default. + required: false + - name: --read_extension5 + type: integer + description: | + Reads are extended upstream by bases from their 5' end. + required: false + - name: --read_extension3 + type: integer + description: | + Reads are extended upstream by bases from their 3' end. + required: false + - name: --read2pos + type: integer + description: | + Reduce reads to their 5' most base or 3' most base. Read counting is then performed based on the single base the read is reduced to. + required: false + choices: [3, 5] + + - name: Multi-mapping reads + arguments: + - name: --multi_mapping + alternatives: ["-M"] + type: boolean_true + description: | + Multi-mapping reads will also be counted. For a multi-mapping read, all its reported alignments will be counted. The 'NH' tag in BAM/SAM input is used to detect multi-mapping reads. + + - name: Fractional counting + arguments: + - name: --fraction + type: boolean_true + description: | + Assign fractional counts to features. This option must be used together with '--multi_mapping' or '--overlapping' or both. When '--multi_mapping' is specified, each reported alignment from a multi-mapping read (identified via 'NH' tag) will carry a fractional count of 1/x, instead of 1 (one), where x is the total number of alignments reported for the same read. When '--overlapping' is specified, each overlapping feature will receive a fractional count of 1/y, where y is the total number of features overlapping with the read. When both '--multi_mapping' and '--overlapping' are specified, each alignment will carry a fractional count of 1/(x*y). + + - name: Read filtering + arguments: + - name: --min_map_quality + alternatives: ["-Q"] + type: integer + description: | + The minimum mapping quality score a read must satisfy in order to be counted. For paired-end reads, at least one end should satisfy this criteria. 0 by default. + required: false + example: 0 + - name: --split_only + type: boolean_true + description: | + Count split alignments only (ie. alignments with CIGAR string containing 'N'). An example of split alignments is exon-spanning reads in RNA-seq data. + - name: --non_split_only + type: boolean_true + description: | + If specified, only non-split alignments (CIGAR strings do not contain letter 'N') will be counted. All the other alignments will be ignored. + - name: --primary + type: boolean_true + description: | + Count primary alignments only. Primary alignments are identified using bit 0x100 in SAM/BAM FLAG field. + - name: --ignore_dup + type: boolean_true + description: | + Ignore duplicate reads in read counting. Duplicate reads are identified using bit Ox400 in BAM/SAM FLAG field. The whole read pair is ignored if one of the reads is a duplicate read for paired end data. + + - name: Strandedness + arguments: + - name: --strand + alternatives: ["-s"] + type: integer + description: | + Perform strand-specific read counting. A single integer value (applied to all input files) should be provided. Possible values include: 0 (unstranded), 1 (stranded) and 2 (reversely stranded). Default value is 0 (ie. unstranded read counting carried out for all input files). + choices: [0, 1, 2] + example: 0 + required: false + + - name: Exon-exon junctions + arguments: + - name: --ref_fasta + alternatives: ["-G"] + type: file + description: | + Provide the name of a FASTA-format file that contains the reference sequences used in read mapping that produced the provided SAM/BAM files. + required: false + example: reference.fasta + + - name: Parameters specific to paired end reads + arguments: + - name: --paired + alternatives: ["-p"] + type: boolean_true + description: | + Specify that input data contain paired-end reads. To perform fragment counting (ie. counting read pairs), the '--countReadPairs' parameter should also be specified in addition to this parameter. + - name: --count_read_pairs + type: boolean_true + description: | + Count read pairs (fragments) instead of reads. This option is only applicable for paired-end reads. + - name: --both_aligned + alternatives: ["-B"] + type: boolean_true + description: | + Count read pairs (fragments) instead of reads. This option is only applicable for paired-end reads. + - name: --check_pe_dist + alternatives: ["-P"] + type: boolean_true + description: | + Check validity of paired-end distance when counting read pairs. Use '--min_length' and '--max_length' to set thresholds. + - name: --min_length + alternatives: ["-d"] + type: integer + description: | + Minimum fragment/template length, 50 by default. + required: false + example: 50 + - name: --max_length + alternatives: ["-D"] + type: integer + description: | + Maximum fragment/template length, 600 by default. + required: false + example: 600 + - name: --same_strand + alternatives: ["-C"] + type: boolean_true + description: | + Do not count read pairs that have their two ends mapping to different chromosomes or mapping to same chromosome but on different strands. + - name: --donotsort + type: boolean_true + description: | + Do not sort reads in BAM/SAM input. Note that reads from the same pair are required to be located next to each other in the input. + + - name: Read groups + arguments: + - name: --by_read_group + type: boolean_true + description: | + Assign reads by read group. "RG" tag is required to be present in the input BAM/SAM files. + + - name: Long reads + arguments: + - name: --long_reads + type: boolean_true + description: | + Count long reads such as Nanopore and PacBio reads. Long read counting can only run in one thread and only reads (not read-pairs) can be counted. There is no limitation on the number of 'M' operations allowed in a CIGAR string in long read counting. + + - name: Assignment results for each read + arguments: + - name: --detailed_results + type: file + direction: output + description: | + Directory to save the detailed assignment results. Use `--detailed_results_format` to determine the format of the detailed results. + example: detailed_results/ + required: false + - name: --detailed_results_format + alternatives: ["-R"] + type: string + description: | + Output detailed assignment results for each read or read-pair. Results are saved to a file that is in one of the following formats: CORE, SAM and BAM. See documentaiton for more info about these formats. + required: false + choices: [CORE, SAM, BAM] + + - name: Miscellaneous + arguments: + - name: --max_M_op + type: integer + description: | + Maximum number of 'M' operations allowed in a CIGAR string. 10 by default. Both 'X' and '=' are treated as 'M' and adjacent 'M' operations are merged in the CIGAR string. + required: false + example: 10 + - name: --verbose + type: boolean_true + description: | + Output verbose information for debugging, such as un-matched chromosome/contig names. + +resources: + - type: bash_script + path: script.sh + +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data + +engines: + - type: docker + image: quay.io/biocontainers/subread:2.0.6--he4a0461_0 + setup: + - type: docker + run: | + featureCounts -v 2>&1 | sed 's/featureCounts v\([0-9.]*\)/featureCounts: \1/' > /var/software_versions.txt +runners: + - type: executable + - type: nextflow \ No newline at end of file diff --git a/src/featurecounts/help.txt b/src/featurecounts/help.txt new file mode 100644 index 00000000..9ad33331 --- /dev/null +++ b/src/featurecounts/help.txt @@ -0,0 +1,242 @@ +```bash +featureCounts +``` + +Version 2.0.3 + +Usage: featureCounts [options] -a -o input_file1 [input_file2] ... + +## Mandatory arguments: + + -a Name of an annotation file. GTF/GFF format by default. See + -F option for more format information. Inbuilt annotations + (SAF format) is available in 'annotation' directory of the + package. Gzipped file is also accepted. + + -o Name of output file including read counts. A separate file + including summary statistics of counting results is also + included in the output ('.summary'). Both files + are in tab delimited format. + + input_file1 [input_file2] ... A list of SAM or BAM format files. They can be + either name or location sorted. If no files provided, + input is expected. Location-sorted paired-end reads + are automatically sorted by read names. + +## Optional arguments: +# Annotation + + -F Specify format of the provided annotation file. Acceptable + formats include 'GTF' (or compatible GFF format) and + 'SAF'. 'GTF' by default. For SAF format, please refer to + Users Guide. + + -t Specify feature type(s) in a GTF annotation. If multiple + types are provided, they should be separated by ',' with + no space in between. 'exon' by default. Rows in the + annotation with a matched feature will be extracted and + used for read mapping. + + -g Specify attribute type in GTF annotation. 'gene_id' by + default. Meta-features used for read counting will be + extracted from annotation using the provided value. + + --extraAttributes Extract extra attribute types from the provided GTF + annotation and include them in the counting output. These + attribute types will not be used to group features. If + more than one attribute type is provided they should be + separated by comma. + + -A Provide a chromosome name alias file to match chr names in + annotation with those in the reads. This should be a two- + column comma-delimited text file. Its first column should + include chr names in the annotation and its second column + should include chr names in the reads. Chr names are case + sensitive. No column header should be included in the + file. + +# Level of summarization + + -f Perform read counting at feature level (eg. counting + reads for exons rather than genes). + +# Overlap between reads and features + + -O Assign reads to all their overlapping meta-features (or + features if -f is specified). + + --minOverlap Minimum number of overlapping bases in a read that is + required for read assignment. 1 by default. Number of + overlapping bases is counted from both reads if paired + end. If a negative value is provided, then a gap of up + to specified size will be allowed between read and the + feature that the read is assigned to. + + --fracOverlap Minimum fraction of overlapping bases in a read that is + required for read assignment. Value should be within range + [0,1]. 0 by default. Number of overlapping bases is + counted from both reads if paired end. Both this option + and '--minOverlap' option need to be satisfied for read + assignment. + + --fracOverlapFeature Minimum fraction of overlapping bases in a + feature that is required for read assignment. Value + should be within range [0,1]. 0 by default. + + --largestOverlap Assign reads to a meta-feature/feature that has the + largest number of overlapping bases. + + --nonOverlap Maximum number of non-overlapping bases in a read (or a + read pair) that is allowed when being assigned to a + feature. No limit is set by default. + + --nonOverlapFeature Maximum number of non-overlapping bases in a feature + that is allowed in read assignment. No limit is set by + default. + + --readExtension5 Reads are extended upstream by bases from their + 5' end. + + --readExtension3 Reads are extended upstream by bases from their + 3' end. + + --read2pos <5:3> Reduce reads to their 5' most base or 3' most base. Read + counting is then performed based on the single base the + read is reduced to. + +# Multi-mapping reads + + -M Multi-mapping reads will also be counted. For a multi- + mapping read, all its reported alignments will be + counted. The 'NH' tag in BAM/SAM input is used to detect + multi-mapping reads. + +# Fractional counting + + --fraction Assign fractional counts to features. This option must + be used together with '-M' or '-O' or both. When '-M' is + specified, each reported alignment from a multi-mapping + read (identified via 'NH' tag) will carry a fractional + count of 1/x, instead of 1 (one), where x is the total + number of alignments reported for the same read. When '-O' + is specified, each overlapping feature will receive a + fractional count of 1/y, where y is the total number of + features overlapping with the read. When both '-M' and + '-O' are specified, each alignment will carry a fractional + count of 1/(x*y). + +# Read filtering + + -Q The minimum mapping quality score a read must satisfy in + order to be counted. For paired-end reads, at least one + end should satisfy this criteria. 0 by default. + + --splitOnly Count split alignments only (ie. alignments with CIGAR + string containing 'N'). An example of split alignments is + exon-spanning reads in RNA-seq data. + + --nonSplitOnly If specified, only non-split alignments (CIGAR strings do + not contain letter 'N') will be counted. All the other + alignments will be ignored. + + --primary Count primary alignments only. Primary alignments are + identified using bit 0x100 in SAM/BAM FLAG field. + + --ignoreDup Ignore duplicate reads in read counting. Duplicate reads + are identified using bit Ox400 in BAM/SAM FLAG field. The + whole read pair is ignored if one of the reads is a + duplicate read for paired end data. + +# Strandness + + -s Perform strand-specific read counting. A single integer + value (applied to all input files) or a string of comma- + separated values (applied to each corresponding input + file) should be provided. Possible values include: + 0 (unstranded), 1 (stranded) and 2 (reversely stranded). + Default value is 0 (ie. unstranded read counting carried + out for all input files). + +# Exon-exon junctions + + -J Count number of reads supporting each exon-exon junction. + Junctions were identified from those exon-spanning reads + in the input (containing 'N' in CIGAR string). Counting + results are saved to a file named '.jcounts' + + -G Provide the name of a FASTA-format file that contains the + reference sequences used in read mapping that produced the + provided SAM/BAM files. This optional argument can be used + with '-J' option to improve read counting for junctions. + +# Parameters specific to paired end reads + + -p Specify that input data contain paired-end reads. To + perform fragment counting (ie. counting read pairs), the + '--countReadPairs' parameter should also be specified in + addition to this parameter. + + --countReadPairs Count read pairs (fragments) instead of reads. This option + is only applicable for paired-end reads. + + -B Only count read pairs that have both ends aligned. + + -P Check validity of paired-end distance when counting read + pairs. Use -d and -D to set thresholds. + + -d Minimum fragment/template length, 50 by default. + + -D Maximum fragment/template length, 600 by default. + + -C Do not count read pairs that have their two ends mapping + to different chromosomes or mapping to same chromosome + but on different strands. + + --donotsort Do not sort reads in BAM/SAM input. Note that reads from + the same pair are required to be located next to each + other in the input. + +# Number of CPU threads + + -T Number of the threads. 1 by default. + +# Read groups + + --byReadGroup Assign reads by read group. "RG" tag is required to be + present in the input BAM/SAM files. + + +# Long reads + + -L Count long reads such as Nanopore and PacBio reads. Long + read counting can only run in one thread and only reads + (not read-pairs) can be counted. There is no limitation on + the number of 'M' operations allowed in a CIGAR string in + long read counting. + +# Assignment results for each read + + -R Output detailed assignment results for each read or read- + pair. Results are saved to a file that is in one of the + following formats: CORE, SAM and BAM. See Users Guide for + more info about these formats. + + --Rpath Specify a directory to save the detailed assignment + results. If unspecified, the directory where counting + results are saved is used. + +# Miscellaneous + + --tmpDir Directory under which intermediate files are saved (later + removed). By default, intermediate files will be saved to + the directory specified in '-o' argument. + + --maxMOp Maximum number of 'M' operations allowed in a CIGAR + string. 10 by default. Both 'X' and '=' are treated as 'M' + and adjacent 'M' operations are merged in the CIGAR + string. + + --verbose Output verbose information for debugging, such as un- + matched chromosome/contig names. + + -v Output version of the program. \ No newline at end of file diff --git a/src/featurecounts/script.sh b/src/featurecounts/script.sh new file mode 100644 index 00000000..2e54feb3 --- /dev/null +++ b/src/featurecounts/script.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +set -e + +## VIASH START +## VIASH END + +# create temporary directory +tmp_dir=$(mktemp -d -p "$meta_temp_dir" "${meta_functionality_name}_XXXXXX") +mkdir -p "$tmp_dir/temp" + +# create detailed_results directory if variable is set and directory does not exist +if [[ ! -z "$par_detailed_results" ]] && [[ ! -d "$par_detailed_results" ]]; then + mkdir -p "$par_detailed_results" +fi + +# replace comma with semicolon +par_feature_type=$(echo $par_feature_type | tr ',' ';') +par_extra_attributes=$(echo $par_extra_attributes | tr ',' ';') + +# unset flag variables +[[ "$par_feature_level" == "false" ]] && unset par_feature_level +[[ "$par_overlapping" == "false" ]] && unset par_overlapping +[[ "$par_largest_overlap" == "false" ]] && unset par_largest_overlap +[[ "$par_multi_mapping" == "false" ]] && unset par_multi_mapping +[[ "$par_fraction" == "false" ]] && unset par_fraction +[[ "$par_split_only" == "false" ]] && unset par_split_only +[[ "$par_non_split_only" == "false" ]] && unset par_non_split_only +[[ "$par_primary" == "false" ]] && unset par_primary +[[ "$par_ignore_dup" == "false" ]] && unset par_ignore_dup +[[ "$par_paired" == "false" ]] && unset par_paired +[[ "$par_count_read_pairs" == "false" ]] && unset par_count_read_pairs +[[ "$par_both_aligned" == "false" ]] && unset par_both_aligned +[[ "$par_check_pe_dist" == "false" ]] && unset par_check_pe_dist +[[ "$par_same_strand" == "false" ]] && unset par_same_strand +[[ "$par_donotsort" == "false" ]] && unset par_donotsort +[[ "$par_by_read_group" == "false" ]] && unset par_by_read_group +[[ "$par_long_reads" == "false" ]] && unset par_long_reads +[[ "$par_verbose" == "false" ]] && unset par_verbose + +IFS=";" read -ra input <<< $par_input + +featureCounts \ + ${par_format:+-F "${par_format}"} \ + ${par_feature_type:+-t "${par_feature_type}"} \ + ${par_attribute_type:+-g "${par_attribute_type}"} \ + ${par_extra_attributes:+--extraAttributes "${extra_attributes}"} \ + ${par_chrom_alias:+-A "${par_chrom_alias}"} \ + ${par_feature_level:+-f} \ + ${par_overlapping:+-O} \ + ${par_min_overlap:+--minOverlap "${par_min_overlap}"} \ + ${par_frac_overlap:+--fracOverlap "${par_frac_overlap}"} \ + ${par_frac_overlap_feature:+--fracOverlapFeature "${par_frac_overlap_feature}"} \ + ${par_largest_overlap:+--largestOverlap} \ + ${par_non_overlap:+--nonOverlap "${par_non_overlap}"} \ + ${par_non_overlap_feature:+--nonOverlapFeature "${par_non_overlap_feature}"} \ + ${par_read_extension5:+--readExtension5 "${par_read_extension5}"} \ + ${par_read_extension3:+--readExtension3 "${par_read_extension3}"} \ + ${par_read2pos:+--read2pos "${par_read2pos}"} \ + ${par_multi_mapping:+-M} \ + ${par_fraction:+--fraction} \ + ${par_min_map_quality:+-Q "${par_min_map_quality}"} \ + ${par_split_only:+--splitOnly} \ + ${par_non_split_only:+--nonSplitOnly} \ + ${par_primary:+--primary} \ + ${par_ignore_dup:+--ignoreDup} \ + ${par_strand:+-s "${par_strand}"} \ + ${par_junctions:+-J} \ + ${par_ref_fasta:+-G "${par_ref_fasta}"} \ + ${par_paired:+-p} \ + ${par_count_read_pairs:+--countReadPairs} \ + ${par_both_aligned:+-B} \ + ${par_check_pe_dist:+-P} \ + ${par_min_length:+-d "${par_min_length}"} \ + ${par_max_length:+-D "${par_max_length}"} \ + ${par_same_strand:+-C} \ + ${par_donotsort:+--donotsort} \ + ${par_by_read_group:+--byReadGroup} \ + ${par_long_reads:+-L} \ + ${par_detailed_results:+--Rpath "${par_detailed_results}"} \ + ${par_detailed_results_format:+-R "${par_detailed_results_format}"} \ + ${par_max_M_op:+--maxMOp "${par_max_M_op}"} \ + ${par_verbose:+--verbose} \ + ${meta_cpus:+-T "${meta_cpus}"} \ + --tmpDir "$tmp_dir/temp" \ + -a "$par_annotation" \ + -o "$tmp_dir/output.txt" \ + "${input[*]}" + +[[ ! -z "$par_counts" ]] && mv "$tmp_dir/output.txt" "$par_counts" +[[ ! -z "$par_summary" ]] && mv "$tmp_dir/output.txt.summary" "$par_summary" +if [[ ! -z "$par_junctions" ]] && [[ -e "$tmp_dir/output.txt.jcounts" ]]; then + mv "$tmp_dir/output.txt.jcounts" "$par_junctions" +fi diff --git a/src/featurecounts/test.sh b/src/featurecounts/test.sh new file mode 100644 index 00000000..3349d016 --- /dev/null +++ b/src/featurecounts/test.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +set -e + +dir_in="$meta_resources_dir/test_data" + +echo "> Run featureCounts (with junctions)" +"$meta_executable" \ + --input "$dir_in/a.bam" \ + --annotation "$dir_in/annotation.gtf" \ + --counts "features.tsv" \ + --summary "summary.tsv" \ + --junctions "junction_counts.txt" \ + --ref_fasta "$dir_in/genome.fasta" \ + --overlapping \ + --frac_overlap 0.2 \ + --paired \ + --strand 0 \ + --detailed_results detailed_results \ + --detailed_results_format SAM + +echo ">> Checking output" +[ ! -f "features.tsv" ] && echo "Output file features.tsv does not exist" && exit 1 +[ ! -f "summary.tsv" ] && echo "Output file summary.tsv does not exist" && exit 1 +[ ! -f "junction_counts.txt" ] && echo "Output file junction_counts.txt does not exist" && exit 1 +[ ! -d "detailed_results" ] && echo "Output directory detailed_results does not exist" && exit 1 +[ ! -f "detailed_results/a.bam.featureCounts.sam" ] && echo "Output file detailed_results/a.bam.featureCounts.sam does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "features.tsv" ] && echo "Output file features.tsv is empty" && exit 1 +[ ! -s "summary.tsv" ] && echo "Output file summary.tsv is empty" && exit 1 +[ ! -s "junction_counts.txt" ] && echo "Output file junction_counts.txt is empty" && exit 1 +[ ! -s "detailed_results/a.bam.featureCounts.sam" ] && echo "Output file detailed_results/a.bam.featureCounts.sam is empty" && exit 1 + +echo "> Run featureCounts (without junctions)" +"$meta_executable" \ + --input "$dir_in/a.bam" \ + --annotation "$dir_in/annotation.gtf" \ + --counts "features.tsv" \ + --summary "summary.tsv" \ + --overlapping \ + --frac_overlap 0.2 \ + --paired \ + --strand 0 \ + --detailed_results detailed_results \ + --detailed_results_format SAM + +echo ">> Checking output" +[ ! -f "features.tsv" ] && echo "Output file features.tsv does not exist" && exit 1 +[ ! -f "summary.tsv" ] && echo "Output file summary.tsv does not exist" && exit 1 +[ ! -d "detailed_results" ] && echo "Output directory detailed_results does not exist" && exit 1 +[ ! -f "detailed_results/a.bam.featureCounts.sam" ] && echo "Output file detailed_results/a.bam.featureCounts.sam does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "features.tsv" ] && echo "Output file features.tsv is empty" && exit 1 +[ ! -s "summary.tsv" ] && echo "Output file summary.tsv is empty" && exit 1 +[ ! -s "detailed_results/a.bam.featureCounts.sam" ] && echo "Output file detailed_results/a.bam.featureCounts.sam is empty" && exit 1 + +echo "> Test successful" \ No newline at end of file diff --git a/src/featurecounts/test_data/a.bam b/src/featurecounts/test_data/a.bam new file mode 100644 index 00000000..57511ab3 Binary files /dev/null and b/src/featurecounts/test_data/a.bam differ diff --git a/src/featurecounts/test_data/annotation.gtf b/src/featurecounts/test_data/annotation.gtf new file mode 100644 index 00000000..22b3a67a --- /dev/null +++ b/src/featurecounts/test_data/annotation.gtf @@ -0,0 +1,6 @@ +1 havana gene 1 80 . + . gene_id "ENSG00000000000"; gene_version "5"; gene_name "A"; gene_source "havana"; gene_biotype "gene"; +1 havana transcript 1 80 . + . gene_id "ENSG00000000000"; gene_version "5"; transcript_id "ENST00000000000"; transcript_version "2"; gene_name "A"; gene_source "havana"; gene_biotype "gene"; transcript_name "A-202"; transcript_source "havana"; transcript_biotype "processed_transcript"; tag "basic"; transcript_support_level "1"; +1 havana exon 1 80 . + . gene_id "ENSG00000000000"; gene_version "5"; transcript_id "ENST00000000000"; transcript_version "2"; exon_number "1"; gene_name "A"; gene_source "havana"; gene_biotype "gene"; transcript_name "A-202"; transcript_source "havana"; transcript_biotype "processed_transcript"; exon_id "ENSE00000000000"; exon_version "1"; tag "basic"; transcript_support_level "1"; +2 havana gene 1 80 . + . gene_id "ENSG00000000001"; gene_version "5"; gene_name "B"; gene_source "havana"; gene_biotype "gene"; +2 havana transcript 1 80 . + . gene_id "ENSG00000000001"; gene_version "5"; transcript_id "ENST00000000001"; transcript_version "2"; gene_name "B"; gene_source "havana"; gene_biotype "gene"; transcript_name "B-202"; transcript_source "havana"; transcript_biotype "processed_transcript"; tag "basic"; transcript_support_level "1"; +2 havana exon 1 80 . + . gene_id "ENSG00000000001"; gene_version "5"; transcript_id "ENST00000000001"; transcript_version "2"; exon_number "1"; gene_name "B"; gene_source "havana"; gene_biotype "gene"; transcript_name "B-202"; transcript_source "havana"; transcript_biotype "processed_transcript"; exon_id "ENSE00000000001"; exon_version "1"; tag "basic"; transcript_support_level "1"; diff --git a/src/featurecounts/test_data/genome.fasta b/src/featurecounts/test_data/genome.fasta new file mode 100644 index 00000000..91ea0d37 --- /dev/null +++ b/src/featurecounts/test_data/genome.fasta @@ -0,0 +1,4 @@ +>1 +GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG +>2 +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA diff --git a/src/featurecounts/test_data/script.sh b/src/featurecounts/test_data/script.sh new file mode 100644 index 00000000..28472b0e --- /dev/null +++ b/src/featurecounts/test_data/script.sh @@ -0,0 +1,9 @@ +# featureCounts test data + +# Test data was obtained from https://github.com/snakemake/snakemake-wrappers/tree/master/bio/subread/featurecounts/test + +if [ ! -d /tmp/snakemake-wrappers ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers /tmp/snakemake-wrappers +fi + +cp -r /tmp/snakemake-wrappers/bio/subread/featurecounts/test/* src/subread/featurecounts/test_data \ No newline at end of file diff --git a/src/gffread/config.vsh.yaml b/src/gffread/config.vsh.yaml new file mode 100644 index 00000000..d2c41a87 --- /dev/null +++ b/src/gffread/config.vsh.yaml @@ -0,0 +1,397 @@ +name: gffread +description: Validate, filter, convert and perform various other operations on GFF files. +keywords: [gff, conversion, validation, filtering] +links: + homepage: https://ccb.jhu.edu/software/stringtie/gff.shtml#gffread + documentation: https://ccb.jhu.edu/software/stringtie/gff.shtml#gffread + repository: https://github.com/gpertea/gffread +references: + doi: 10.12688/f1000research.23297.2 +license: MIT +requirements: + commands: [ gffread ] +argument_groups: + - name: Inputs + arguments: + - name: --input + type: file + direction: input + description: | + A reference file in either the GFF3, GFF2 or GTF format. + required: true + example: annotation.gff + - name: --chr_mapping + alternatives: -m + type: file + direction: input + description: | + is a name mapping table for converting reference sequence names, + having this 2-column format: . + - name: --seq_info + alternatives: -s + type: file + direction: input + description: | + is a tab-delimited file providing this info for each of the mapped + sequences: (useful for --description option with + mRNA/EST/protein mappings). + - name: --genome + alternatives: -g + type: file + description: | + Full path to a multi-fasta file with the genomic sequences for all input mappings, + OR a directory with single-fasta files (one per genomic sequence, with file names + matching sequence names). + example: genome.fa + - name: Outputs + arguments: + - name: --outfile + alternatives: -o + type: file + direction: output + required: true + description: | + Write the output records into . + default: output.gff + - name: --force_exons + type: boolean_true + description: | + Make sure that the lowest level GFF features are considered "exon" features. + - name: --gene2exon + type: boolean_true + description: | + For single-line genes not parenting any transcripts, add an exon feature spanning + the entire gene (treat it as a transcript). + - name: --t_adopt + type: boolean_true + description: | + Try to find a parent gene overlapping/containing a transcript that does not have + any explicit gene Parent. + - name: --decode + alternatives: -D + type: boolean_true + description: | + Decode url encoded characters within attributes. + - name: --merge_exons + alternatives: -Z + type: boolean_true + description: | + Merge very close exons into a single exon (when intron size<4). + - name: --junctions + alternatives: -j + type: boolean_true + description: | + Output the junctions and the corresponding transcripts. + - name: --spliced_exons + alternatives: -w + type: file + direction: output + must_exist: false + description: | + Write a fasta file with spliced exons for each transcript. + example: exons.fa + - name: --w_add + type: integer + description: | + For the --spliced_exons option, extract additional bases both upstream and + downstream of the transcript boundaries. + - name: --w_nocds + type: boolean_true + description: | + For --spliced_exons, disable the output of CDS info in the FASTA file. + - name: --spliced_cds + alternatives: -x + type: file + must_exist: false + example: cds.fa + description: | + Write a fasta file with spliced CDS for each GFF transcript. + - name: --tr_cds + alternatives: -y + type: file + must_exist: false + example: tr_cds.fa + description: | + Write a protein fasta file with the translation of CDS for each record. + - name: --w_coords + alternatives: -W + type: boolean_true + description: | + For --spliced_exons, --spliced_cds and -tr_cds options, write in the FASTA defline + all the exon coordinates projected onto the spliced sequence. + - name: --stop_dot + alternatives: -S + type: boolean_true + description: | + For --tr_cds option, use '*' instead of '.' as stop codon translation. + - name: --id_version + alternatives: -L + type: boolean_true + description: | + Ensembl GTF to GFF3 conversion, adds version to IDs. + - name: --trackname + alternatives: -t + type: string + description: | + Use in the 2nd column of each GFF/GTF output line. + - name: --gtf_output + alternatives: -T + type: boolean_true + description: | + Main output will be GTF instead of GFF3. + - name: --bed + type: boolean_true + description: | + Output records in BED format instead of default GFF3. + - name: --tlf + type: boolean_true + description: | + Output "transcript line format" which is like GFF but with exons and CDS related + features stored as GFF attributes in the transcript feature line, like this: + exoncount=N;exons=;CDSphase=;CDS= + is a comma-delimited list of exon_start-exon_end coordinates; + is CDS_start:CDS_end coordinates or a list like . + - name: --table + type: string + multiple: true + multiple_sep: "," + description: | + Output a simple tab delimited format instead of GFF, with columns having the values + of GFF attributes given in ; special pseudo-attributes (prefixed by @) are + recognized: + @id, @geneid, @chr, @start, @end, @strand, @numexons, @exons, @cds, @covlen, @cdslen + If any of --spliced_exons/--tr_cds/--spliced_cds FASTA output files are enabled, the + same fields (excluding @id) are appended to the definition line of corresponding FASTA + records. + - name: --expose_dups + type: boolean_true + alternatives: [-E, -v] + description: | + Expose (warn about) duplicate transcript IDs and other potential problems with the + given GFF/GTF records. + - name: Options + arguments: + - name: --ids + type: file + description: | + Discard records/transcripts if their IDs are not listed in . + - name: --nids + type: file + description: | + Discard records/transcripts if their IDs are listed in . + - name: --maxintron + alternatives: -i + type: integer + description: | + Discard transcripts having an intron larger than . + - name: --minlen + alternatives: -l + type: integer + description: | + Discard transcripts shorter than bases. + - name: --range + alternatives: -r + type: string + description: | + Only show transcripts overlapping coordinate range .. (on chromosome/contig + , strand if provided). + - name: --strict_range + alternatives: -R + type: boolean_true + description: | + For --range option, discard all transcripts that are not fully contained within the given + range. + - name: --jmatch + type: string + description: | + Only output transcripts matching the given junction. + - name: --no_single_exon + alternatives: -U + type: boolean_true + description: | + Discard single-exon transcripts. + - name: --coding + alternatives: -C + type: boolean_true + description: | + Coding only: discard mRNAs that have no CDS features. + - name: --nc + type: boolean_true + description: | + Non-coding only: discard mRNAs that have CDS features. + - name: --ignore_locus + type: boolean_true + description: | + Discard locus features and attributes found in the input. + - name: --description + alternatives: -A + type: boolean_true + description: | + Use the description field from and add it as the value for a 'descr' + attribute to the GFF record. + + - name: Sorting + arguments: + - name: --sort_alpha + type: boolean_true + description: | + Chromosomes (reference sequences) are sorted alphabetically. + - name: --sort_by + type: file + must_exist: true + description: | + Sort the reference sequences by the order in which their names are given in the + file. + - name: Misc options + arguments: + - name: --keep_attrs + alternatives: -F + type: boolean_true + description: | + Keep all GFF attributes (for non-exon features). + - name: --keep_exon_attrs + type: boolean_true + description: | + For -F option, do not attempt to reduce redundant exon/CDS attributes. + - name: --no_exon_attrs + alternatives: -G + type: boolean_true + description: | + Do not keep exon attributes, move them to the transcript feature (for GFF3 output). + - name: --attrs + type: string + description: | + Only output the GTF/GFF attributes listed in which is a comma delimited + list of attribute names to. + - name: --keep_genes + type: boolean_true + description: | + In transcript-only mode (default), also preserve gene records. + - name: --keep_comments + type: boolean_true + description: | + For GFF3 input/output, try to preserve comments. + - name: --process_other + alternatives: -O + type: boolean_true + description: | + process other non-transcript GFF records (by default non-transcript records are ignored). + - name: --rm_stop_codons + alternatives: -V + type: boolean_true + description: | + Discard any mRNAs with CDS having in-frame stop codons (requires --genome). + - name: --adj_cds_start + alternatives: -H + type: boolean_true + description: | + For --rm_stop_codons option, check and adjust the starting CDS phase if the original phase + leads to a translation with an in-frame stop codon. + - name: --opposite_strand + alternatives: -B + type: boolean_true + description: | + For -V option, single-exon transcripts are also checked on the opposite strand (requires + --genome). + - name: --coding_status + alternatives: -P + type: boolean_true + description: | + Add transcript level GFF attributes about the coding status of each transcript, including + partialness or in-frame stop codons (requires --genome). + - name: --add_hasCDS + type: boolean_true + description: | + Add a "hasCDS" attribute with value "true" for transcripts that have CDS features. + - name: --adj_stop + type: boolean_true + description: | + Stop codon adjustment: enables --coding_status and performs automatic adjustment of the CDS stop + coordinate if premature or downstream. + - name: --rm_noncanon + alternatives: -N + type: boolean_true + description: | + Discard multi-exon mRNAs that have any intron with a non-canonical splice site consensus + (i.e. not GT-AG, GC-AG or AT-AC). + - name: --complete_cds + alternatives: -J + type: boolean_true + description: | + Discard any mRNAs that either lack initial START codon or the terminal STOP codon, or + have an in-frame stop codon (i.e. only print mRNAs with a complete CDS). + - name: --no_pseudo + type: boolean_true + description: | + Filter out records matching the 'pseudo' keyword. + - name: --in_bed + type: boolean_true + description: | + Input should be parsed as BED format (automatic if the input filename ends with .bed*). + - name: --in_tlf + type: boolean_true + description: | + Input GFF-like one-line-per-transcript format without exon/CDS features (see --tlf option + below); automatic if the input filename ends with .tlf). + - name: --stream + type: boolean_true + description: | + Fast processing of input GFF/BED transcripts as they are received (no sorting, exons must + be grouped by transcript in the input data). + + - name: Clustering + arguments: + - name: --merge + alternatives: -M + type: boolean_true + description: | + Cluster the input transcripts into loci, discarding "redundant" transcripts (those with + the same exact introns and fully contained or equal boundaries). + - name: --dupinfo + alternatives: -d + type: file + description: | + For --merge option, write duplication info to file . + - name: --cluster_only + type: boolean_true + description: | + Same as --merge but without discarding any of the "duplicate" transcripts, only create + "locus" features. + - name: --rm_redundant + alternatives: -K + type: boolean_true + description: | + For --merge option: also discard as redundant the shorter, fully contained transcripts (intron + chains matching a part of the container). + - name: --no_boundary + alternatives: -Q + type: boolean_true + description: | + For --merge option, no longer require boundary containment when assessing redundancy (can be + combined with --rm_redundant); only introns have to match for multi-exon transcripts, and >=80% + overlap for single-exon transcripts. + - name: --no_overlap + alternatives: -Y + type: boolean_true + description: | + For --merge option, enforce --no_boundary but also discard overlapping single-exon transcripts, + even on the opposite strand (can be combined with --rm_redudant). + +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: +- type: docker + image: quay.io/biocontainers/gffread:0.12.7--hdcf5f25_3 + setup: + - type: docker + run: | + echo "gffread: \"$(gffread --version 2>&1)\"" > /var/software_versions.txt +runners: +- type: executable +- type: nextflow \ No newline at end of file diff --git a/src/gffread/help.txt b/src/gffread/help.txt new file mode 100644 index 00000000..f9991c71 --- /dev/null +++ b/src/gffread/help.txt @@ -0,0 +1,140 @@ +```sh +gffread --help +``` + +gffread v0.12.7. Usage: +gffread [-g | ] [-s ] + [-o ] [-t ] [-r []:- [-R]] + [--jmatch :-] [--no-pseudo] + [-CTVNJMKQAFPGUBHZWTOLE] [-w ] [-x ] [-y ] + [-j ][--ids | --nids ] [--attrs ] [-i ] + [--stream] [--bed | --gtf | --tlf] [--table ] [--sort-by ] + [] + + Filter, convert or cluster GFF/GTF/BED records, extract the sequence of + transcripts (exon or CDS) and more. + By default (i.e. without -O) only transcripts are processed, discarding any + other non-transcript features. Default output is a simplified GFF3 with only + the basic attributes. + +Options: + --ids discard records/transcripts if their IDs are not listed in + --nids discard records/transcripts if their IDs are listed in + -i discard transcripts having an intron larger than + -l discard transcripts shorter than bases + -r only show transcripts overlapping coordinate range .. + (on chromosome/contig , strand if provided) + -R for -r option, discard all transcripts that are not fully + contained within the given range + --jmatch only output transcripts matching the given junction + -U discard single-exon transcripts + -C coding only: discard mRNAs that have no CDS features + --nc non-coding only: discard mRNAs that have CDS features + --ignore-locus : discard locus features and attributes found in the input + -A use the description field from and add it + as the value for a 'descr' attribute to the GFF record + -s is a tab-delimited file providing this info + for each of the mapped sequences: + + (useful for -A option with mRNA/EST/protein mappings) +Sorting: (by default, chromosomes are kept in the order they were found) + --sort-alpha : chromosomes (reference sequences) are sorted alphabetically + --sort-by : sort the reference sequences by the order in which their + names are given in the file +Misc options: + -F keep all GFF attributes (for non-exon features) + --keep-exon-attrs : for -F option, do not attempt to reduce redundant + exon/CDS attributes + -G do not keep exon attributes, move them to the transcript feature + (for GFF3 output) + --attrs only output the GTF/GFF attributes listed in + which is a comma delimited list of attribute names to + --keep-genes : in transcript-only mode (default), also preserve gene records + --keep-comments: for GFF3 input/output, try to preserve comments + -O process other non-transcript GFF records (by default non-transcript + records are ignored) + -V discard any mRNAs with CDS having in-frame stop codons (requires -g) + -H for -V option, check and adjust the starting CDS phase + if the original phase leads to a translation with an + in-frame stop codon + -B for -V option, single-exon transcripts are also checked on the + opposite strand (requires -g) + -P add transcript level GFF attributes about the coding status of each + transcript, including partialness or in-frame stop codons (requires -g) + --add-hasCDS : add a "hasCDS" attribute with value "true" for transcripts + that have CDS features + --adj-stop stop codon adjustment: enables -P and performs automatic + adjustment of the CDS stop coordinate if premature or downstream + -N discard multi-exon mRNAs that have any intron with a non-canonical + splice site consensus (i.e. not GT-AG, GC-AG or AT-AC) + -J discard any mRNAs that either lack initial START codon + or the terminal STOP codon, or have an in-frame stop codon + (i.e. only print mRNAs with a complete CDS) + --no-pseudo: filter out records matching the 'pseudo' keyword + --in-bed: input should be parsed as BED format (automatic if the input + filename ends with .bed*) + --in-tlf: input GFF-like one-line-per-transcript format without exon/CDS + features (see --tlf option below); automatic if the input + filename ends with .tlf) + --stream: fast processing of input GFF/BED transcripts as they are received + ((no sorting, exons must be grouped by transcript in the input data) +Clustering: + -M/--merge : cluster the input transcripts into loci, discarding + "redundant" transcripts (those with the same exact introns + and fully contained or equal boundaries) + -d : for -M option, write duplication info to file + --cluster-only: same as -M/--merge but without discarding any of the + "duplicate" transcripts, only create "locus" features + -K for -M option: also discard as redundant the shorter, fully contained + transcripts (intron chains matching a part of the container) + -Q for -M option, no longer require boundary containment when assessing + redundancy (can be combined with -K); only introns have to match for + multi-exon transcripts, and >=80% overlap for single-exon transcripts + -Y for -M option, enforce -Q but also discard overlapping single-exon + transcripts, even on the opposite strand (can be combined with -K) +Output options: + --force-exons: make sure that the lowest level GFF features are considered + "exon" features + --gene2exon: for single-line genes not parenting any transcripts, add an + exon feature spanning the entire gene (treat it as a transcript) + --t-adopt: try to find a parent gene overlapping/containing a transcript + that does not have any explicit gene Parent + -D decode url encoded characters within attributes + -Z merge very close exons into a single exon (when intron size<4) + -g full path to a multi-fasta file with the genomic sequences + for all input mappings, OR a directory with single-fasta files + (one per genomic sequence, with file names matching sequence names) + -j output the junctions and the corresponding transcripts + -w write a fasta file with spliced exons for each transcript + --w-add for the -w option, extract additional bases + both upstream and downstream of the transcript boundaries + --w-nocds for -w, disable the output of CDS info in the FASTA file + -x write a fasta file with spliced CDS for each GFF transcript + -y write a protein fasta file with the translation of CDS for each record + -W for -w, -x and -y options, write in the FASTA defline all the exon + coordinates projected onto the spliced sequence; + -S for -y option, use '*' instead of '.' as stop codon translation + -L Ensembl GTF to GFF3 conversion, adds version to IDs + -m is a name mapping table for converting reference + sequence names, having this 2-column format: + + -t use in the 2nd column of each GFF/GTF output line + -o write the output records into instead of stdout + -T main output will be GTF instead of GFF3 + --bed output records in BED format instead of default GFF3 + --tlf output "transcript line format" which is like GFF + but with exons and CDS related features stored as GFF + attributes in the transcript feature line, like this: + exoncount=N;exons=;CDSphase=;CDS= + is a comma-delimited list of exon_start-exon_end coordinates; + is CDS_start:CDS_end coordinates or a list like + --table output a simple tab delimited format instead of GFF, with columns + having the values of GFF attributes given in ; special + pseudo-attributes (prefixed by @) are recognized: + @id, @geneid, @chr, @start, @end, @strand, @numexons, @exons, + @cds, @covlen, @cdslen + If any of -w/-y/-x FASTA output files are enabled, the same fields + (excluding @id) are appended to the definition line of corresponding + FASTA records + -v,-E expose (warn about) duplicate transcript IDs and other potential + problems with the given GFF/GTF records \ No newline at end of file diff --git a/src/gffread/script.sh b/src/gffread/script.sh new file mode 100644 index 00000000..9c4a2b8f --- /dev/null +++ b/src/gffread/script.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +# unset flags +[[ "$par_coding" == "false" ]] && unset par_coding +[[ "$par_strict_range" == "false" ]] && unset par_strict_range +[[ "$par_no_single_exon" == "false" ]] && unset par_no_single_exon +[[ "$par_no_exon_attrs" == "false" ]] && unset par_no_exon_attrs +[[ "$par_nc" == "false" ]] && unset par_nc +[[ "$par_ignore_locus" == "false" ]] && unset par_ignore_locus +[[ "$par_description" == "false" ]] && unset par_description +[[ "$par_sort_alpha" == "false" ]] && unset par_sort_alpha +[[ "$par_keep_genes" == "false" ]] && unset par_keep_genes +[[ "$par_keep_attrs" == "false" ]] && unset par_keep_attrs +[[ "$par_keep_exon_attrs" == "false" ]] && unset par_keep_exon_attrs +[[ "$par_keep_comments" == "false" ]] && unset par_keep_comments +[[ "$par_process_other" == "false" ]] && unset par_process_other +[[ "$par_rm_stop_codons" == "false" ]] && unset par_rm_stop_codons +[[ "$par_adj_cds_start" == "false" ]] && unset par_adj_cds_start +[[ "$par_opposite_strand" == "false" ]] && unset par_opposite_strand +[[ "$par_coding_status" == "false" ]] && unset par_coding_status +[[ "$par_add_hasCDS" == "false" ]] && unset par_add_hasCDS +[[ "$par_adj_stop" == "false" ]] && unset par_adj_stop +[[ "$par_rm_noncanon" == "false" ]] && unset par_rm_noncanon +[[ "$par_complete_cds" == "false" ]] && unset par_complete_cds +[[ "$par_no_pseudo" == "false" ]] && unset par_no_pseudo +[[ "$par_in_bed" == "false" ]] && unset par_in_bed +[[ "$par_in_tlf" == "false" ]] && unset par_in_tlf +[[ "$par_stream" == "false" ]] && unset par_stream +[[ "$par_merge" == "false" ]] && unset par_merge +[[ "$par_rm_redundant" == "false" ]] && unset par_rm_redundant +[[ "$par_no_boundary" == "false" ]] && unset par_no_boundary +[[ "$par_no_overlap" == "false" ]] && unset par_no_overlap +[[ "$par_force_exons" == "false" ]] && unset par_force_exons +[[ "$par_gene2exon" == "false" ]] && unset par_gene2exon +[[ "$par_t_adopt" == "false" ]] && unset par_t_adopt +[[ "$par_decode" == "false" ]] && unset par_decode +[[ "$par_merge_exons" == "false" ]] && unset par_merge_exons +[[ "$par_junctions" == "false" ]] && unset par_junctions +[[ "$par_w_nocds" == "false" ]] && unset par_w_nocds +[[ "$par_tr_cds" == "false" ]] && unset par_tr_cds +[[ "$par_w_coords" == "false" ]] && unset par_w_coords +[[ "$par_stop_dot" == "false" ]] && unset par_stop_dot +[[ "$par_id_version" == "false" ]] && unset par_id_version +[[ "$par_gtf_output" == "false" ]] && unset par_gtf_output +[[ "$par_bed" == "false" ]] && unset par_bed +[[ "$par_tlf" == "false" ]] && unset par_tlf +[[ "$par_expose_dups" == "false" ]] && unset par_expose_dups +[[ "$par_cluster_only" == "false" ]] && unset par_cluster_only + + +$(which gffread) \ + "$par_input" \ + ${par_chr_mapping:+-m "$par_chr_mapping"} \ + ${par_seq_info:+-s "$par_seq_info"} \ + -o "$par_outfile" \ + ${par_force_exons:+--force-exons} \ + ${par_gene2exon:+--gene2exon} \ + ${par_t_adopt:+--t-adopt} \ + ${par_decode:+-D} \ + ${par_merge_exons:+-Z} \ + ${par_genome:+-g "$par_genome"} \ + ${par_junctions:+-j} \ + ${par_spliced_exons:+-w "$par_spliced_exons"} \ + ${par_w_add:+--w-add "$par_w_add"} \ + ${par_w_nocds:+--w-nocds} \ + ${par_spliced_cds:+-x "$par_spliced_cds"} \ + ${par_tr_cds:+-y "$par_tr_cds"} \ + ${par_w_coords:+-W} \ + ${par_stop_dot:+-S} \ + ${par_id_version:+-L} \ + ${par_trackname:+-t "$par_trackname"} \ + ${par_gtf_output:+-T} \ + ${par_bed:+--bed} \ + ${par_tlf:+--tlf} \ + ${par_table:+--table "$par_table"} \ + ${par_expose_dups:+-E} \ + ${par_ids:+--ids "$par_ids"} \ + ${par_nids:+--nids "$par_nids"} \ + ${par_maxintron:+-i "$par_maxintron"} \ + ${par_minlen:+-l "$par_minlen"} \ + ${par_range:+-r "$par_range"} \ + ${par_strict_range:+-R} \ + ${par_jmatch:+--jmatch "$par_jmatch"} \ + ${par_no_single_exon:+-U} \ + ${par_coding:+-C} \ + ${par_nc:+--nc} \ + ${par_ignore_locus:+--ignore-locus} \ + ${par_description:+-A} \ + ${par_sort_alpha:+--sort-alpha} \ + ${par_sort_by:+--sort-by "$par_sort_by"} \ + ${par_keep_attrs:+-F} \ + ${par_keep_exon_attrs:+--keep-exon-attrs} \ + ${par_no_exon_attrs:+-G} \ + ${par_attrs:+--attrs "$par_attrs"} \ + ${par_keep_genes:+--keep-genes} \ + ${par_keep_comments:+--keep-comments} \ + ${par_process_other:+-O} \ + ${par_rm_stop_codons:+-V} \ + ${par_adj_cds_start:+-H} \ + ${par_opposite_strand:+-B} \ + ${par_coding_status:+-P} \ + ${par_add_hasCDS:+--add-hasCDS} \ + ${par_adj_stop:+--adj-stop} \ + ${par_rm_noncanon:+-N} \ + ${par_complete_cds:+-J} \ + ${par_no_pseudo:+--no-pseudo} \ + ${par_in_bed:+--in-bed} \ + ${par_in_tlf:+--in-tlf} \ + ${par_stream:+--stream} \ + ${par_merge:+-M} \ + ${par_dupinfo:+-d "$par_dupinfo"} \ + ${par_cluster_only:+--cluster-only} \ + ${par_rm_redundant:+-K} \ + ${par_no_boundary:+-Q} \ + ${par_no_overlap:+-Y} + diff --git a/src/gffread/test.sh b/src/gffread/test.sh new file mode 100755 index 00000000..326fce50 --- /dev/null +++ b/src/gffread/test.sh @@ -0,0 +1,111 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +set -e + +test_output_dir="${meta_resources_dir}/test_data/test_output" +test_dir="${meta_resources_dir}/test_data" +expected_output_dir="${meta_resources_dir}/test_data/output" + +mkdir -p "$test_output_dir" + + +################################################################################ + +echo "> Test 1 - Read annotation file, output GFF" + +"$meta_executable" \ + --expose_dups \ + --outfile "$test_output_dir/ann_simple.gff" \ + --input "$test_dir/sequence.gff3" + + +echo ">> Check if output exists" +[ ! -f "$test_output_dir/ann_simple.gff" ] \ + && echo "Output file test_output/ann_simple.gff does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "$test_output_dir/ann_simple.gff" ] \ + && echo "Output file test_output/ann_simple.gff is empty" && exit 1 + +echo ">> Compare output to expected output" + +# compare file expect lines starting with "#" +diff <(grep -v "^#" "$expected_output_dir/ann_simple.gff") \ + <(grep -v "^#" "$test_output_dir/ann_simple.gff") || \ + (echo "Output file ann_simple.gff does not match expected output" && exit 1) + +################################################################################ + +echo "> Test 2 - Read annotation file, output GTF" + +"$meta_executable" \ + --gtf_output \ + --outfile "$test_output_dir/annotation.gtf" \ + --input "$test_dir/sequence.gff3" + +echo ">> Check if output exists" +[ ! -f "$test_output_dir/annotation.gtf" ] \ + && echo "Output file test_output/annotation.gtf does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "$test_output_dir/annotation.gtf" ] \ + && echo "Output file test_output/annotation.gtf is empty" && exit 1 + +echo ">> Compare output to expected output" +diff "$expected_output_dir/annotation.gtf" "$test_output_dir/annotation.gtf" || \ + (echo "Output file annotation.gtf does not match expected output" && exit 1) + +################################################################################ + +echo "> Test 3 - Generate fasta file from annotation file" + + +"$meta_executable" \ + --genome "$test_dir/sequence.fasta" \ + --spliced_exons "$test_output_dir/transcripts.fa" \ + --outfile "$test_output_dir/output.gff" \ + --input "$test_dir/sequence.gff3" + +echo ">> Check if output exists" +[ ! -f "$test_output_dir/transcripts.fa" ] \ + && echo "Output file transcripts.fa does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "$test_output_dir/transcripts.fa" ] \ + && echo "Output file transcripts.fa is empty" && exit 1 + +echo ">> Compare output to expected output" +diff "$expected_output_dir/transcripts.fa" "$test_output_dir/transcripts.fa" || \ + (echo "Output file transcripts.fa does not match expected output" && exit 1) + +################################################################################ + +echo "> Test 4 - Generate table from GFF annotation file" + +"$meta_executable" \ + --table @id,@chr,@start,@end,@strand,@exons,Name,gene,product \ + --outfile "$test_output_dir/annotation.tbl" \ + --input "$test_dir/sequence.gff3" + +echo ">> Check if output exists" +[ ! -f "$test_output_dir/annotation.tbl" ] \ + && echo "Output file test_output/annotation.tbl does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "$test_output_dir/annotation.tbl" ] \ + && echo "Output file test_output/annotation.tbl is empty" && exit 1 + +echo ">> Compare output to expected output" +diff "$expected_output_dir/annotation.tbl" "$test_output_dir/annotation.tbl" || \ + (echo "Output file annotation.tbl does not match expected output" && exit 1) + +################################################################################ + +rm -r "$test_output_dir" + +echo "> All tests successful" + +exit 0 diff --git a/src/gffread/test_data/README.md b/src/gffread/test_data/README.md new file mode 100644 index 00000000..f1638b95 --- /dev/null +++ b/src/gffread/test_data/README.md @@ -0,0 +1,38 @@ +## GffRead usage examples + +GffRead can be used to simply read an annotation file in a GFF format, and print it in either GFF3 (default) or +GTF2 format (with the -T option), while discarding any non-trasncript features and optional attributes. +It can also report some potential issues found in the input GFF records. The command line for such a quick GFF/GTF +file cleanup would be: +``` +gffread -E annotation.gff -o ann_simple.gff +``` + +This will create a minimalist GFF3 re-formatting of the transcript records found in the input file (`annotation.gff` in this example). +The -E option directs GffRead to "expose" (display warnings about) any potential formatting issues +encountered while parsing the input file. + +In order to obtain the GTF2 version of the same transcript records, the `-T` option should be added: +``` +gffread annotation.gff -T -o annotation.gtf +``` + +GffRead can be used to generate a FASTA file with the DNA sequences for all transcripts in a GFF file. For this operation +a fasta file with the genomic sequences has to be provided as well. This can be accomplished with a command line like this: +``` +gffread -w transcripts.fa -g genome.fa annotation.gff +``` +The file `genome.fa` in this example would be a multi-fasta file with the chromosome/contig sequences of the target genome. +This also requires that every contig or chromosome name found in the 1st column of the input GFF file +(`annotation.gff` in this example) must have a corresponding sequence entry in the `genome.fa` file. + + +``` +gffread --table @id,@chr,@start,@end,@strand,@exons,Name,gene,product \ + -o annotation.tbl annotation.gff +``` +This shows how the `--table` option can make a tab delimited table out of a GFF3 input. + +The `output` directory contains all the output files that should be generated by the above examples. + + diff --git a/src/gffread/test_data/output/ann_simple.gff b/src/gffread/test_data/output/ann_simple.gff new file mode 100644 index 00000000..c8e5e933 --- /dev/null +++ b/src/gffread/test_data/output/ann_simple.gff @@ -0,0 +1,5 @@ +##gff-version 3 +# gffread v0.12.7 +# gffread -E -o output/ann_simple.gff sequence.gff3 +NM_141699.3 RefSeq gene 22 795 . + . ID=gene-Dmel_CG16905;gene_name=eloF +NM_141699.3 RefSeq CDS 22 795 . + 0 Parent=gene-Dmel_CG16905 diff --git a/src/gffread/test_data/output/annotation.gtf b/src/gffread/test_data/output/annotation.gtf new file mode 100644 index 00000000..7e203137 --- /dev/null +++ b/src/gffread/test_data/output/annotation.gtf @@ -0,0 +1,2 @@ +NM_141699.3 RefSeq transcript 22 795 . + . transcript_id "gene-Dmel_CG16905"; gene_id "gene-Dmel_CG16905"; gene_name "eloF" +NM_141699.3 RefSeq CDS 22 795 . + 0 transcript_id "gene-Dmel_CG16905"; gene_name "eloF"; diff --git a/src/gffread/test_data/output/annotation.tbl b/src/gffread/test_data/output/annotation.tbl new file mode 100644 index 00000000..15a5c0fd --- /dev/null +++ b/src/gffread/test_data/output/annotation.tbl @@ -0,0 +1 @@ +gene-Dmel_CG16905 NM_141699.3 22 795 + 22-795 eloF eloF elongase F diff --git a/src/gffread/test_data/output/transcripts.fa b/src/gffread/test_data/output/transcripts.fa new file mode 100644 index 00000000..889ebec9 --- /dev/null +++ b/src/gffread/test_data/output/transcripts.fa @@ -0,0 +1,13 @@ +>gene-Dmel_CG16905 CDS=1-774 +ATGTTCGCTCCGATAGATCCTGTAAAGATACCCGTTGTAAGCAATCCATGGATAACCATGGGCACATTGA +TTGGCTATCTGCTGTTTGTGCTCAAGCTGGGCCCCAAAATCATGGAGCACCGAAAGCCCTTCCATTTGAA +TGGCGTCATCAGGATCTACAACATATTCCAGATCCTTTACAATGGTCTAATACTCGTTTTAGGAGTTCAC +TTCCTGTTTGTCCTGAAAGCCTACCAAATCAGTTGCATTGTTAGCCTGCCGATGGATCACAAATATAAGG +ATAGAGAGCGTTTGATTTGCACTTTGTACCTGGTGAACAAATTCGTAGACCTTGTGGAAACCATTTTCTT +TGTGCTCCGCAAAAAGGACAGACAGATATCCTTCCTGCACGTCTTCCATCATTTTGCGATGGCATTTTTT +GGATATCTCTACTACTGCTTCCACGGATACGGTGGCGTTGCCTTTCCACAGTGCCTGCTAAACACCGCCG +TCCACGTGATTATGTACGCCTACTACTATCTATCCTCGATCAGCAAGGAGGTGCAGAGAAGTCTCTGGTG +GAAGAAATACATCACAATTGCTCAGCTGGTCCAGTTCGCCATTATTCTGCTCCACTGTACCATCACGCTG +GCACAGCCCAACTGCGCGGTCAACAGACCCTTGACCTACGGATGCGGATCGCTTTCAGCGTTTTTTGCAG +TGATATTTAGCCAATTTTATTACCACAACTACATAAAGCCAGGAAAGAAGTCAGCGAAACAAAACAAAAA +TTAA diff --git a/src/gffread/test_data/script.sh b/src/gffread/test_data/script.sh new file mode 100755 index 00000000..0c6e725c --- /dev/null +++ b/src/gffread/test_data/script.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# clone repo +if [ ! -d /tmp/gffread_source ]; then + git clone --depth 2 --single-branch --branch master https://github.com/gpertea/gffread.git /tmp/gffread_source +fi + +# copy test data +cp -r /tmp/gffread_source/examples/* src/gffread/test_data diff --git a/src/gffread/test_data/sequence.fasta b/src/gffread/test_data/sequence.fasta new file mode 100644 index 00000000..31ec0f04 --- /dev/null +++ b/src/gffread/test_data/sequence.fasta @@ -0,0 +1,16 @@ +>NM_141699.3 Drosophila melanogaster elongase F (eloF), mRNA +CACAACTCGATTAGATTCGCCATGTTCGCTCCGATAGATCCTGTAAAGATACCCGTTGTAAGCAATCCAT +GGATAACCATGGGCACATTGATTGGCTATCTGCTGTTTGTGCTCAAGCTGGGCCCCAAAATCATGGAGCA +CCGAAAGCCCTTCCATTTGAATGGCGTCATCAGGATCTACAACATATTCCAGATCCTTTACAATGGTCTA +ATACTCGTTTTAGGAGTTCACTTCCTGTTTGTCCTGAAAGCCTACCAAATCAGTTGCATTGTTAGCCTGC +CGATGGATCACAAATATAAGGATAGAGAGCGTTTGATTTGCACTTTGTACCTGGTGAACAAATTCGTAGA +CCTTGTGGAAACCATTTTCTTTGTGCTCCGCAAAAAGGACAGACAGATATCCTTCCTGCACGTCTTCCAT +CATTTTGCGATGGCATTTTTTGGATATCTCTACTACTGCTTCCACGGATACGGTGGCGTTGCCTTTCCAC +AGTGCCTGCTAAACACCGCCGTCCACGTGATTATGTACGCCTACTACTATCTATCCTCGATCAGCAAGGA +GGTGCAGAGAAGTCTCTGGTGGAAGAAATACATCACAATTGCTCAGCTGGTCCAGTTCGCCATTATTCTG +CTCCACTGTACCATCACGCTGGCACAGCCCAACTGCGCGGTCAACAGACCCTTGACCTACGGATGCGGAT +CGCTTTCAGCGTTTTTTGCAGTGATATTTAGCCAATTTTATTACCACAACTACATAAAGCCAGGAAAGAA +GTCAGCGAAACAAAACAAAAATTAACTAAATTTAAACTAAATCATGAGTACAAAGCCTAAAGATTCGTGA +AGCAACAATAGCCACAGCCTATTTTTGAATATTTCATATATGATTTTATGGGGTAAATGAATTAAAAAAC +ATTTGTTTTCTTGGCGTCAAACT + diff --git a/src/gffread/test_data/sequence.gff3 b/src/gffread/test_data/sequence.gff3 new file mode 100644 index 00000000..c6a77a7a --- /dev/null +++ b/src/gffread/test_data/sequence.gff3 @@ -0,0 +1,9 @@ +##gff-version 3 +#!gff-spec-version 1.21 +#!processor NCBI annotwriter +##sequence-region NM_141699.3 1 933 +##species https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?id=7227 +NM_141699.3 RefSeq region 1 933 . + . ID=NM_141699.3:1..933;Dbxref=taxon:7227;Name=3R;chromosome=3R;gbkey=Src;genome=chromosome;genotype=y[1]%3B Gr22b[1] Gr22d[1] cn[1] CG33964[R4.2] bw[1] sp[1]%3B LysC[1] MstProx[1] GstD5[1] Rh6[1];mol_type=mRNA +NM_141699.3 RefSeq gene 1 933 . + . ID=gene-Dmel_CG16905;Dbxref=FLYBASE:FBgn0037762,GeneID:41211;Name=eloF;cyt_map=85E10-85E10;description=elongase F;gbkey=Gene;gen_map=3-49 cM;gene=eloF;gene_synonym=CG16905,Dmel\CG16905,EloF;locus_tag=Dmel_CG16905 +NM_141699.3 RefSeq CDS 22 795 . + 0 ID=cds-NP_649956.1;Parent=gene-Dmel_CG16905;Dbxref=FLYBASE:FBpp0081622,GeneID:41211,GenBank:NP_649956.1,FLYBASE:FBgn0037762;Name=NP_649956.1;gbkey=CDS;gene=eloF;locus_tag=Dmel_CG16905;orig_transcript_id=gnl|FlyBase|CG16905-RA;product=elongase F;protein_id=NP_649956.1 + diff --git a/src/lofreq/call/config.vsh.yaml b/src/lofreq/call/config.vsh.yaml new file mode 100644 index 00000000..c547de9d --- /dev/null +++ b/src/lofreq/call/config.vsh.yaml @@ -0,0 +1,250 @@ +name: lofreq_call +namespace: lofreq +description: | + Call variants from a BAM file. + + LoFreq* (i.e. LoFreq version 2) is a fast and sensitive variant-caller for inferring SNVs and indels from next-generation sequencing data. It makes full use of base-call qualities and other sources of errors inherent in sequencing (e.g. mapping or base/indel alignment uncertainty), which are usually ignored by other methods or only used for filtering. + + LoFreq* can run on almost any type of aligned sequencing data (e.g. Illumina, IonTorrent or Pacbio) since no machine- or sequencing-technology dependent thresholds are used. It automatically adapts to changes in coverage and sequencing quality and can therefore be applied to a variety of data-sets e.g. viral/quasispecies, bacterial, metagenomics or somatic data. + + LoFreq* is very sensitive; most notably, it is able to predict variants below the average base-call quality (i.e. sequencing error rate). Each variant call is assigned a p-value which allows for rigorous false positive control. Even though it uses no approximations or heuristics, it is very efficient due to several runtime optimizations and also provides a (pseudo-)parallel implementation. LoFreq* is generic and fast enough to be applied to high-coverage data and large genomes. On a single processor it takes a minute to analyze Dengue genome sequencing data with nearly 4000X coverage, roughly one hour to call SNVs on a 600X coverage E.coli genome and also roughly an hour to run on a 100X coverage human exome dataset. +keywords: [ "variant calling", "low frequancy variant calling", "lofreq", "lofreq/call"] +links: + homepage: https://csb5.github.io/lofreq/ + documentation: https://csb5.github.io/lofreq/commands/ +references: + doi: 10.1093/nar/gks918 +license: "MIT" +requirements: + commands: [ lofreq ] +argument_groups: + - name: Inputs + arguments: + - name: --input + type: file + description: | + Input BAM file. + required: true + example: "normal.bam" + - name: --input_bai + type: file + description: | + Index file for the input BAM file. + required: true + example: "normal.bai" + - name: --ref + alternatives: -f + type: file + description: | + Indexed reference fasta file (gzip supported). Default: none. + required: true + example: "reference.fasta" + - name: Outputs + arguments: + - name: --out + alternatives: -o + type: file + description: | + Vcf output file. Default: stdout. + required: true + direction: output + example: "output.vcf" + - name: Arguments + arguments: + - name: --region + alternatives: -r + type: string + description: | + Limit calls to this region (chrom:start-end). Default: none. + required: false + example: "chr1:1000-2000" + - name: --bed + alternatives: -l + type: file + description: | + List of positions (chr pos) or regions (BED). Default: none. + required: false + example: "regions.bed" + - name: --min_bq + alternatives: -q + type: integer + description: | + Skip any base with baseQ smaller than INT. Default: 6. + required: false + example: 6 + - name: --min_alt_bq + alternatives: -Q + type: integer + description: | + Skip alternate bases with baseQ smaller than INT. Default: 6. + required: false + example: 6 + - name: --def_alt_bq + alternatives: -R + type: integer + description: | + Overwrite baseQs of alternate bases (that passed bq filter) with this value (-1: use median ref-bq; 0: keep). Default: 0. + required: false + example: 0 + - name: --min_jq + alternatives: -j + type: integer + description: | + Skip any base with joinedQ smaller than INT. Default: 0. + example: 0 + - name: --min_alt_jq + alternatives: -J + type: integer + description: | + Skip alternate bases with joinedQ smaller than INT. Default: 0. + required: false + example: 0 + - name: --def_alt_jq + alternatives: -K + type: integer + description: | + Overwrite joinedQs of alternate bases (that passed jq filter) with this value (-1: use median ref-bq; 0: keep). Default: 0. + required: false + example: 0 + - name: --no_baq + alternatives: -B + type: boolean_true + description: | + Disable use of base-alignment quality (BAQ). + - name: --no_idaq + alternatives: -A + type: boolean_true + description: | + Don't use IDAQ values (NOT recommended under ANY circumstances other than debugging). + - name: --del_baq + alternatives: -D + type: boolean_true + description: | + Delete pre-existing BAQ values, i.e. compute even if already present in BAM. + - name: --no_ext_baq + alternatives: -e + type: boolean_true + description: | + Use 'normal' BAQ (samtools default) instead of extended BAQ (both computed on the fly if not already present in lb tag). + - name: --min_mq + alternatives: -m + type: integer + description: | + Skip reads with mapping quality smaller than INT. Default: 0. + required: false + example: 0 + - name: --max_mq + alternatives: -M + type: integer + description: | + Cap mapping quality at INT. Default: 255. + required: false + example: 255 + - name: --no_mq + alternatives: -N + type: boolean_true + description: | + Don't merge mapping quality in LoFreq's model. + - name: --call_indels + type: boolean_true + description: | + Enable indel calls (note: preprocess your file to include indel alignment qualities!). + - name: --only_indels + type: boolean_true + description: | + Only call indels; no SNVs. + - name: --src_qual + alternatives: -s + type: boolean_true + description: | + Enable computation of source quality. + - name: --ign_vcf + alternatives: -S + type: file + description: | + Ignore variants in this vcf file for source quality computation. Multiple files can be given separated by commas. + required: false + example: "variants.vcf" + - name: --def_nm_q + alternatives: -T + type: integer + description: | + If >= 0, then replace non-match base qualities with this default value. Default: -1. + required: false + example: -1 + - name: --sig + alternatives: -a + type: double + description: | + P-Value cutoff / significance level. Default: 0.010000. + required: false + example: 0.01 + - name: --bonf + alternatives: -b + type: string + description: | + Bonferroni factor. 'dynamic' (increase per actually performed test) or INT. Default: Dynamic. + required: false + example: "dynamic" + - name: --min_cov + alternatives: -C + type: integer + description: | + Test only positions having at least this coverage. Default: 1. + (note: without --no-default-filter default filters (incl. coverage) kick in after predictions are done). + required: false + example: 1 + - name: --max_depth + alternatives: -d + type: integer + description: | + Cap coverage at this depth. Default: 1000000. + required: false + example: 1000000 + - name: --illumina_13 + type: boolean_true + description: | + Assume the quality is Illumina-1.3-1.7/ASCII+64 encoded. + - name: --use_orphan + type: boolean_true + description: | + Count anomalous read pairs (i.e. where mate is not aligned properly). + - name: --plp_summary_only + type: boolean_true + description: | + No variant calling. Just output pileup summary per column. + - name: --no_default_filter + type: boolean_true + description: | + Don't run default 'lofreq filter' automatically after calling variants. + - name: --force_overwrite + type: boolean_true + description: | + Overwrite any existing output. + - name: --verbose + type: boolean_true + description: | + Be verbose. + - name: --debug + type: boolean_true + description: | + Enable debugging. +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/lofreq:2.1.5--py38h794fc9e_10 + setup: + - type: docker + run: | + version=$(lofreq version | grep 'version' | sed 's/version: //') && \ + echo "lofreq: $version" > /var/software_versions.txt +runners: + - type: executable + - type: nextflow diff --git a/src/lofreq/call/help.txt b/src/lofreq/call/help.txt new file mode 100644 index 00000000..16178f07 --- /dev/null +++ b/src/lofreq/call/help.txt @@ -0,0 +1,49 @@ +lofreq call: call variants from BAM file + +Usage: lofreq call [options] in.bam + +Options: +- Reference: + -f | --ref FILE Indexed reference fasta file (gzip supported) [null] +- Output: + -o | --out FILE Vcf output file [- = stdout] +- Regions: + -r | --region STR Limit calls to this region (chrom:start-end) [null] + -l | --bed FILE List of positions (chr pos) or regions (BED) [null] +- Base-call quality: + -q | --min-bq INT Skip any base with baseQ smaller than INT [6] + -Q | --min-alt-bq INT Skip alternate bases with baseQ smaller than INT [6] + -R | --def-alt-bq INT Overwrite baseQs of alternate bases (that passed bq filter) with this value (-1: use median ref-bq; 0: keep) [0] + -j | --min-jq INT Skip any base with joinedQ smaller than INT [0] + -J | --min-alt-jq INT Skip alternate bases with joinedQ smaller than INT [0] + -K | --def-alt-jq INT Overwrite joinedQs of alternate bases (that passed jq filter) with this value (-1: use median ref-bq; 0: keep) [0] +- Base-alignment (BAQ) and indel-aligment (IDAQ) qualities: + -B | --no-baq Disable use of base-alignment quality (BAQ) + -A | --no-idaq Don't use IDAQ values (NOT recommended under ANY circumstances other than debugging) + -D | --del-baq Delete pre-existing BAQ values, i.e. compute even if already present in BAM + -e | --no-ext-baq Use 'normal' BAQ (samtools default) instead of extended BAQ (both computed on the fly if not already present in lb tag) +- Mapping quality: + -m | --min-mq INT Skip reads with mapping quality smaller than INT [0] + -M | --max-mq INT Cap mapping quality at INT [255] + -N | --no-mq Don't merge mapping quality in LoFreq's model +- Indels: + --call-indels Enable indel calls (note: preprocess your file to include indel alignment qualities!) + --only-indels Only call indels; no SNVs +- Source quality: + -s | --src-qual Enable computation of source quality + -S | --ign-vcf FILE Ignore variants in this vcf file for source quality computation. Multiple files can be given separated by commas + -T | --def-nm-q INT If >= 0, then replace non-match base qualities with this default value [-1] +- P-values: + -a | --sig P-Value cutoff / significance level [0.010000] + -b | --bonf Bonferroni factor. 'dynamic' (increase per actually performed test) or INT ['dynamic'] +- Misc.: + -C | --min-cov INT Test only positions having at least this coverage [1] + (note: without --no-default-filter default filters (incl. coverage) kick in after predictions are done) + -d | --max-depth INT Cap coverage at this depth [1000000] + --illumina-1.3 Assume the quality is Illumina-1.3-1.7/ASCII+64 encoded + --use-orphan Count anomalous read pairs (i.e. where mate is not aligned properly) + --plp-summary-only No variant calling. Just output pileup summary per column + --no-default-filter Don't run default 'lofreq filter' automatically after calling variants + --force-overwrite Overwrite any existing output + --verbose Be verbose + --debug Enable debugging \ No newline at end of file diff --git a/src/lofreq/call/script.sh b/src/lofreq/call/script.sh new file mode 100644 index 00000000..863fe986 --- /dev/null +++ b/src/lofreq/call/script.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +# Unset all parameters that are set to "false" +[[ "$par_no_baq" == "false" ]] && unset par_no_baq +[[ "$par_no_idaq" == "false" ]] && unset par_no_idaq +[[ "$par_del_baq" == "false" ]] && unset par_del_baq +[[ "$par_no_ext_baq" == "false" ]] && unset par_no_ext_baq +[[ "$par_no_mq" == "false" ]] && unset par_no_mq +[[ "$par_call_indels" == "false" ]] && unset par_call_indels +[[ "$par_only_indels" == "false" ]] && unset par_only_indels +[[ "$par_src_qual" == "false" ]] && unset par_src_qual +[[ "$par_illumina_13" == "false" ]] && unset par_illumina_13 +[[ "$par_use_orphan" == "false" ]] && unset par_use_orphan +[[ "$par_plp_summary_only" == "false" ]] && unset par_plp_summary_only +[[ "$par_no_default_filter" == "false" ]] && unset par_no_default_filter +[[ "$par_force_overwrite" == "false" ]] && unset par_force_overwrite +[[ "$par_verbose" == "false" ]] && unset par_verbose +[[ "$par_debug" == "false" ]] && unset par_debug + +# Run lofreq call +lofreq call \ + -f "$par_ref" \ + -o "$par_out" \ + ${par_region:+-r "${par_region}"} \ + ${par_bed:+-l "${par_bed}"} \ + ${par_min_bq:+-q "${par_min_bq}"} \ + ${par_min_alt_bq:+-Q "${par_min_alt_bq}"} \ + ${par_def_alt_bq:+-R "${par_def_alt_bq}"} \ + ${par_min_jq:+-j "${par_min_jq}"} \ + ${par_alt_jq:+-K "${par_alt_jq}"} \ + ${par_no_baq:+-B} \ + ${par_no_idaq:+-A} \ + ${par_del_baq:+-D} \ + ${par_no_ext_baq:+-e} \ + ${par_min_mq:+-m "${par_min_mq}"} \ + ${par_max_mq:+-M "${par_max_mq}"} \ + ${par_no_mq:+-N} \ + ${par_call_indels:+--call-indels} \ + ${par_only_indels:+--only-indels} \ + ${par_src_qual:+-s} \ + ${par_ign_vcf:+-S "${par_ign_vcf}"} \ + ${par_def_nm_q:+-T "${par_def_nm_q}"} \ + ${par_sig:+-a "${par_sig}"} \ + ${par_bonf:+-b "${par_bonf}"} \ + ${par_min_cov:+-C "${par_min_cov}"} \ + ${par_max_depth:+-d "${par_max_depth}"} \ + ${par_illumina_13:+--illumina-1.3} \ + ${par_use_orphan:+--use-orphan} \ + ${par_plp_summary_only:+--plp-summary-only} \ + ${par_no_default_filter:+--no-default-filter} \ + ${par_force_overwrite:+--force-overwrite} \ + ${par_verbose:+--verbose} \ + ${par_debug:+--debug} \ + "$par_input" \ No newline at end of file diff --git a/src/lofreq/call/test.sh b/src/lofreq/call/test.sh new file mode 100644 index 00000000..d8556398 --- /dev/null +++ b/src/lofreq/call/test.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -e + +dir_in="${meta_resources_dir%/}/test_data" + +echo "> Run lofreq call" +"$meta_executable" \ + --input "$dir_in/a.bam" \ + --input_bai "$dir_in/a.bai" \ + --ref "$dir_in/genome.fasta" \ + --out "output.vcf" \ + +echo ">> Checking output" +[ ! -f "output.vcf" ] && echo "Output file output.vcf does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "output.vcf" ] && echo "Output file output.vcf is empty" && exit 1 + +echo "> Test successful" \ No newline at end of file diff --git a/src/lofreq/call/test_data/a.bai b/src/lofreq/call/test_data/a.bai new file mode 100644 index 00000000..fd401327 Binary files /dev/null and b/src/lofreq/call/test_data/a.bai differ diff --git a/src/lofreq/call/test_data/a.bam b/src/lofreq/call/test_data/a.bam new file mode 100644 index 00000000..109b5fac Binary files /dev/null and b/src/lofreq/call/test_data/a.bam differ diff --git a/src/lofreq/call/test_data/genome.fasta b/src/lofreq/call/test_data/genome.fasta new file mode 100644 index 00000000..e2015391 --- /dev/null +++ b/src/lofreq/call/test_data/genome.fasta @@ -0,0 +1,8 @@ +>SheilaA +GCTAGCTCAGAAAAAAAAAA +>SheilaB +GCTAGCTCAGAAAAAAAAAA +>SheilaC +GCTAGCTCAGAAAAAAAAAA +>SheilaD +GCTAGCTCAGAAAAAAAAAA diff --git a/src/lofreq/call/test_data/genome.fasta.fai b/src/lofreq/call/test_data/genome.fasta.fai new file mode 100644 index 00000000..e42bfe2c --- /dev/null +++ b/src/lofreq/call/test_data/genome.fasta.fai @@ -0,0 +1,4 @@ +SheilaA 20 9 20 21 +SheilaB 20 39 20 21 +SheilaC 20 69 20 21 +SheilaD 20 99 20 21 diff --git a/src/lofreq/call/test_data/script.sh b/src/lofreq/call/test_data/script.sh new file mode 100644 index 00000000..9a90bf48 --- /dev/null +++ b/src/lofreq/call/test_data/script.sh @@ -0,0 +1,10 @@ +# pear test data + +# Test data was obtained from https://github.com/snakemake/snakemake-wrappers/tree/master/bio/lofreq/call/test/data + +if [ ! -d /tmp/snakemake-wrappers ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers /tmp/snakemake-wrappers +fi + +cp -r /tmp/snakemake-wrappers/bio/lofreq/call/test/data/* src/lofreq/call/test_data + diff --git a/src/lofreq/indelqual/config.vsh.yaml b/src/lofreq/indelqual/config.vsh.yaml new file mode 100644 index 00000000..0524458e --- /dev/null +++ b/src/lofreq/indelqual/config.vsh.yaml @@ -0,0 +1,82 @@ +name: lofreq_indelqual +namespace: lofreq +description: | + Insert indel qualities into BAM file (required for indel predictions). + + The preferred way of inserting indel qualities should be via GATK's BQSR (>=2) If that's not possible, use this subcommand. + The command has two modes: 'uniform' and 'dindel': + - 'uniform' will assign a given value uniformly, whereas + - 'dindel' will insert indel qualities based on Dindel (PMID 20980555). + Both will overwrite any existing values. + Do not realign your BAM file afterwards! +keywords: [ "bam", "indel", "qualities", "indelqual", "lofreq", "lofreq/indelqual"] +links: + homepage: https://csb5.github.io/lofreq/ + documentation: https://csb5.github.io/lofreq/commands/ +references: + doi: 10.1093/nar/gks918 +license: "MIT" +requirements: + commands: [ lofreq ] +argument_groups: + - name: Inputs + arguments: + - name: --input + type: file + description: | + Input BAM file. + required: true + example: "normal.bam" + - name: --ref + alternatives: -f + type: file + description: | + Reference sequence used for mapping (Only required for --dindel). + required: false + example: "reference.fasta" + - name: Outputs + arguments: + - name: --out + alternatives: -o + type: file + description: | + Output BAM file. + required: true + direction: output + example: "output.bam" + - name: Arguments + arguments: + - name: --uniform + alternatives: -u + type: string + description: | + Add this indel quality uniformly to all bases. Use two comma separated values to specify insertion and deletion quality separately. (clashes with --dindel). + required: false + example: "50,50" + - name: --dindel + type: boolean_true + description: | + Add Dindel's indel qualities (Illumina specific) (clashes with -u; needs --ref). + - name: --verbose + type: boolean_true + description: | + Be verbose. +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/lofreq:2.1.5--py38h794fc9e_10 + setup: + - type: docker + run: | + version=$(lofreq version | grep 'version' | sed 's/version: //') && \ + echo "lofreq: $version" > /var/software_versions.txt +runners: + - type: executable + - type: nextflow diff --git a/src/lofreq/indelqual/help.txt b/src/lofreq/indelqual/help.txt new file mode 100644 index 00000000..d520f1ad --- /dev/null +++ b/src/lofreq/indelqual/help.txt @@ -0,0 +1,21 @@ +lofreq indelqual: Insert indel qualities into BAM file (required for indel predictions) + +Usage: lofreq indelqual [options] in.bam +Options: + -u | --uniform INT[,INT] Add this indel quality uniformly to all bases. + Use two comma separated values to specify + insertion and deletion quality separately. + (clashes with --dindel) + --dindel Add Dindel's indel qualities (Illumina specific) + (clashes with -u; needs --ref) + -f | --ref Reference sequence used for mapping + (Only required for --dindel) + -o | --out FILE Output BAM file [- = stdout = default] + --verbose Be verbose + +The preferred way of inserting indel qualities should be via GATK's BQSR (>=2) If that's not possible, use this subcommand. +The command has two modes: 'uniform' and 'dindel': +- 'uniform' will assign a given value uniformly, whereas +- 'dindel' will insert indel qualities based on Dindel (PMID 20980555). +Both will overwrite any existing values. +Do not realign your BAM file afterwards! \ No newline at end of file diff --git a/src/lofreq/indelqual/script.sh b/src/lofreq/indelqual/script.sh new file mode 100644 index 00000000..341886ba --- /dev/null +++ b/src/lofreq/indelqual/script.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +# Unset all parameters that are set to "false" +[[ "$par_dindel" == "false" ]] && unset par_dindel +[[ "$par_verbose" == "false" ]] && unset par_verbose + +# run lofreq indelqual +lofreq indelqual \ + -o "$par_out" \ + ${par_uniform:+-u "${par_uniform}"} \ + ${par_dindel:+--dindel} \ + ${par_ref:+-f "${par_ref}"} \ + ${par_verbose:+--verbose} \ + "$par_input" diff --git a/src/lofreq/indelqual/test.sh b/src/lofreq/indelqual/test.sh new file mode 100644 index 00000000..9e7f6fe3 --- /dev/null +++ b/src/lofreq/indelqual/test.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +set -e + +dir_in="${meta_resources_dir%/}/test_data" + +############################################# +mkdir uniform +cd uniform + +echo "> Run lofreq indelqual uniform" +"$meta_executable" \ + --input "$dir_in/test.bam" \ + -u 15 \ + --out "uniform.bam" \ + +echo ">> Checking output" +[ ! -f "uniform.bam" ] && echo "Output file uniform.bam does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "uniform.bam" ] && echo "Output file uniform.bam is empty" && exit 1 + +cd .. + +############################################# +mkdir dindel +cd dindel + +echo "> run lofreq indelqual dindel" +"$meta_executable" \ + --input "$dir_in/test.bam" \ + --ref "$dir_in/test.fa" \ + --dindel \ + --out "dindel.bam" + +echo ">> Checking output" +[ ! -f "dindel.bam" ] && echo "Output file dindel.bam does not exist" && exit 1 + +echo ">> Check if output is empty" +[ ! -s "dindel.bam" ] && echo "Output file dindel.bam is empty" && exit 1 + +cd .. + +############################################# + +echo "> Test successful" diff --git a/src/lofreq/indelqual/test_data/script.sh b/src/lofreq/indelqual/test_data/script.sh new file mode 100755 index 00000000..ba348067 --- /dev/null +++ b/src/lofreq/indelqual/test_data/script.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +set -e + +TMPDIR=$(mktemp -d) +trap "rm -rf $TMPDIR" EXIT + +### Step 1: Generate Test Reference FASTA File (`test.fa`) + +cat > $TMPDIR/test.fa <chr1 +AACTCTCCGTGCTGTCCGGGGTCACTGTGATGCCAGTGCCGTCGACGGACCACAGGAGCGCCGCCAATTACGATTTATA +GGCGGCCCGGCCGATTATATCTTTGGCGGTCCCCTAGGCTCTCTAGGGGCCCGCACTGAAGAGGGCAACTCTGCAAGGA +CACGAATCTGACTCCTTAATAAAGGTGTGAAATCTGTCCGGTCGTCTCCTAATATGGGGCTTCATCATCTCAGGCGAAA +TCAGCGCCCGACGGGCCATAGTAAGCGGTGTTGTGGCATAGGTGCAGGTGGCCACCGATTATAACAGGATGACATACGC +GGAATTCGGGGTATGATGCTCTCCCGACACTTTGAGACAATAAATAGTTTAGTGTCCTGATGGTCTAAACCGAAGTCAT +TCAAAATAGCTAAGTGTAGTCTTCCCGTTCTAGGGATAGTCTAGGACATGCCCTATATTGGTTTTCTCTTACCGCGGAC +TACTCCCGCGCCCTCGGAGGTGTCTCAATTCATCCATGTTGATCCTTCAAATCGGGGCAGCGACGGGGGCACGGAGGGG +GTACGATAACCGCTAAATTGACCACCACCATCGATGATTCTACCATCTCTATCCATCCAACCCTTTTTTTGTTTATTTC +CTCTATGGGTTACAGCTA +EOF + +### Step 2: Index the Reference FASTA File + +samtools faidx $TMPDIR/test.fa + +### Step 3: Generate Test Reads with `wgsim` + +wgsim -N 100 -1 70 -2 70 $TMPDIR/test.fa $TMPDIR/reads1.fq $TMPDIR/reads2.fq + +### Step 4: Align Reads to Generate BAM File + +bwa index $TMPDIR/test.fa + +bwa mem $TMPDIR/test.fa $TMPDIR/reads1.fq $TMPDIR/reads2.fq > $TMPDIR/aligned_reads.sam + +### Step 5: Convert SAM to BAM, Sort, and Index + +samtools view -Sb $TMPDIR/aligned_reads.sam > $TMPDIR/test.bam + +### Step 6: Copy output + +cp $TMPDIR/test.bam src/lofreq/indelqual/test_data/test.bam +cp $TMPDIR/test.fa src/lofreq/indelqual/test_data/test.fa \ No newline at end of file diff --git a/src/lofreq/indelqual/test_data/test.bam b/src/lofreq/indelqual/test_data/test.bam new file mode 100644 index 00000000..2d326400 Binary files /dev/null and b/src/lofreq/indelqual/test_data/test.bam differ diff --git a/src/lofreq/indelqual/test_data/test.fa b/src/lofreq/indelqual/test_data/test.fa new file mode 100644 index 00000000..6f39d3e9 --- /dev/null +++ b/src/lofreq/indelqual/test_data/test.fa @@ -0,0 +1,10 @@ +>chr1 +AACTCTCCGTGCTGTCCGGGGTCACTGTGATGCCAGTGCCGTCGACGGACCACAGGAGCGCCGCCAATTACGATTTATA +GGCGGCCCGGCCGATTATATCTTTGGCGGTCCCCTAGGCTCTCTAGGGGCCCGCACTGAAGAGGGCAACTCTGCAAGGA +CACGAATCTGACTCCTTAATAAAGGTGTGAAATCTGTCCGGTCGTCTCCTAATATGGGGCTTCATCATCTCAGGCGAAA +TCAGCGCCCGACGGGCCATAGTAAGCGGTGTTGTGGCATAGGTGCAGGTGGCCACCGATTATAACAGGATGACATACGC +GGAATTCGGGGTATGATGCTCTCCCGACACTTTGAGACAATAAATAGTTTAGTGTCCTGATGGTCTAAACCGAAGTCAT +TCAAAATAGCTAAGTGTAGTCTTCCCGTTCTAGGGATAGTCTAGGACATGCCCTATATTGGTTTTCTCTTACCGCGGAC +TACTCCCGCGCCCTCGGAGGTGTCTCAATTCATCCATGTTGATCCTTCAAATCGGGGCAGCGACGGGGGCACGGAGGGG +GTACGATAACCGCTAAATTGACCACCACCATCGATGATTCTACCATCTCTATCCATCCAACCCTTTTTTTGTTTATTTC +CTCTATGGGTTACAGCTA diff --git a/src/multiqc/config.vsh.yaml b/src/multiqc/config.vsh.yaml new file mode 100644 index 00000000..0a3a784b --- /dev/null +++ b/src/multiqc/config.vsh.yaml @@ -0,0 +1,229 @@ +name: "multiqc" +description: | + MultiQC aggregates results from bioinformatics analyses across many samples into a single report. + It searches a given directory for analysis logs and compiles a HTML report. It's a general use tool, perfect for summarising the output from numerous bioinformatics tools. +info: + keywords: [QC, html report, aggregate analysis] + links: + homepage: https://multiqc.info/ + documentation: https://multiqc.info/docs/ + repository: https://github.com/MultiQC/MultiQC + references: + doi: 10.1093/bioinformatics/btw354 + licence: GPL v3 or later + +argument_groups: + - name: "Input" + arguments: + - name: "--input" + type: file + multiple: true + required: true + example: data/results/ + description: | + File paths to be searched for analysis results to be included in the report. + + - name: "Ouput" + arguments: + - name: "--output_report" + type: file + direction: output + must_exist: false + example: multiqc_report.html + description: | + Filepath of the generated report. + - name: "--output_data" + type: file + required: false + direction: output + example: multiqc_data + must_exist: false + description: | + Output directory for parsed data files. If not provided, parsed data will not be published. + - name: "--output_plots" + type: file + required: false + direction: output + must_exist: false + example: multiqc_plots + description: | + Output directory for generated plots. If not provided, plots will not be published. + + - name: "Modules and analyses to run" + arguments: + - name: "--include_modules" + type: string + multiple: true + multiple_sep: "," + example: fastqc,cutadapt + description: Use only these module + - name: "--exclude_modules" + type: string + multiple: true + multiple_sep: "," + example: fastqc,cutadapt + description: Do not use only these modules + - name: "--ignore_analysis" + type: string + multiple: true + multiple_sep: "," + example: run_one/*,run_two/* + - name: "--ignore_samples" + type: string + multiple: true + multiple_sep: "," + example: sample_1*,sample_3* + - name: "--ignore_symlinks" + type: boolean_true + description: Ignore symlinked directories and files + + - name: "Sample name handling" + arguments: + - name: "--dirs" + type: boolean_true + description: Prepend directory to sample names to avoid clashing filenames + - name: "--dirs_depth" + type: integer + description: Prepend n directories to sample names. Negative number to take from start of path. + - name: "--full_names" + type: boolean_true + description: Do not clean the sample names (leave as full file name) + - name: "--fn_as_s_name" + type: boolean_true + description: Use the log filename as the sample name + - name: "--replace_names" + type: file + example: replace_names.tsv + description: TSV file to rename sample names during report generation + + - name: "Report Customisation" + arguments: + - name: "--title" + type: string + description: | + Report title. Printed as page header, used for filename if not otherwise specified. + - name: "--comment" + type: string + description: | + Custom comment, will be printed at the top of the report. + - name: "--template" + type: string + choices: [default, gathered, geo, highcharts, sections, simple] + description: | + Report template to use. + - name: "--sample_names" + type: file + description: | + TSV file containing alternative sample names for renaming buttons in the report. + example: sample_names.tsv + - name: "--sample_filters" + type: file + description: | + TSV file containing show/hide patterns for the report + example: sample_filters.tsv + - name: "--custom_css_file" + type: file + description: | + Custom CSS file to add to the final report + example: custom_style_sheet.css + - name: "--profile_runtime" + type: boolean_true + description: | + Add analysis of how long MultiQC takes to run to the report + + - name: "MultiQC behaviour" + arguments: + - name: "--verbose" + type: boolean_true + description: | + Increase output verbosity. + - name: "--quiet" + type: boolean_true + description: | + Only show log warnings + - name: "--strict" + type: boolean_true + description: | + Don't catch exceptions, run additional code checks to help development. + - name: "--development" + type: boolean_true + description: | + Development mode. Do not compress and minimise JS, export uncompressed plot data. + - name: "--require_logs" + type: boolean_true + description: | + Require all explicitly requested modules to have log files. If not, MultiQC will exit with an error. + - name: "--no_megaqc_upload" + type: boolean_true + description: | + Don't upload generated report to MegaQC, even if MegaQC options are found. + - name: "--no_ansi" + type: boolean_true + description: | + Disable coloured log output. + - name: "--cl_config" + type: string + required: false + description: | + YAML formatted string that allows to customize MultiQC behaviour like input file detection. + example: "qualimap_config: { general_stats_coverage: [20,40,200] }" + + - name: "Output format" + arguments: + - name: "--flat" + type: boolean_true + description: | + Use only flat plots (static images). + - name: "--interactive" + type: boolean_true + description: | + Use only interactive plots (in-browser Javascript). + - name: "--data_dir" + type: boolean_true + description: | + Force the parsed data directory to be created. + - name: "--no_data_dir" + type: boolean_true + description: | + Prevent the parsed data directory from being created. + - name: "--zip_data_dir" + type: boolean_true + description: | + Compress the data directory. + - name: "--data_format" + type: string + choices: [tsv, csv, json, yaml] + description: | + Output parsed data in a different format than the default 'txt'. + - name: "--pdf" + type: boolean_true + description: | + Creates PDF report with the 'simple' template. Requires Pandoc to be installed. + +resources: + - type: bash_script + path: script.sh + +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data + +engines: + - type: docker + image: quay.io/biocontainers/multiqc:1.21--pyhdfd78af_0 + setup: + - type: docker + run: | + multiqc --version | sed 's/multiqc, version\s\(.*\)/multiqc: "\1"/' > /var/software_versions.txt + test_setup: + - type: apt + packages: + - jq + +runners: + - type: executable + - type: nextflow + + diff --git a/src/multiqc/help.txt b/src/multiqc/help.txt new file mode 100644 index 00000000..9509e720 --- /dev/null +++ b/src/multiqc/help.txt @@ -0,0 +1,67 @@ + ```bash +multiqc --help +``` + +/// MultiQC 🔍 | v1.20 + + Usage: multiqc [OPTIONS] [ANALYSIS DIRECTORY] + + MultiQC aggregates results from bioinformatics analyses across many samples into a single report. + It searches a given directory for analysis logs and compiles a HTML report. It's a general use tool, perfect for summarising the output from numerous bioinformatics tools. + To run, supply with one or more directory to scan for analysis results. For example, to run in the current working directory, use 'multiqc .' + +╭─ Main options ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --force -f Overwrite any existing reports │ +│ --config -c Specific config file to load, after those in MultiQC dir / home dir / working dir. (PATH) │ +│ --cl-config Specify MultiQC config YAML on the command line (TEXT) │ +│ --filename -n Report filename. Use 'stdout' to print to standard out. (TEXT) │ +│ --outdir -o Create report in the specified output directory. (TEXT) │ +│ --ignore -x Ignore analysis files (GLOB EXPRESSION) │ +│ --ignore-samples Ignore sample names (GLOB EXPRESSION) │ +│ --ignore-symlinks Ignore symlinked directories and files │ +│ --file-list -l Supply a file containing a list of file paths to be searched, one per row │ +╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Choosing modules to run ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --module -m Use only this module. Can specify multiple times. (MODULE NAME) │ +│ --exclude -e Do not use this module. Can specify multiple times. (MODULE NAME) │ +╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Sample handling ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --dirs -d Prepend directory to sample names │ +│ --dirs-depth -dd Prepend n directories to sample names. Negative number to take from start of path. (INTEGER) │ +│ --fullnames -s Do not clean the sample names (leave as full file name) │ +│ --fn_as_s_name Use the log filename as the sample name │ +│ --replace-names TSV file to rename sample names during report generation (PATH) │ +╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Report customisation ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --title -i Report title. Printed as page header, used for filename if not otherwise specified. (TEXT) │ +│ --comment -b Custom comment, will be printed at the top of the report. (TEXT) │ +│ --template -t Report template to use. (default|gathered|geo|highcharts|sections|simple) │ +│ --sample-names TSV file containing alternative sample names for renaming buttons in the report (PATH) │ +│ --sample-filters TSV file containing show/hide patterns for the report (PATH) │ +│ --custom-css-file Custom CSS file to add to the final report (PATH) │ +╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Output files ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --flat -fp Use only flat plots (static images) │ +│ --interactive -ip Use only interactive plots (in-browser Javascript) │ +│ --export -p Export plots as static images in addition to the report │ +│ --data-dir Force the parsed data directory to be created. │ +│ --no-data-dir Prevent the parsed data directory from being created. │ +│ --data-format -k Output parsed data in a different format. (tsv|csv|json|yaml) │ +│ --zip-data-dir -z Compress the data directory. │ +│ --no-report Do not generate a report, only export data and plots │ +│ --pdf Creates PDF report with the 'simple' template. Requires Pandoc to be installed. │ +╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ MultiQC behaviour ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --verbose -v Increase output verbosity. (INTEGER RANGE) │ +│ --quiet -q Only show log warnings │ +│ --strict Don't catch exceptions, run additional code checks to help development. │ +│ --development,--dev Development mode. Do not compress and minimise JS, export uncompressed plot data │ +│ --require-logs Require all explicitly requested modules to have log files. If not, MultiQC will exit with an error. │ +│ --profile-runtime Add analysis of how long MultiQC takes to run to the report │ +│ --no-megaqc-upload Don't upload generated report to MegaQC, even if MegaQC options are found │ +│ --no-ansi Disable coloured log output │ +│ --version Show the version and exit. │ +│ --help -h Show this message and exit. │ +╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + + See http://multiqc.info for more details. \ No newline at end of file diff --git a/src/multiqc/script.sh b/src/multiqc/script.sh new file mode 100755 index 00000000..6353eb11 --- /dev/null +++ b/src/multiqc/script.sh @@ -0,0 +1,130 @@ +#!/bin/bash + +# disable flags +[[ "$par_ignore_symlinks" == "false" ]] && unset par_ignore_symlinks +[[ "$par_dirs" == "false" ]] && unset par_dirs +[[ "$par_full_names" == "false" ]] && unset par_full_names +[[ "$par_fn_as_s_name" == "false" ]] && unset par_fn_as_s_name +[[ "$par_profile_runtime" == "false" ]] && unset par_profile_runtime +[[ "$par_verbose" == "false" ]] && unset par_verbose +[[ "$par_quiet" == "false" ]] && unset par_quiet +[[ "$par_strict" == "false" ]] && unset par_strict +[[ "$par_development" == "false" ]] && unset par_development +[[ "$par_require_logs" == "false" ]] && unset par_require_logs +[[ "$par_no_megaqc_upload" == "false" ]] && unset par_no_megaqc_upload +[[ "$par_no_ansi" == "false" ]] && unset par_no_ansi +[[ "$par_flat" == "false" ]] && unset par_flat +[[ "$par_interactive" == "false" ]] && unset par_interactive +[[ "$par_static_plot_export" == "false" ]] && unset par_static_plot_export +[[ "$par_data_dir" == "false" ]] && unset par_data_dir +[[ "$par_no_data_dir" == "false" ]] && unset par_no_data_dir +[[ "$par_zip_data_dir" == "false" ]] && unset par_zip_data_dir +[[ "$par_pdf" == "false" ]] && unset par_pdf + + +# handle inputs +out_dir=$(dirname "$par_output_report") +output_report_file=$(basename "$par_output_report") +report_name="${output_report_file%.*}" + +# handle outputs +[[ -z "$par_output_report" ]] && no_report=true +[[ -z "$par_output_data" ]] && no_data_dir=true +[[ ! -z "$par_output_data" ]] && data_dir=true +[[ ! -z "$par_output_plots" ]] && export=true + +# handle multiples +IFS=";" read -ra inputs <<< $par_input + +if [[ -n "$par_include_modules" ]]; then + include_modules="" + IFS="," read -ra incl_modules <<< $par_include_modules + for i in "${incl_modules[@]}"; do + include_modules+="--include $i " + done + unset IFS +fi + +if [[ -n "$par_exclude_modules" ]]; then + exclude_modules="" + IFS="," read -ra excl_modules <<< $par_exclude_modules + for i in "${excl_modules[@]}"; do + exclude_modules+="--exclude $i" + done + unset IFS +fi + +if [[ -n "$par_ignore_analysis" ]]; then + ignore="" + IFS="," read -ra ignore_analysis <<< $par_ignore_analysis + for i in "${ignore_analysis[@]}"; do + ignore+="--ignore $i " + done + unset IFS +fi + +if [[ -n "$par_ignore_samples" ]]; then + ignore_samples="" + IFS="," read -ra ign_samples <<< $par_ignore_samples + for i in "${ign_samples[@]}"; do + ignore_samples+="--ignore-samples $i" + done + unset IFS +fi + +# run multiqc +multiqc \ + ${par_output_report:+--filename "$report_name"} \ + ${out_dir:+--outdir "$out_dir"} \ + ${no_report:+--no-report} \ + ${no_data_dir:+--no-data-dir} \ + ${data_dir:+--data-dir} \ + ${export:+--export} \ + ${par_title:+--title "$par_title"} \ + ${par_comment:+--comment "$par_comment"} \ + ${par_template:+--template "$par_template"} \ + ${par_sample_names:+--sample-names "$par_sample_names"} \ + ${par_sample_filters:+--sample-filters "$par_sample_filters"} \ + ${par_custom_css_file:+--custom-css-file "$par_custom_css_file"} \ + ${par_profile_runtime:+--profile-runtime} \ + ${par_dirs:+--dirs} \ + ${par_dirs_depth:+--dirs-depth "$par_dirs_depth"} \ + ${par_full_names:+--full-names} \ + ${par_fn_as_s_name:+--fn-as-s-name} \ + ${par_ignore_names:+--ignore-names "$par_ignore_names"} \ + ${par_ignore_symlinks:+--ignore-symlinks} \ + ${ignore_samples} \ + ${ignore} \ + ${exclude_modules} \ + ${include_modules} \ + ${par_include_modules:+--include-modules "$par_include_modules"} \ + ${par_data_format:+--data-format "$par_data_format"} \ + ${par_cl_config:+--cl-config "$par_cl_config"} \ + ${par_zip_data_dir:+--zip-data-dir} \ + ${par_pdf:+--pdf} \ + ${par_interactive:+--interactive} \ + ${par_flat:+--flat} \ + ${par_verbose:+--verbose} \ + ${par_quiet:+--quiet} \ + ${par_strict:+--strict} \ + ${par_no_megaqc_upload:+--no-megaqc-upload} \ + ${par_no_ansi:+--no-ansi} \ + ${par_profile_runtime:+--profile-runtime} \ + ${par_require_logs:+--require-logs} \ + ${par_development:+--development} \ + --force \ + "${inputs[@]}" + +# Move outputs + +if [[ -n "$par_output_data" ]] && [[ -d "${out_dir}/${report_name}_data" ]]; then + mv "${out_dir}/${report_name}_data" "$par_output_data" +elif [[ -n "$par_output_data" ]] && [[ ! -d "${out_dir}/${report_name}_data" ]]; then + echo "WARNING: Data could not be saved because data folder was not generated by multiqc. This could be due to filtering out of modules or samples." +fi + +if [[ -n "$par_output_plots" ]] && [[ -d "${out_dir}/${report_name}_plots" ]]; then + mv "${out_dir}/${report_name}_plots" "$par_output_plots" +elif [[ -n "$par_output_plots" ]] && [[ ! -d "${out_dir}/${report_name}_plots" ]]; then + echo "WARNING: Plots could not be saved because plots folder was not generated by multiqc. This could be due to filtering out of modules or samples." +fi diff --git a/src/multiqc/test.sh b/src/multiqc/test.sh new file mode 100644 index 00000000..a2844f54 --- /dev/null +++ b/src/multiqc/test.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +echo ">>> Testing input/output handling" + +"$meta_executable" \ + --input "$meta_resources_dir/test_data/" \ + --output_report test1.html \ + --output_data data1 \ + --output_plots plots1 \ + --quiet + +[ ! -f test1.html ] && echo "MultiQC report does not exist!" && exit 1 +[ ! -d data1 ] && echo "MultiQC data directory does not exist!" && exit 1 +[ ! -d plots1 ] && echo "MultiQC plots directory does not exist!" && exit 1 + +echo ">>> Testing module exclusion" + +"$meta_executable" \ + --input "$meta_resources_dir/test_data/" \ + --output_report test2.html \ + --output_data data2 \ + --output_plots plots2 \ + --exclude_modules samtools \ + --quiet + +[ -f test2.html ] && echo "MultiQC report should not exist!" && exit 1 +[ -d data2 ] && echo "MultiQC data directory should not exist!" && exit 1 +[ -d plots2 ] && echo "MultiQC plots directory should not exist!" && exit 1 + +echo ">>> Testing sample exclusion" + +"$meta_executable" \ + --input "$meta_resources_dir/test_data/" \ + --output_report test3.html \ + --output_data data3 \ + --ignore_samples a \ + --quiet + +key_to_check=".report_general_stats_data[0].a" +json_file="data3/multiqc_data.json" +[[ $(jq -r "$key_to_check" "$json_file") != null ]] && echo "$key_to_check should not be present in $json_file" && exit 1 + +echo "All tests succeeded!" +exit 0 \ No newline at end of file diff --git a/src/multiqc/test_data/a.txt b/src/multiqc/test_data/a.txt new file mode 100644 index 00000000..1be51d70 --- /dev/null +++ b/src/multiqc/test_data/a.txt @@ -0,0 +1,1504 @@ +# This file was produced by samtools stats (1.3+htslib-1.3) and can be plotted using plot-bamstats +# This file contains statistics for all reads. +# The command line was: stats mapped/a.bam +# CHK, Checksum [2]Read Names [3]Sequences [4]Qualities +# CHK, CRC32 of reads which passed filtering followed by addition (32bit overflow) +CHK db35d7d5 ec933459 1f587026 +# Summary Numbers. Use `grep ^SN | cut -f 2-` to extract this part. +SN raw total sequences: 21838387 +SN filtered sequences: 0 +SN sequences: 21838387 +SN is sorted: 1 +SN 1st fragments: 21838387 +SN last fragments: 0 +SN reads mapped: 21231961 +SN reads mapped and paired: 0 # paired-end technology bit set + both mates mapped +SN reads unmapped: 606426 +SN reads properly paired: 0 # proper-pair bit set +SN reads paired: 0 # paired-end technology bit set +SN reads duplicated: 3096782 # PCR or optical duplicate bit set +SN reads MQ0: 4882153 # mapped and MQ=0 +SN reads QC failed: 0 +SN non-primary alignments: 0 +SN total length: 1090509989 # ignores clipping +SN bases mapped: 1060219802 # ignores clipping +SN bases mapped (cigar): 1060219787 # more accurate +SN bases trimmed: 0 +SN bases duplicated: 154717551 +SN mismatches: 6032903 # from NM fields +SN error rate: 5.690238e-03 # mismatches / bases mapped (cigar) +SN average length: 49 +SN maximum length: 50 +SN average quality: 37.0 +SN insert size average: 0.0 +SN insert size standard deviation: 0.0 +SN inward oriented pairs: 0 +SN outward oriented pairs: 0 +SN pairs with other orientation: 0 +SN pairs on different chromosomes: 0 +# First Fragment Qualitites. Use `grep ^FFQ | cut -f 2-` to extract this part. +# Columns correspond to qualities and rows to cycles. First column is the cycle number. +FFQ 1 0 0 51315 0 0 0 0 0 0 0 0 0 0 0 0 382312 0 0 0 0 0 0 178198 0 0 0 0 1338772 0 0 0 0 0 19887790 0 0 0 0 0 0 0 0 +FFQ 2 0 0 5115 0 0 0 0 0 0 0 0 0 0 0 0 275192 0 0 0 0 0 0 147400 0 0 0 0 1284560 0 0 0 0 0 20126120 0 0 0 0 0 0 0 0 +FFQ 3 0 0 5128 0 0 0 0 0 0 0 0 0 0 0 0 191502 0 0 0 0 0 0 150841 0 0 0 0 1285815 0 0 0 0 0 20205101 0 0 0 0 0 0 0 0 +FFQ 4 0 0 5145 0 0 0 0 0 0 0 0 0 0 0 0 146549 0 0 0 0 0 0 39295 0 0 0 0 471059 0 0 0 0 0 1708658 0 0 0 19467681 0 0 0 0 +FFQ 5 0 0 5171 0 0 0 0 0 0 0 0 0 0 0 0 182896 0 0 0 0 0 0 41859 0 0 0 0 541845 0 0 0 0 0 1799240 0 0 0 19267376 0 0 0 0 +FFQ 6 0 0 5207 0 0 0 4751 0 0 0 0 0 0 0 0 177726 0 0 0 0 0 0 50754 0 0 0 0 533328 0 0 0 0 0 1789862 0 0 0 19276759 0 0 0 0 +FFQ 7 0 0 5265 0 0 0 5303 0 0 0 0 0 0 0 0 189482 0 0 0 0 0 0 50374 0 0 0 0 545491 0 0 0 0 0 1801670 0 0 0 19240802 0 0 0 0 +FFQ 8 0 0 5321 0 0 0 5251 0 0 0 0 0 0 0 0 189946 0 0 0 0 0 0 50759 0 0 0 0 544729 0 0 0 0 0 1805074 0 0 0 19237307 0 0 0 0 +FFQ 9 0 0 5412 0 0 0 5572 0 0 0 0 0 0 0 0 194980 0 0 0 0 0 0 50487 0 0 0 0 546813 0 0 0 0 0 1809993 0 0 0 19225130 0 0 0 0 +FFQ 10 0 0 5522 0 0 0 5109 0 0 0 0 0 0 0 0 187331 0 0 0 0 0 0 52119 0 0 0 0 547170 0 0 0 0 0 1806426 0 0 0 19234710 0 0 0 0 +FFQ 11 0 0 5659 0 0 0 5437 0 0 0 0 0 0 0 0 188095 0 0 0 0 0 0 53798 0 0 0 0 550305 0 0 0 0 0 1815355 0 0 0 19219738 0 0 0 0 +FFQ 12 0 0 5842 0 0 0 5812 0 0 0 0 0 0 0 0 191987 0 0 0 0 0 0 54137 0 0 0 0 551697 0 0 0 0 0 1820134 0 0 0 19208778 0 0 0 0 +FFQ 13 0 0 6054 0 0 0 5991 0 0 0 0 0 0 0 0 190425 0 0 0 0 0 0 55749 0 0 0 0 554090 0 0 0 0 0 1824863 0 0 0 19201215 0 0 0 0 +FFQ 14 0 0 6305 0 0 0 6562 0 0 0 0 0 0 0 0 194459 0 0 0 0 0 0 56577 0 0 0 0 553776 0 0 0 0 0 1736245 0 0 0 6044499 0 0 13239964 0 +FFQ 15 0 0 6575 0 0 0 6948 0 0 0 0 0 0 0 0 200015 0 0 0 0 0 0 58113 0 0 0 0 558164 0 0 0 0 0 1741159 0 0 0 6049860 0 0 13217553 0 +FFQ 16 0 0 6884 0 0 0 7121 0 0 0 0 0 0 0 0 201645 0 0 0 0 0 0 59457 0 0 0 0 560449 0 0 0 0 0 1751983 0 0 0 6029168 0 0 13221680 0 +FFQ 17 0 0 7255 0 0 0 7367 0 0 0 0 0 0 0 0 199780 0 0 0 0 0 0 63085 0 0 0 0 565569 0 0 0 0 0 1761217 0 0 0 6046012 0 0 13188102 0 +FFQ 18 0 0 7720 0 0 0 8336 0 0 0 0 0 0 0 0 200938 0 0 0 0 0 0 65311 0 0 0 0 558465 0 0 0 0 0 1759662 0 0 0 6058023 0 0 13179932 0 +FFQ 19 0 0 8413 0 0 0 8308 0 0 0 0 0 0 0 0 201721 0 0 0 0 0 0 69098 0 0 0 0 563047 0 0 0 0 0 1769492 0 0 0 5699288 0 0 13519020 0 +FFQ 20 0 0 8973 0 0 0 9516 0 0 0 0 0 0 0 0 201161 0 0 0 0 0 0 74284 0 0 0 0 562610 0 0 0 0 0 1778241 0 0 0 5767457 0 0 13436145 0 +FFQ 21 0 0 9857 0 0 0 10500 0 0 0 0 0 0 0 0 200349 0 0 0 0 0 0 80072 0 0 0 0 557657 0 0 0 0 0 1780395 0 0 0 5847132 0 0 13352425 0 +FFQ 22 0 0 11063 0 0 0 12270 0 0 0 0 0 0 0 0 204602 0 0 0 0 0 0 89896 0 0 0 0 558198 0 0 0 0 0 1791483 0 0 0 5932912 0 0 13237963 0 +FFQ 23 0 0 12768 0 0 0 14297 0 0 0 0 0 0 0 0 207883 0 0 0 0 0 0 99992 0 0 0 0 556435 0 0 0 0 0 1802873 0 0 0 6033565 0 0 13110574 0 +FFQ 24 0 0 14914 0 0 0 16397 0 0 0 0 0 0 0 0 207240 0 0 0 0 0 0 111512 0 0 0 0 545672 0 0 0 0 0 1806750 0 0 0 6007638 0 0 13128264 0 +FFQ 25 0 0 17715 0 0 0 20138 0 0 0 0 0 0 0 0 214768 0 0 0 0 0 0 129032 0 0 0 0 540112 0 0 0 0 0 1819460 0 0 0 6047119 0 0 13050043 0 +FFQ 26 0 0 21240 0 0 0 34290 0 0 0 0 0 0 0 0 249730 0 0 0 0 0 0 144422 0 0 0 0 520807 0 0 0 0 0 1809886 0 0 0 6058279 0 0 12999733 0 +FFQ 27 0 0 24829 0 0 0 43086 0 0 0 0 0 0 0 0 259480 0 0 0 0 0 0 161601 0 0 0 0 518294 0 0 0 0 0 1824722 0 0 0 6047197 0 0 12959178 0 +FFQ 28 0 0 28496 0 0 0 52585 0 0 0 0 0 0 0 0 260264 0 0 0 0 0 0 176084 0 0 0 0 509295 0 0 0 0 0 1839906 0 0 0 6068273 0 0 12903484 0 +FFQ 29 0 0 32506 0 0 0 62389 0 0 0 0 0 0 0 0 254623 0 0 0 0 0 0 189495 0 0 0 0 499599 0 0 0 0 0 1852205 0 0 0 6080516 0 0 12867054 0 +FFQ 30 0 0 36854 0 0 0 73580 0 0 0 0 0 0 0 0 247860 0 0 0 0 0 0 200625 0 0 0 0 495018 0 0 0 0 0 1863132 0 0 0 6112314 0 0 12809004 0 +FFQ 31 0 0 41323 0 0 0 87468 0 0 0 0 0 0 0 0 242078 0 0 0 0 0 0 209555 0 0 0 0 491444 0 0 0 0 0 1867737 0 0 0 6132089 0 0 12766610 0 +FFQ 32 0 0 46286 0 0 0 98002 0 0 0 0 0 0 0 0 232699 0 0 0 0 0 0 213564 0 0 0 0 491302 0 0 0 0 0 1878843 0 0 0 6142018 0 0 12735496 0 +FFQ 33 0 0 51663 0 0 0 106786 0 0 0 0 0 0 0 0 221792 0 0 0 0 0 0 218240 0 0 0 0 491145 0 0 0 0 0 1877911 0 0 0 6158285 0 0 12712271 0 +FFQ 34 0 0 57443 0 0 0 119491 0 0 0 0 0 0 0 0 216326 0 0 0 0 0 0 222138 0 0 0 0 487486 0 0 0 0 0 1875659 0 0 0 6153206 0 0 12706214 0 +FFQ 35 0 0 63864 0 0 0 122385 0 0 0 0 0 0 0 0 210969 0 0 0 0 0 0 227897 0 0 0 0 488945 0 0 0 0 0 1888985 0 0 0 6136699 0 0 12698075 0 +FFQ 36 0 0 70925 0 0 0 127772 0 0 0 0 0 0 0 0 204278 0 0 0 0 0 0 232925 0 0 0 0 489984 0 0 0 0 0 1893912 0 0 0 6147628 0 0 12670239 0 +FFQ 37 0 0 78443 0 0 0 130513 0 0 0 0 0 0 0 0 201817 0 0 0 0 0 0 237205 0 0 0 0 489393 0 0 0 0 0 1901072 0 0 0 6157060 0 0 12641982 0 +FFQ 38 0 0 86727 0 0 0 134572 0 0 0 0 0 0 0 0 201643 0 0 0 0 0 0 245851 0 0 0 0 493135 0 0 0 0 0 1911564 0 0 0 6168370 0 0 12595418 0 +FFQ 39 0 0 96199 0 0 0 136517 0 0 0 0 0 0 0 0 202556 0 0 0 0 0 0 253424 0 0 0 0 496159 0 0 0 0 0 1926927 0 0 0 6191376 0 0 12533859 0 +FFQ 40 0 0 106625 0 0 0 141535 0 0 0 0 0 0 0 0 207572 0 0 0 0 0 0 263379 0 0 0 0 494281 0 0 0 0 0 1936713 0 0 0 6212472 0 0 12474092 0 +FFQ 41 0 0 118435 0 0 0 134585 0 0 0 0 0 0 0 0 207617 0 0 0 0 0 0 271430 0 0 0 0 497097 0 0 0 0 0 1952887 0 0 0 6236465 0 0 12417628 0 +FFQ 42 0 0 132662 0 0 0 134133 0 0 0 0 0 0 0 0 204434 0 0 0 0 0 0 279354 0 0 0 0 498810 0 0 0 0 0 1968372 0 0 0 6267796 0 0 12349794 0 +FFQ 43 0 0 147587 0 0 0 131817 0 0 0 0 0 0 0 0 204674 0 0 0 0 0 0 288492 0 0 0 0 497849 0 0 0 0 0 1975284 0 0 0 6290454 0 0 12298484 0 +FFQ 44 0 0 166442 0 0 0 128205 0 0 0 0 0 0 0 0 201990 0 0 0 0 0 0 298737 0 0 0 0 496519 0 0 0 0 0 1988815 0 0 0 6294537 0 0 12258940 0 +FFQ 45 0 0 190003 0 0 0 124333 0 0 0 0 0 0 0 0 196796 0 0 0 0 0 0 309371 0 0 0 0 499163 0 0 0 0 0 2001867 0 0 0 6259352 0 0 12252716 0 +FFQ 46 0 0 220981 0 0 0 116347 0 0 0 0 0 0 0 0 191467 0 0 0 0 0 0 316096 0 0 0 0 498894 0 0 0 0 0 2007430 0 0 0 6270683 0 0 12210477 0 +FFQ 47 0 0 258782 0 0 0 106166 0 0 0 0 0 0 0 0 185518 0 0 0 0 0 0 319187 0 0 0 0 496114 0 0 0 0 0 2015168 0 0 0 6276041 0 0 12156632 0 +FFQ 48 0 0 310568 0 0 0 93125 0 0 0 0 0 0 0 0 177253 0 0 0 0 0 0 323208 0 0 0 0 501414 0 0 0 0 0 2033190 0 0 0 6270044 0 0 12009645 0 +FFQ 49 0 0 374982 0 0 0 66673 0 0 0 0 0 0 0 0 165208 0 0 0 0 0 0 314721 0 0 0 0 492972 0 0 0 0 0 2005393 0 0 0 6174269 0 0 11627542 0 +FFQ 50 0 0 468205 0 0 0 30279 0 0 0 0 0 0 0 0 157265 0 0 0 0 0 0 309324 0 0 0 0 497388 0 0 0 0 0 2038361 0 0 0 6218900 0 0 11502038 0 +FFQ 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +# Last Fragment Qualitites. Use `grep ^LFQ | cut -f 2-` to extract this part. +# Columns correspond to qualities and rows to cycles. First column is the cycle number. +LFQ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 37 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 38 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 41 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 44 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 45 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 46 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 49 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +# GC Content of first fragments. Use `grep ^GCF | cut -f 2-` to extract this part. +GCF 0.75 865 +GCF 1.76 1418 +GCF 2.76 1432 +GCF 3.77 2552 +GCF 4.27 2584 +GCF 5.03 2582 +GCF 5.78 4188 +GCF 6.78 4242 +GCF 7.79 6900 +GCF 8.79 6990 +GCF 9.80 12077 +GCF 10.30 12202 +GCF 11.06 12248 +GCF 11.81 20918 +GCF 12.31 21159 +GCF 13.07 21223 +GCF 13.82 35416 +GCF 14.32 35417 +GCF 14.82 35899 +GCF 15.33 35912 +GCF 16.08 58403 +GCF 16.83 59262 +GCF 17.34 59292 +GCF 17.84 92651 +GCF 18.34 92652 +GCF 18.84 93563 +GCF 19.35 93799 +GCF 20.10 141217 +GCF 20.85 142627 +GCF 21.36 143006 +GCF 21.86 205422 +GCF 22.36 205429 +GCF 22.86 207354 +GCF 23.37 207906 +GCF 23.87 290147 +GCF 24.37 290170 +GCF 24.87 292864 +GCF 25.38 293730 +GCF 25.88 405518 +GCF 26.38 405534 +GCF 26.88 408551 +GCF 27.39 408580 +GCF 27.89 570253 +GCF 28.39 570499 +GCF 28.89 570545 +GCF 29.40 575537 +GCF 29.90 829159 +GCF 30.40 829379 +GCF 30.90 829405 +GCF 31.41 836277 +GCF 31.91 1143374 +GCF 32.66 1143659 +GCF 33.42 1146746 +GCF 33.92 1501224 +GCF 34.42 1501256 +GCF 34.92 1501488 +GCF 35.43 1507360 +GCF 35.93 1778503 +GCF 36.43 1778542 +GCF 36.93 1778582 +GCF 37.44 1783301 +GCF 37.94 1767410 +GCF 38.44 1767928 +GCF 38.94 1768071 +GCF 39.45 1769688 +GCF 39.95 1656761 +GCF 40.45 1657229 +GCF 40.95 1657212 +GCF 41.46 1658172 +GCF 41.96 1509858 +GCF 42.46 1509735 +GCF 42.96 1509702 +GCF 43.47 1509894 +GCF 43.97 1444340 +GCF 44.47 1444607 +GCF 44.97 1444566 +GCF 45.48 1444635 +GCF 45.98 1405845 +GCF 46.48 1405793 +GCF 46.98 1405225 +GCF 47.49 1405190 +GCF 47.99 1360256 +GCF 48.49 1360229 +GCF 49.25 1359649 +GCF 50.25 1272600 +GCF 51.01 1271785 +GCF 51.51 1271790 +GCF 52.01 1115634 +GCF 52.51 1115609 +GCF 53.02 1114429 +GCF 53.52 1114416 +GCF 54.02 921342 +GCF 54.52 921028 +GCF 55.03 921029 +GCF 55.53 919941 +GCF 56.03 716231 +GCF 56.78 715994 +GCF 57.54 715097 +GCF 58.04 532571 +GCF 58.54 527473 +GCF 59.05 527469 +GCF 59.55 526587 +GCF 60.05 376831 +GCF 60.55 372736 +GCF 61.06 372536 +GCF 61.56 371968 +GCF 62.06 252936 +GCF 62.56 249647 +GCF 63.07 249528 +GCF 63.57 249527 +GCF 64.07 163481 +GCF 64.57 161228 +GCF 65.08 161158 +GCF 65.58 161154 +GCF 66.08 101911 +GCF 66.83 100650 +GCF 67.59 100609 +GCF 68.09 60847 +GCF 68.59 59897 +GCF 69.10 59890 +GCF 69.60 59865 +GCF 70.10 36290 +GCF 70.60 35728 +GCF 71.11 35725 +GCF 71.61 35692 +GCF 72.36 23067 +GCF 73.12 22797 +GCF 73.62 22799 +GCF 74.12 15342 +GCF 74.62 15317 +GCF 75.13 15185 +GCF 75.63 15186 +GCF 76.13 10477 +GCF 76.63 10474 +GCF 77.14 10363 +GCF 77.64 10359 +GCF 78.14 7327 +GCF 78.64 7313 +GCF 79.40 7302 +GCF 80.15 5084 +GCF 80.65 5086 +GCF 81.16 5049 +GCF 81.66 5047 +GCF 82.16 3472 +GCF 82.66 3470 +GCF 83.42 3464 +GCF 84.17 2302 +GCF 84.67 2303 +GCF 85.43 2287 +GCF 86.18 1488 +GCF 86.68 1487 +GCF 87.19 1486 +GCF 87.69 1480 +GCF 88.44 841 +GCF 89.20 842 +GCF 89.70 837 +GCF 90.70 472 +GCF 91.71 471 +GCF 92.71 248 +GCF 93.72 244 +GCF 94.97 126 +GCF 96.98 54 +GCF 98.99 21 +# GC Content of last fragments. Use `grep ^GCL | cut -f 2-` to extract this part. +# ACGT content per cycle. Use `grep ^GCC | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%]; and N and O counts as a percentage of all A/C/G/T bases [%] +GCC 1 27.19 22.69 22.08 28.04 0.21 0.00 +GCC 2 27.70 22.16 21.57 28.57 0.00 0.00 +GCC 3 29.07 20.57 19.82 30.54 0.00 0.00 +GCC 4 28.81 20.73 20.29 30.17 0.00 0.00 +GCC 5 29.23 20.17 19.74 30.86 0.00 0.00 +GCC 6 29.00 20.47 19.91 30.62 0.00 0.00 +GCC 7 28.88 20.46 19.88 30.78 0.00 0.00 +GCC 8 28.85 20.74 20.02 30.39 0.00 0.00 +GCC 9 28.51 21.15 20.49 29.85 0.00 0.00 +GCC 10 28.05 21.54 20.95 29.46 0.00 0.00 +GCC 11 28.15 21.56 20.88 29.41 0.00 0.00 +GCC 12 28.32 21.33 20.59 29.77 0.00 0.00 +GCC 13 28.46 21.18 20.45 29.91 0.00 0.00 +GCC 14 28.36 21.04 20.35 30.25 0.00 0.00 +GCC 15 28.47 20.86 20.15 30.51 0.00 0.00 +GCC 16 28.29 21.14 20.36 30.21 0.00 0.00 +GCC 17 28.23 21.10 20.54 30.12 0.00 0.00 +GCC 18 28.35 20.96 20.66 30.02 0.00 0.00 +GCC 19 28.06 21.42 21.18 29.34 0.00 0.00 +GCC 20 28.15 21.29 21.01 29.55 0.00 0.00 +GCC 21 28.09 21.28 21.33 29.31 0.00 0.00 +GCC 22 28.07 21.26 21.17 29.51 0.00 0.00 +GCC 23 28.22 21.06 20.72 30.01 0.00 0.00 +GCC 24 28.13 21.20 20.87 29.80 0.00 0.00 +GCC 25 28.32 20.89 20.58 30.21 0.00 0.00 +GCC 26 28.13 21.20 20.74 29.92 0.00 0.00 +GCC 27 27.96 21.34 20.88 29.81 0.00 0.00 +GCC 28 28.06 21.17 20.89 29.88 0.00 0.00 +GCC 29 27.79 21.60 21.33 29.29 0.00 0.00 +GCC 30 27.84 21.56 21.24 29.35 0.00 0.00 +GCC 31 27.84 21.59 21.14 29.43 0.00 0.00 +GCC 32 27.82 21.68 21.19 29.31 0.00 0.00 +GCC 33 28.14 21.20 20.82 29.84 0.00 0.00 +GCC 34 28.00 21.30 20.78 29.92 0.00 0.00 +GCC 35 28.14 21.19 20.64 30.03 0.00 0.00 +GCC 36 28.22 21.16 20.67 29.95 0.00 0.00 +GCC 37 28.12 21.36 20.65 29.87 0.00 0.00 +GCC 38 28.15 21.52 20.76 29.58 0.00 0.00 +GCC 39 27.94 21.78 21.08 29.19 0.00 0.00 +GCC 40 27.79 21.80 21.18 29.23 0.00 0.00 +GCC 41 27.65 21.89 21.45 29.01 0.00 0.00 +GCC 42 27.94 21.65 21.15 29.26 0.01 0.00 +GCC 43 27.90 21.51 20.95 29.65 0.00 0.00 +GCC 44 27.87 21.68 21.03 29.42 0.00 0.00 +GCC 45 28.27 21.39 20.69 29.66 0.00 0.00 +GCC 46 28.12 21.20 20.68 30.00 0.01 0.00 +GCC 47 28.06 21.43 21.00 29.51 0.00 0.00 +GCC 48 28.08 21.47 20.96 29.48 0.00 0.00 +GCC 49 27.53 21.94 21.68 28.85 0.02 0.00 +GCC 50 28.35 21.19 20.97 29.49 0.00 0.00 +# Insert sizes. Use `grep ^IS | cut -f 2-` to extract this part. The columns are: insert size, pairs total, inward oriented pairs, outward oriented pairs, other pairs +# Read lengths. Use `grep ^RL | cut -f 2-` to extract this part. The columns are: read length, count +RL 30 83 +RL 31 94 +RL 32 117 +RL 33 130 +RL 34 144 +RL 35 156 +RL 36 178 +RL 37 205 +RL 38 263 +RL 39 348 +RL 40 525 +RL 41 789 +RL 42 714 +RL 43 456 +RL 44 584 +RL 45 1226 +RL 46 18767 +RL 47 95161 +RL 48 496687 +RL 50 21221760 +# Indel distribution. Use `grep ^ID | cut -f 2-` to extract this part. The columns are: length, number of insertions, number of deletions +ID 1 64125 118566 +ID 2 11857 15129 +ID 3 1458 1538 +# Indels per cycle. Use `grep ^IC | cut -f 2-` to extract this part. The columns are: cycle, number of insertions (fwd), .. (rev) , number of deletions (fwd), .. (rev) +IC 1 0 0 0 1 +IC 2 0 203 0 212 +IC 3 0 552 0 878 +IC 4 0 892 0 1911 +IC 5 0 1594 0 2256 +IC 6 0 1567 0 2393 +IC 7 0 1533 0 2675 +IC 8 0 1585 0 2765 +IC 9 0 1556 0 2857 +IC 10 0 1643 0 3115 +IC 11 0 1682 0 2944 +IC 12 0 1707 0 3030 +IC 13 0 1811 0 3124 +IC 14 0 1723 0 3116 +IC 15 0 1838 0 3290 +IC 16 0 1687 0 3202 +IC 17 0 1841 0 3250 +IC 18 0 1850 0 3390 +IC 19 0 1873 0 3484 +IC 20 0 1785 0 3497 +IC 21 0 1763 0 3457 +IC 22 0 1782 0 3406 +IC 23 0 1841 0 3383 +IC 24 0 1828 0 3277 +IC 25 0 1809 0 3322 +IC 26 0 1830 0 3369 +IC 27 0 1890 0 3294 +IC 28 0 1825 0 3385 +IC 29 0 1864 0 3340 +IC 30 0 1899 0 3649 +IC 31 0 1958 0 3771 +IC 32 0 2052 0 3555 +IC 33 0 2086 0 3636 +IC 34 0 1962 0 3649 +IC 35 0 1945 0 3550 +IC 36 0 1972 0 3423 +IC 37 0 1836 0 3536 +IC 38 0 1845 0 3515 +IC 39 0 1883 0 3451 +IC 40 0 1806 0 3300 +IC 41 0 1850 0 3304 +IC 42 0 1710 0 3148 +IC 43 0 1825 0 2849 +IC 44 0 1639 0 2612 +IC 45 0 1708 0 1439 +IC 46 0 1072 0 765 +IC 47 0 659 0 340 +IC 48 0 288 0 118 +IC 49 0 91 0 0 +# Coverage distribution. Use `grep ^COV | cut -f 2-` to extract this part. +COV [1-1] 1 609977704 +COV [2-2] 2 115063033 +COV [3-3] 3 16119927 +COV [4-4] 4 2001769 +COV [5-5] 5 294243 +COV [6-6] 6 82570 +COV [7-7] 7 41437 +COV [8-8] 8 27141 +COV [9-9] 9 19572 +COV [10-10] 10 15755 +COV [11-11] 11 12467 +COV [12-12] 12 10784 +COV [13-13] 13 8648 +COV [14-14] 14 7758 +COV [15-15] 15 6163 +COV [16-16] 16 5267 +COV [17-17] 17 4349 +COV [18-18] 18 3615 +COV [19-19] 19 3296 +COV [20-20] 20 2835 +COV [21-21] 21 2708 +COV [22-22] 22 2026 +COV [23-23] 23 1585 +COV [24-24] 24 1605 +COV [25-25] 25 1606 +COV [26-26] 26 1387 +COV [27-27] 27 1529 +COV [28-28] 28 1270 +COV [29-29] 29 1185 +COV [30-30] 30 1228 +COV [31-31] 31 1101 +COV [32-32] 32 1125 +COV [33-33] 33 1032 +COV [34-34] 34 935 +COV [35-35] 35 880 +COV [36-36] 36 985 +COV [37-37] 37 1009 +COV [38-38] 38 915 +COV [39-39] 39 838 +COV [40-40] 40 828 +COV [41-41] 41 837 +COV [42-42] 42 894 +COV [43-43] 43 903 +COV [44-44] 44 902 +COV [45-45] 45 780 +COV [46-46] 46 796 +COV [47-47] 47 854 +COV [48-48] 48 804 +COV [49-49] 49 755 +COV [50-50] 50 728 +COV [51-51] 51 753 +COV [52-52] 52 834 +COV [53-53] 53 797 +COV [54-54] 54 806 +COV [55-55] 55 748 +COV [56-56] 56 815 +COV [57-57] 57 814 +COV [58-58] 58 775 +COV [59-59] 59 779 +COV [60-60] 60 744 +COV [61-61] 61 787 +COV [62-62] 62 809 +COV [63-63] 63 788 +COV [64-64] 64 748 +COV [65-65] 65 712 +COV [66-66] 66 614 +COV [67-67] 67 631 +COV [68-68] 68 717 +COV [69-69] 69 660 +COV [70-70] 70 591 +COV [71-71] 71 559 +COV [72-72] 72 666 +COV [73-73] 73 535 +COV [74-74] 74 565 +COV [75-75] 75 526 +COV [76-76] 76 514 +COV [77-77] 77 504 +COV [78-78] 78 493 +COV [79-79] 79 452 +COV [80-80] 80 422 +COV [81-81] 81 511 +COV [82-82] 82 494 +COV [83-83] 83 445 +COV [84-84] 84 495 +COV [85-85] 85 412 +COV [86-86] 86 495 +COV [87-87] 87 447 +COV [88-88] 88 469 +COV [89-89] 89 451 +COV [90-90] 90 522 +COV [91-91] 91 471 +COV [92-92] 92 464 +COV [93-93] 93 476 +COV [94-94] 94 520 +COV [95-95] 95 497 +COV [96-96] 96 445 +COV [97-97] 97 494 +COV [98-98] 98 502 +COV [99-99] 99 490 +COV [100-100] 100 461 +COV [101-101] 101 527 +COV [102-102] 102 533 +COV [103-103] 103 515 +COV [104-104] 104 611 +COV [105-105] 105 471 +COV [106-106] 106 492 +COV [107-107] 107 445 +COV [108-108] 108 467 +COV [109-109] 109 455 +COV [110-110] 110 393 +COV [111-111] 111 394 +COV [112-112] 112 370 +COV [113-113] 113 344 +COV [114-114] 114 324 +COV [115-115] 115 308 +COV [116-116] 116 332 +COV [117-117] 117 272 +COV [118-118] 118 248 +COV [119-119] 119 220 +COV [120-120] 120 308 +COV [121-121] 121 281 +COV [122-122] 122 313 +COV [123-123] 123 259 +COV [124-124] 124 222 +COV [125-125] 125 189 +COV [126-126] 126 219 +COV [127-127] 127 194 +COV [128-128] 128 188 +COV [129-129] 129 181 +COV [130-130] 130 223 +COV [131-131] 131 200 +COV [132-132] 132 180 +COV [133-133] 133 167 +COV [134-134] 134 168 +COV [135-135] 135 153 +COV [136-136] 136 179 +COV [137-137] 137 183 +COV [138-138] 138 175 +COV [139-139] 139 177 +COV [140-140] 140 168 +COV [141-141] 141 180 +COV [142-142] 142 197 +COV [143-143] 143 194 +COV [144-144] 144 170 +COV [145-145] 145 172 +COV [146-146] 146 149 +COV [147-147] 147 173 +COV [148-148] 148 184 +COV [149-149] 149 172 +COV [150-150] 150 168 +COV [151-151] 151 187 +COV [152-152] 152 187 +COV [153-153] 153 174 +COV [154-154] 154 134 +COV [155-155] 155 179 +COV [156-156] 156 153 +COV [157-157] 157 160 +COV [158-158] 158 160 +COV [159-159] 159 171 +COV [160-160] 160 163 +COV [161-161] 161 168 +COV [162-162] 162 183 +COV [163-163] 163 175 +COV [164-164] 164 183 +COV [165-165] 165 194 +COV [166-166] 166 180 +COV [167-167] 167 174 +COV [168-168] 168 155 +COV [169-169] 169 173 +COV [170-170] 170 169 +COV [171-171] 171 199 +COV [172-172] 172 190 +COV [173-173] 173 231 +COV [174-174] 174 221 +COV [175-175] 175 235 +COV [176-176] 176 216 +COV [177-177] 177 215 +COV [178-178] 178 210 +COV [179-179] 179 226 +COV [180-180] 180 207 +COV [181-181] 181 232 +COV [182-182] 182 232 +COV [183-183] 183 214 +COV [184-184] 184 210 +COV [185-185] 185 231 +COV [186-186] 186 191 +COV [187-187] 187 216 +COV [188-188] 188 210 +COV [189-189] 189 218 +COV [190-190] 190 260 +COV [191-191] 191 240 +COV [192-192] 192 239 +COV [193-193] 193 252 +COV [194-194] 194 268 +COV [195-195] 195 237 +COV [196-196] 196 256 +COV [197-197] 197 254 +COV [198-198] 198 266 +COV [199-199] 199 297 +COV [200-200] 200 255 +COV [201-201] 201 285 +COV [202-202] 202 259 +COV [203-203] 203 249 +COV [204-204] 204 238 +COV [205-205] 205 247 +COV [206-206] 206 261 +COV [207-207] 207 244 +COV [208-208] 208 241 +COV [209-209] 209 237 +COV [210-210] 210 271 +COV [211-211] 211 245 +COV [212-212] 212 234 +COV [213-213] 213 252 +COV [214-214] 214 241 +COV [215-215] 215 215 +COV [216-216] 216 227 +COV [217-217] 217 212 +COV [218-218] 218 230 +COV [219-219] 219 184 +COV [220-220] 220 213 +COV [221-221] 221 225 +COV [222-222] 222 188 +COV [223-223] 223 214 +COV [224-224] 224 198 +COV [225-225] 225 195 +COV [226-226] 226 192 +COV [227-227] 227 191 +COV [228-228] 228 184 +COV [229-229] 229 182 +COV [230-230] 230 177 +COV [231-231] 231 169 +COV [232-232] 232 174 +COV [233-233] 233 152 +COV [234-234] 234 154 +COV [235-235] 235 132 +COV [236-236] 236 130 +COV [237-237] 237 146 +COV [238-238] 238 130 +COV [239-239] 239 103 +COV [240-240] 240 161 +COV [241-241] 241 120 +COV [242-242] 242 117 +COV [243-243] 243 132 +COV [244-244] 244 134 +COV [245-245] 245 140 +COV [246-246] 246 123 +COV [247-247] 247 126 +COV [248-248] 248 126 +COV [249-249] 249 99 +COV [250-250] 250 87 +COV [251-251] 251 103 +COV [252-252] 252 109 +COV [253-253] 253 101 +COV [254-254] 254 91 +COV [255-255] 255 72 +COV [256-256] 256 64 +COV [257-257] 257 88 +COV [258-258] 258 86 +COV [259-259] 259 87 +COV [260-260] 260 79 +COV [261-261] 261 67 +COV [262-262] 262 76 +COV [263-263] 263 89 +COV [264-264] 264 64 +COV [265-265] 265 52 +COV [266-266] 266 64 +COV [267-267] 267 69 +COV [268-268] 268 64 +COV [269-269] 269 60 +COV [270-270] 270 55 +COV [271-271] 271 44 +COV [272-272] 272 58 +COV [273-273] 273 63 +COV [274-274] 274 63 +COV [275-275] 275 75 +COV [276-276] 276 81 +COV [277-277] 277 81 +COV [278-278] 278 74 +COV [279-279] 279 44 +COV [280-280] 280 78 +COV [281-281] 281 78 +COV [282-282] 282 81 +COV [283-283] 283 75 +COV [284-284] 284 100 +COV [285-285] 285 50 +COV [286-286] 286 57 +COV [287-287] 287 52 +COV [288-288] 288 54 +COV [289-289] 289 65 +COV [290-290] 290 67 +COV [291-291] 291 58 +COV [292-292] 292 56 +COV [293-293] 293 48 +COV [294-294] 294 61 +COV [295-295] 295 51 +COV [296-296] 296 55 +COV [297-297] 297 60 +COV [298-298] 298 50 +COV [299-299] 299 47 +COV [300-300] 300 62 +COV [301-301] 301 56 +COV [302-302] 302 62 +COV [303-303] 303 49 +COV [304-304] 304 66 +COV [305-305] 305 64 +COV [306-306] 306 61 +COV [307-307] 307 53 +COV [308-308] 308 51 +COV [309-309] 309 68 +COV [310-310] 310 53 +COV [311-311] 311 60 +COV [312-312] 312 73 +COV [313-313] 313 61 +COV [314-314] 314 70 +COV [315-315] 315 47 +COV [316-316] 316 44 +COV [317-317] 317 51 +COV [318-318] 318 52 +COV [319-319] 319 56 +COV [320-320] 320 60 +COV [321-321] 321 56 +COV [322-322] 322 55 +COV [323-323] 323 51 +COV [324-324] 324 60 +COV [325-325] 325 60 +COV [326-326] 326 65 +COV [327-327] 327 75 +COV [328-328] 328 52 +COV [329-329] 329 63 +COV [330-330] 330 59 +COV [331-331] 331 50 +COV [332-332] 332 51 +COV [333-333] 333 57 +COV [334-334] 334 44 +COV [335-335] 335 32 +COV [336-336] 336 62 +COV [337-337] 337 47 +COV [338-338] 338 69 +COV [339-339] 339 64 +COV [340-340] 340 51 +COV [341-341] 341 56 +COV [342-342] 342 51 +COV [343-343] 343 49 +COV [344-344] 344 72 +COV [345-345] 345 61 +COV [346-346] 346 51 +COV [347-347] 347 76 +COV [348-348] 348 75 +COV [349-349] 349 55 +COV [350-350] 350 48 +COV [351-351] 351 52 +COV [352-352] 352 68 +COV [353-353] 353 47 +COV [354-354] 354 59 +COV [355-355] 355 52 +COV [356-356] 356 51 +COV [357-357] 357 59 +COV [358-358] 358 57 +COV [359-359] 359 69 +COV [360-360] 360 59 +COV [361-361] 361 80 +COV [362-362] 362 42 +COV [363-363] 363 46 +COV [364-364] 364 65 +COV [365-365] 365 60 +COV [366-366] 366 45 +COV [367-367] 367 48 +COV [368-368] 368 39 +COV [369-369] 369 48 +COV [370-370] 370 44 +COV [371-371] 371 49 +COV [372-372] 372 61 +COV [373-373] 373 48 +COV [374-374] 374 45 +COV [375-375] 375 55 +COV [376-376] 376 54 +COV [377-377] 377 67 +COV [378-378] 378 53 +COV [379-379] 379 65 +COV [380-380] 380 49 +COV [381-381] 381 61 +COV [382-382] 382 48 +COV [383-383] 383 49 +COV [384-384] 384 49 +COV [385-385] 385 45 +COV [386-386] 386 40 +COV [387-387] 387 38 +COV [388-388] 388 41 +COV [389-389] 389 46 +COV [390-390] 390 42 +COV [391-391] 391 53 +COV [392-392] 392 38 +COV [393-393] 393 36 +COV [394-394] 394 37 +COV [395-395] 395 40 +COV [396-396] 396 43 +COV [397-397] 397 24 +COV [398-398] 398 54 +COV [399-399] 399 36 +COV [400-400] 400 40 +COV [401-401] 401 28 +COV [402-402] 402 52 +COV [403-403] 403 70 +COV [404-404] 404 52 +COV [405-405] 405 49 +COV [406-406] 406 42 +COV [407-407] 407 37 +COV [408-408] 408 41 +COV [409-409] 409 61 +COV [410-410] 410 44 +COV [411-411] 411 36 +COV [412-412] 412 58 +COV [413-413] 413 37 +COV [414-414] 414 53 +COV [415-415] 415 33 +COV [416-416] 416 44 +COV [417-417] 417 35 +COV [418-418] 418 40 +COV [419-419] 419 43 +COV [420-420] 420 48 +COV [421-421] 421 58 +COV [422-422] 422 53 +COV [423-423] 423 44 +COV [424-424] 424 55 +COV [425-425] 425 62 +COV [426-426] 426 49 +COV [427-427] 427 43 +COV [428-428] 428 31 +COV [429-429] 429 47 +COV [430-430] 430 54 +COV [431-431] 431 43 +COV [432-432] 432 57 +COV [433-433] 433 33 +COV [434-434] 434 50 +COV [435-435] 435 49 +COV [436-436] 436 32 +COV [437-437] 437 49 +COV [438-438] 438 40 +COV [439-439] 439 44 +COV [440-440] 440 42 +COV [441-441] 441 43 +COV [442-442] 442 42 +COV [443-443] 443 50 +COV [444-444] 444 36 +COV [445-445] 445 29 +COV [446-446] 446 46 +COV [447-447] 447 34 +COV [448-448] 448 40 +COV [449-449] 449 40 +COV [450-450] 450 46 +COV [451-451] 451 28 +COV [452-452] 452 37 +COV [453-453] 453 42 +COV [454-454] 454 42 +COV [455-455] 455 44 +COV [456-456] 456 38 +COV [457-457] 457 42 +COV [458-458] 458 42 +COV [459-459] 459 38 +COV [460-460] 460 45 +COV [461-461] 461 34 +COV [462-462] 462 38 +COV [463-463] 463 36 +COV [464-464] 464 44 +COV [465-465] 465 43 +COV [466-466] 466 44 +COV [467-467] 467 31 +COV [468-468] 468 58 +COV [469-469] 469 44 +COV [470-470] 470 57 +COV [471-471] 471 39 +COV [472-472] 472 37 +COV [473-473] 473 42 +COV [474-474] 474 38 +COV [475-475] 475 41 +COV [476-476] 476 40 +COV [477-477] 477 41 +COV [478-478] 478 44 +COV [479-479] 479 40 +COV [480-480] 480 48 +COV [481-481] 481 34 +COV [482-482] 482 40 +COV [483-483] 483 41 +COV [484-484] 484 45 +COV [485-485] 485 44 +COV [486-486] 486 48 +COV [487-487] 487 32 +COV [488-488] 488 44 +COV [489-489] 489 38 +COV [490-490] 490 22 +COV [491-491] 491 32 +COV [492-492] 492 45 +COV [493-493] 493 28 +COV [494-494] 494 33 +COV [495-495] 495 39 +COV [496-496] 496 46 +COV [497-497] 497 32 +COV [498-498] 498 34 +COV [499-499] 499 30 +COV [500-500] 500 30 +COV [501-501] 501 31 +COV [502-502] 502 39 +COV [503-503] 503 44 +COV [504-504] 504 30 +COV [505-505] 505 34 +COV [506-506] 506 25 +COV [507-507] 507 48 +COV [508-508] 508 35 +COV [509-509] 509 41 +COV [510-510] 510 38 +COV [511-511] 511 36 +COV [512-512] 512 45 +COV [513-513] 513 26 +COV [514-514] 514 34 +COV [515-515] 515 35 +COV [516-516] 516 36 +COV [517-517] 517 29 +COV [518-518] 518 28 +COV [519-519] 519 31 +COV [520-520] 520 34 +COV [521-521] 521 47 +COV [522-522] 522 35 +COV [523-523] 523 44 +COV [524-524] 524 58 +COV [525-525] 525 26 +COV [526-526] 526 40 +COV [527-527] 527 36 +COV [528-528] 528 37 +COV [529-529] 529 55 +COV [530-530] 530 37 +COV [531-531] 531 32 +COV [532-532] 532 33 +COV [533-533] 533 39 +COV [534-534] 534 34 +COV [535-535] 535 26 +COV [536-536] 536 42 +COV [537-537] 537 25 +COV [538-538] 538 40 +COV [539-539] 539 38 +COV [540-540] 540 35 +COV [541-541] 541 31 +COV [542-542] 542 33 +COV [543-543] 543 38 +COV [544-544] 544 36 +COV [545-545] 545 39 +COV [546-546] 546 35 +COV [547-547] 547 36 +COV [548-548] 548 41 +COV [549-549] 549 38 +COV [550-550] 550 30 +COV [551-551] 551 33 +COV [552-552] 552 40 +COV [553-553] 553 33 +COV [554-554] 554 30 +COV [555-555] 555 41 +COV [556-556] 556 31 +COV [557-557] 557 37 +COV [558-558] 558 41 +COV [559-559] 559 26 +COV [560-560] 560 30 +COV [561-561] 561 35 +COV [562-562] 562 35 +COV [563-563] 563 34 +COV [564-564] 564 35 +COV [565-565] 565 39 +COV [566-566] 566 29 +COV [567-567] 567 41 +COV [568-568] 568 29 +COV [569-569] 569 27 +COV [570-570] 570 40 +COV [571-571] 571 32 +COV [572-572] 572 30 +COV [573-573] 573 25 +COV [574-574] 574 35 +COV [575-575] 575 30 +COV [576-576] 576 28 +COV [577-577] 577 34 +COV [578-578] 578 21 +COV [579-579] 579 31 +COV [580-580] 580 34 +COV [581-581] 581 18 +COV [582-582] 582 31 +COV [583-583] 583 24 +COV [584-584] 584 30 +COV [585-585] 585 31 +COV [586-586] 586 32 +COV [587-587] 587 23 +COV [588-588] 588 33 +COV [589-589] 589 31 +COV [590-590] 590 28 +COV [591-591] 591 27 +COV [592-592] 592 28 +COV [593-593] 593 40 +COV [594-594] 594 28 +COV [595-595] 595 26 +COV [596-596] 596 22 +COV [597-597] 597 34 +COV [598-598] 598 35 +COV [599-599] 599 29 +COV [600-600] 600 23 +COV [601-601] 601 34 +COV [602-602] 602 19 +COV [603-603] 603 25 +COV [604-604] 604 23 +COV [605-605] 605 33 +COV [606-606] 606 27 +COV [607-607] 607 31 +COV [608-608] 608 23 +COV [609-609] 609 29 +COV [610-610] 610 34 +COV [611-611] 611 36 +COV [612-612] 612 32 +COV [613-613] 613 27 +COV [614-614] 614 26 +COV [615-615] 615 31 +COV [616-616] 616 27 +COV [617-617] 617 36 +COV [618-618] 618 15 +COV [619-619] 619 36 +COV [620-620] 620 20 +COV [621-621] 621 30 +COV [622-622] 622 30 +COV [623-623] 623 40 +COV [624-624] 624 29 +COV [625-625] 625 24 +COV [626-626] 626 40 +COV [627-627] 627 36 +COV [628-628] 628 24 +COV [629-629] 629 20 +COV [630-630] 630 18 +COV [631-631] 631 28 +COV [632-632] 632 28 +COV [633-633] 633 23 +COV [634-634] 634 24 +COV [635-635] 635 21 +COV [636-636] 636 18 +COV [637-637] 637 22 +COV [638-638] 638 24 +COV [639-639] 639 24 +COV [640-640] 640 19 +COV [641-641] 641 26 +COV [642-642] 642 16 +COV [643-643] 643 24 +COV [644-644] 644 22 +COV [645-645] 645 19 +COV [646-646] 646 24 +COV [647-647] 647 27 +COV [648-648] 648 22 +COV [649-649] 649 15 +COV [650-650] 650 30 +COV [651-651] 651 32 +COV [652-652] 652 21 +COV [653-653] 653 25 +COV [654-654] 654 24 +COV [655-655] 655 26 +COV [656-656] 656 33 +COV [657-657] 657 20 +COV [658-658] 658 28 +COV [659-659] 659 32 +COV [660-660] 660 28 +COV [661-661] 661 29 +COV [662-662] 662 22 +COV [663-663] 663 26 +COV [664-664] 664 18 +COV [665-665] 665 28 +COV [666-666] 666 24 +COV [667-667] 667 30 +COV [668-668] 668 24 +COV [669-669] 669 24 +COV [670-670] 670 21 +COV [671-671] 671 31 +COV [672-672] 672 22 +COV [673-673] 673 24 +COV [674-674] 674 27 +COV [675-675] 675 28 +COV [676-676] 676 30 +COV [677-677] 677 34 +COV [678-678] 678 43 +COV [679-679] 679 31 +COV [680-680] 680 26 +COV [681-681] 681 26 +COV [682-682] 682 24 +COV [683-683] 683 25 +COV [684-684] 684 21 +COV [685-685] 685 16 +COV [686-686] 686 30 +COV [687-687] 687 23 +COV [688-688] 688 30 +COV [689-689] 689 19 +COV [690-690] 690 22 +COV [691-691] 691 30 +COV [692-692] 692 30 +COV [693-693] 693 23 +COV [694-694] 694 39 +COV [695-695] 695 15 +COV [696-696] 696 20 +COV [697-697] 697 29 +COV [698-698] 698 34 +COV [699-699] 699 18 +COV [700-700] 700 31 +COV [701-701] 701 23 +COV [702-702] 702 25 +COV [703-703] 703 36 +COV [704-704] 704 34 +COV [705-705] 705 35 +COV [706-706] 706 29 +COV [707-707] 707 31 +COV [708-708] 708 22 +COV [709-709] 709 22 +COV [710-710] 710 26 +COV [711-711] 711 29 +COV [712-712] 712 34 +COV [713-713] 713 33 +COV [714-714] 714 20 +COV [715-715] 715 23 +COV [716-716] 716 24 +COV [717-717] 717 25 +COV [718-718] 718 24 +COV [719-719] 719 28 +COV [720-720] 720 26 +COV [721-721] 721 24 +COV [722-722] 722 19 +COV [723-723] 723 21 +COV [724-724] 724 28 +COV [725-725] 725 23 +COV [726-726] 726 29 +COV [727-727] 727 28 +COV [728-728] 728 31 +COV [729-729] 729 15 +COV [730-730] 730 25 +COV [731-731] 731 26 +COV [732-732] 732 17 +COV [733-733] 733 20 +COV [734-734] 734 15 +COV [735-735] 735 23 +COV [736-736] 736 14 +COV [737-737] 737 20 +COV [738-738] 738 21 +COV [739-739] 739 24 +COV [740-740] 740 20 +COV [741-741] 741 24 +COV [742-742] 742 24 +COV [743-743] 743 23 +COV [744-744] 744 18 +COV [745-745] 745 18 +COV [746-746] 746 14 +COV [747-747] 747 13 +COV [748-748] 748 20 +COV [749-749] 749 37 +COV [750-750] 750 22 +COV [751-751] 751 25 +COV [752-752] 752 21 +COV [753-753] 753 16 +COV [754-754] 754 24 +COV [755-755] 755 20 +COV [756-756] 756 20 +COV [757-757] 757 29 +COV [758-758] 758 16 +COV [759-759] 759 15 +COV [760-760] 760 16 +COV [761-761] 761 21 +COV [762-762] 762 22 +COV [763-763] 763 24 +COV [764-764] 764 17 +COV [765-765] 765 15 +COV [766-766] 766 21 +COV [767-767] 767 27 +COV [768-768] 768 16 +COV [769-769] 769 29 +COV [770-770] 770 27 +COV [771-771] 771 17 +COV [772-772] 772 16 +COV [773-773] 773 23 +COV [774-774] 774 28 +COV [775-775] 775 16 +COV [776-776] 776 24 +COV [777-777] 777 16 +COV [778-778] 778 20 +COV [779-779] 779 27 +COV [780-780] 780 18 +COV [781-781] 781 27 +COV [782-782] 782 18 +COV [783-783] 783 23 +COV [784-784] 784 12 +COV [785-785] 785 33 +COV [786-786] 786 20 +COV [787-787] 787 22 +COV [788-788] 788 15 +COV [789-789] 789 18 +COV [790-790] 790 19 +COV [791-791] 791 19 +COV [792-792] 792 31 +COV [793-793] 793 17 +COV [794-794] 794 16 +COV [795-795] 795 17 +COV [796-796] 796 16 +COV [797-797] 797 18 +COV [798-798] 798 19 +COV [799-799] 799 22 +COV [800-800] 800 12 +COV [801-801] 801 19 +COV [802-802] 802 22 +COV [803-803] 803 15 +COV [804-804] 804 18 +COV [805-805] 805 20 +COV [806-806] 806 14 +COV [807-807] 807 20 +COV [808-808] 808 22 +COV [809-809] 809 16 +COV [810-810] 810 23 +COV [811-811] 811 24 +COV [812-812] 812 27 +COV [813-813] 813 15 +COV [814-814] 814 18 +COV [815-815] 815 28 +COV [816-816] 816 22 +COV [817-817] 817 32 +COV [818-818] 818 14 +COV [819-819] 819 21 +COV [820-820] 820 18 +COV [821-821] 821 21 +COV [822-822] 822 17 +COV [823-823] 823 18 +COV [824-824] 824 17 +COV [825-825] 825 21 +COV [826-826] 826 11 +COV [827-827] 827 14 +COV [828-828] 828 15 +COV [829-829] 829 18 +COV [830-830] 830 18 +COV [831-831] 831 27 +COV [832-832] 832 21 +COV [833-833] 833 24 +COV [834-834] 834 25 +COV [835-835] 835 19 +COV [836-836] 836 27 +COV [837-837] 837 19 +COV [838-838] 838 24 +COV [839-839] 839 16 +COV [840-840] 840 17 +COV [841-841] 841 12 +COV [842-842] 842 22 +COV [843-843] 843 18 +COV [844-844] 844 11 +COV [845-845] 845 29 +COV [846-846] 846 22 +COV [847-847] 847 18 +COV [848-848] 848 25 +COV [849-849] 849 19 +COV [850-850] 850 13 +COV [851-851] 851 18 +COV [852-852] 852 21 +COV [853-853] 853 19 +COV [854-854] 854 19 +COV [855-855] 855 19 +COV [856-856] 856 17 +COV [857-857] 857 21 +COV [858-858] 858 21 +COV [859-859] 859 15 +COV [860-860] 860 28 +COV [861-861] 861 14 +COV [862-862] 862 20 +COV [863-863] 863 10 +COV [864-864] 864 15 +COV [865-865] 865 20 +COV [866-866] 866 18 +COV [867-867] 867 18 +COV [868-868] 868 17 +COV [869-869] 869 13 +COV [870-870] 870 19 +COV [871-871] 871 14 +COV [872-872] 872 19 +COV [873-873] 873 14 +COV [874-874] 874 13 +COV [875-875] 875 20 +COV [876-876] 876 28 +COV [877-877] 877 21 +COV [878-878] 878 14 +COV [879-879] 879 21 +COV [880-880] 880 22 +COV [881-881] 881 16 +COV [882-882] 882 18 +COV [883-883] 883 24 +COV [884-884] 884 22 +COV [885-885] 885 22 +COV [886-886] 886 23 +COV [887-887] 887 19 +COV [888-888] 888 16 +COV [889-889] 889 11 +COV [890-890] 890 18 +COV [891-891] 891 18 +COV [892-892] 892 16 +COV [893-893] 893 20 +COV [894-894] 894 18 +COV [895-895] 895 13 +COV [896-896] 896 25 +COV [897-897] 897 15 +COV [898-898] 898 22 +COV [899-899] 899 21 +COV [900-900] 900 13 +COV [901-901] 901 16 +COV [902-902] 902 16 +COV [903-903] 903 22 +COV [904-904] 904 19 +COV [905-905] 905 24 +COV [906-906] 906 26 +COV [907-907] 907 20 +COV [908-908] 908 14 +COV [909-909] 909 15 +COV [910-910] 910 19 +COV [911-911] 911 19 +COV [912-912] 912 19 +COV [913-913] 913 17 +COV [914-914] 914 21 +COV [915-915] 915 24 +COV [916-916] 916 6 +COV [917-917] 917 21 +COV [918-918] 918 10 +COV [919-919] 919 13 +COV [920-920] 920 25 +COV [921-921] 921 12 +COV [922-922] 922 12 +COV [923-923] 923 13 +COV [924-924] 924 21 +COV [925-925] 925 13 +COV [926-926] 926 24 +COV [927-927] 927 13 +COV [928-928] 928 9 +COV [929-929] 929 17 +COV [930-930] 930 7 +COV [931-931] 931 9 +COV [932-932] 932 19 +COV [933-933] 933 16 +COV [934-934] 934 21 +COV [935-935] 935 17 +COV [936-936] 936 14 +COV [937-937] 937 13 +COV [938-938] 938 17 +COV [939-939] 939 14 +COV [940-940] 940 10 +COV [941-941] 941 20 +COV [942-942] 942 19 +COV [943-943] 943 17 +COV [944-944] 944 16 +COV [945-945] 945 13 +COV [946-946] 946 14 +COV [947-947] 947 17 +COV [948-948] 948 11 +COV [949-949] 949 12 +COV [950-950] 950 14 +COV [951-951] 951 11 +COV [952-952] 952 14 +COV [953-953] 953 11 +COV [954-954] 954 14 +COV [955-955] 955 18 +COV [956-956] 956 18 +COV [957-957] 957 17 +COV [958-958] 958 13 +COV [959-959] 959 17 +COV [960-960] 960 19 +COV [961-961] 961 14 +COV [962-962] 962 19 +COV [963-963] 963 7 +COV [964-964] 964 12 +COV [965-965] 965 13 +COV [966-966] 966 13 +COV [967-967] 967 15 +COV [968-968] 968 21 +COV [969-969] 969 16 +COV [970-970] 970 18 +COV [971-971] 971 4 +COV [972-972] 972 18 +COV [973-973] 973 14 +COV [974-974] 974 17 +COV [975-975] 975 17 +COV [976-976] 976 10 +COV [977-977] 977 11 +COV [978-978] 978 16 +COV [979-979] 979 13 +COV [980-980] 980 14 +COV [981-981] 981 27 +COV [982-982] 982 18 +COV [983-983] 983 20 +COV [984-984] 984 15 +COV [985-985] 985 18 +COV [986-986] 986 14 +COV [987-987] 987 15 +COV [988-988] 988 19 +COV [989-989] 989 22 +COV [990-990] 990 12 +COV [991-991] 991 11 +COV [992-992] 992 14 +COV [993-993] 993 20 +COV [994-994] 994 11 +COV [995-995] 995 11 +COV [996-996] 996 15 +COV [997-997] 997 17 +COV [998-998] 998 6 +COV [999-999] 999 11 +COV [1000-1000] 1000 16 +COV [1000<] 1000 29066 +# GC-depth. Use `grep ^GCD | cut -f 2-` to extract this part. The columns are: GC%, unique sequence percentiles, 10th, 25th, 50th, 75th and 90th depth percentile +GCD 0.0 0.006 0.000 0.002 0.002 0.005 0.005 +GCD 1.0 0.007 0.007 0.007 0.007 0.007 0.007 +GCD 2.0 0.012 0.002 0.002 0.002 0.005 0.007 +GCD 3.0 0.014 0.005 0.005 0.007 0.020 0.020 +GCD 4.0 0.016 0.002 0.002 0.002 0.002 0.002 +GCD 6.0 0.020 0.002 0.002 0.002 0.002 0.002 +GCD 8.0 0.022 0.002 0.002 0.002 0.002 0.002 +GCD 10.0 0.024 0.002 0.002 0.002 0.007 0.007 +GCD 11.0 0.025 0.005 0.005 0.005 0.005 0.005 +GCD 11.6 0.025 0.047 0.047 0.047 0.047 0.047 +GCD 12.0 0.031 0.002 0.002 0.002 0.005 0.007 +GCD 13.0 0.032 0.005 0.005 0.005 0.005 0.005 +GCD 14.0 0.033 0.002 0.002 0.002 0.005 0.005 +GCD 15.0 0.035 0.010 0.010 0.010 0.017 0.017 +GCD 16.0 0.039 0.002 0.002 0.005 0.005 0.005 +GCD 17.0 0.041 0.007 0.007 0.007 0.007 0.007 +GCD 18.0 0.045 0.002 0.002 0.005 0.010 0.010 +GCD 19.0 0.048 0.002 0.002 0.002 0.005 0.012 +GCD 20.0 0.052 0.002 0.002 0.002 0.002 0.007 +GCD 21.0 0.054 0.002 0.002 0.005 0.007 0.007 +GCD 22.0 0.062 0.002 0.002 0.005 0.007 0.010 +GCD 23.0 0.068 0.002 0.005 0.005 0.007 0.017 +GCD 24.0 0.080 0.002 0.002 0.002 0.002 0.005 +GCD 25.0 0.089 0.002 0.005 0.007 0.010 0.012 +GCD 26.0 0.105 0.002 0.002 0.002 0.005 0.007 +GCD 27.0 0.112 0.007 0.007 0.007 0.419 0.691 +GCD 28.0 0.124 0.002 0.002 0.007 0.020 0.093 +GCD 29.0 0.134 0.005 0.005 0.007 0.010 0.012 +GCD 30.0 0.149 0.002 0.002 0.005 0.012 0.022 +GCD 31.0 0.171 0.005 0.005 0.010 0.120 0.255 +GCD 32.0 0.225 0.002 0.005 0.120 0.228 0.262 +GCD 33.0 0.344 0.012 0.142 0.225 0.262 0.292 +GCD 34.0 0.721 0.022 0.194 0.240 0.272 0.296 +GCD 35.0 1.791 0.167 0.223 0.255 0.282 0.304 +GCD 36.0 4.174 0.194 0.235 0.265 0.292 0.316 +GCD 37.0 8.546 0.208 0.245 0.277 0.304 0.331 +GCD 38.0 15.513 0.218 0.255 0.287 0.316 0.345 +GCD 39.0 24.259 0.228 0.267 0.299 0.328 0.358 +GCD 40.0 34.137 0.238 0.277 0.311 0.343 0.372 +GCD 41.0 44.039 0.247 0.289 0.323 0.355 0.387 +GCD 42.0 53.383 0.260 0.299 0.336 0.368 0.402 +GCD 43.0 61.865 0.274 0.314 0.348 0.382 0.417 +GCD 44.0 69.571 0.289 0.326 0.363 0.394 0.431 +GCD 45.0 76.519 0.304 0.341 0.375 0.409 0.446 +GCD 46.0 82.424 0.316 0.353 0.387 0.424 0.468 +GCD 47.0 87.213 0.326 0.365 0.402 0.441 0.488 +GCD 48.0 90.973 0.338 0.375 0.409 0.451 0.505 +GCD 49.0 93.667 0.353 0.390 0.424 0.463 0.517 +GCD 50.0 95.910 0.345 0.392 0.429 0.470 0.524 +GCD 51.0 97.447 0.365 0.404 0.439 0.480 0.537 +GCD 52.0 98.546 0.365 0.402 0.441 0.483 0.537 +GCD 53.0 99.252 0.370 0.412 0.446 0.485 0.539 +GCD 54.0 99.662 0.365 0.412 0.448 0.492 0.561 +GCD 55.0 99.840 0.387 0.426 0.461 0.502 0.573 +GCD 56.0 99.930 0.020 0.402 0.446 0.485 0.554 +GCD 57.0 99.966 0.279 0.387 0.426 0.492 0.581 +GCD 58.0 99.982 0.002 0.005 0.470 0.980 1.063 +GCD 59.0 99.988 0.397 0.461 0.475 0.987 1.333 +GCD 60.0 99.989 0.002 0.002 0.002 0.211 0.211 +GCD 61.0 99.992 0.005 0.005 0.485 0.502 0.527 +GCD 62.0 99.995 0.002 0.002 0.002 0.434 0.434 +GCD 63.0 99.997 0.892 0.892 1.105 2.472 2.472 +GCD 64.0 99.998 1.034 1.034 1.034 1.098 1.098 +GCD 65.0 99.999 12.870 12.870 12.870 12.870 12.870 +GCD 66.0 100.000 0.002 0.002 0.002 0.002 0.002 diff --git a/src/multiqc/test_data/b.txt b/src/multiqc/test_data/b.txt new file mode 100644 index 00000000..5df6122a --- /dev/null +++ b/src/multiqc/test_data/b.txt @@ -0,0 +1,1505 @@ +# This file was produced by samtools stats (1.3+htslib-1.3) and can be plotted using plot-bamstats +# This file contains statistics for all reads. +# The command line was: stats mapped/b.bam +# CHK, Checksum [2]Read Names [3]Sequences [4]Qualities +# CHK, CRC32 of reads which passed filtering followed by addition (32bit overflow) +CHK ee6a9cbf ecd7f501 51869fe3 +# Summary Numbers. Use `grep ^SN | cut -f 2-` to extract this part. +SN raw total sequences: 18576178 +SN filtered sequences: 0 +SN sequences: 18576178 +SN is sorted: 1 +SN 1st fragments: 18576178 +SN last fragments: 0 +SN reads mapped: 18166869 +SN reads mapped and paired: 0 # paired-end technology bit set + both mates mapped +SN reads unmapped: 409309 +SN reads properly paired: 0 # proper-pair bit set +SN reads paired: 0 # paired-end technology bit set +SN reads duplicated: 1674761 # PCR or optical duplicate bit set +SN reads MQ0: 3360997 # mapped and MQ=0 +SN reads QC failed: 0 +SN non-primary alignments: 0 +SN total length: 927580112 # ignores clipping +SN bases mapped: 907135249 # ignores clipping +SN bases mapped (cigar): 907135242 # more accurate +SN bases trimmed: 0 +SN bases duplicated: 83676902 +SN mismatches: 4228623 # from NM fields +SN error rate: 4.661513e-03 # mismatches / bases mapped (cigar) +SN average length: 49 +SN maximum length: 50 +SN average quality: 37.1 +SN insert size average: 0.0 +SN insert size standard deviation: 0.0 +SN inward oriented pairs: 0 +SN outward oriented pairs: 0 +SN pairs with other orientation: 0 +SN pairs on different chromosomes: 0 +# First Fragment Qualitites. Use `grep ^FFQ | cut -f 2-` to extract this part. +# Columns correspond to qualities and rows to cycles. First column is the cycle number. +FFQ 1 0 0 43365 0 0 0 0 0 0 0 0 0 0 0 0 316667 0 0 0 0 0 0 145075 0 0 0 0 1091864 0 0 0 0 0 16979207 0 0 0 0 0 0 0 0 +FFQ 2 0 0 2631 0 0 0 0 0 0 0 0 0 0 0 0 226851 0 0 0 0 0 0 117493 0 0 0 0 1044416 0 0 0 0 0 17184787 0 0 0 0 0 0 0 0 +FFQ 3 0 0 2642 0 0 0 0 0 0 0 0 0 0 0 0 150819 0 0 0 0 0 0 121280 0 0 0 0 1043537 0 0 0 0 0 17257900 0 0 0 0 0 0 0 0 +FFQ 4 0 0 2652 0 0 0 0 0 0 0 0 0 0 0 0 118535 0 0 0 0 0 0 32499 0 0 0 0 385005 0 0 0 0 0 1401276 0 0 0 16636211 0 0 0 0 +FFQ 5 0 0 2664 0 0 0 0 0 0 0 0 0 0 0 0 149178 0 0 0 0 0 0 33908 0 0 0 0 445421 0 0 0 0 0 1485286 0 0 0 16459721 0 0 0 0 +FFQ 6 0 0 2681 0 0 0 3389 0 0 0 0 0 0 0 0 146379 0 0 0 0 0 0 40576 0 0 0 0 438297 0 0 0 0 0 1478793 0 0 0 16466063 0 0 0 0 +FFQ 7 0 0 2719 0 0 0 4001 0 0 0 0 0 0 0 0 156176 0 0 0 0 0 0 40734 0 0 0 0 447501 0 0 0 0 0 1488746 0 0 0 16436301 0 0 0 0 +FFQ 8 0 0 2759 0 0 0 3952 0 0 0 0 0 0 0 0 156255 0 0 0 0 0 0 40576 0 0 0 0 446114 0 0 0 0 0 1486214 0 0 0 16440308 0 0 0 0 +FFQ 9 0 0 2822 0 0 0 4334 0 0 0 0 0 0 0 0 159441 0 0 0 0 0 0 40893 0 0 0 0 448307 0 0 0 0 0 1489379 0 0 0 16431002 0 0 0 0 +FFQ 10 0 0 2897 0 0 0 3878 0 0 0 0 0 0 0 0 153155 0 0 0 0 0 0 41101 0 0 0 0 447304 0 0 0 0 0 1484804 0 0 0 16443039 0 0 0 0 +FFQ 11 0 0 2982 0 0 0 4003 0 0 0 0 0 0 0 0 151436 0 0 0 0 0 0 42075 0 0 0 0 447020 0 0 0 0 0 1486604 0 0 0 16442058 0 0 0 0 +FFQ 12 0 0 3101 0 0 0 4242 0 0 0 0 0 0 0 0 155412 0 0 0 0 0 0 42911 0 0 0 0 449860 0 0 0 0 0 1491835 0 0 0 16428817 0 0 0 0 +FFQ 13 0 0 3240 0 0 0 4244 0 0 0 0 0 0 0 0 154472 0 0 0 0 0 0 44402 0 0 0 0 451953 0 0 0 0 0 1495686 0 0 0 16422181 0 0 0 0 +FFQ 14 0 0 3399 0 0 0 4705 0 0 0 0 0 0 0 0 158387 0 0 0 0 0 0 44832 0 0 0 0 452013 0 0 0 0 0 1422645 0 0 0 5009192 0 0 11481005 0 +FFQ 15 0 0 3574 0 0 0 4943 0 0 0 0 0 0 0 0 162629 0 0 0 0 0 0 45939 0 0 0 0 455281 0 0 0 0 0 1429083 0 0 0 5018020 0 0 11456709 0 +FFQ 16 0 0 3789 0 0 0 5204 0 0 0 0 0 0 0 0 164703 0 0 0 0 0 0 47268 0 0 0 0 459688 0 0 0 0 0 1438944 0 0 0 5025956 0 0 11430626 0 +FFQ 17 0 0 4054 0 0 0 5500 0 0 0 0 0 0 0 0 161723 0 0 0 0 0 0 49772 0 0 0 0 462909 0 0 0 0 0 1448410 0 0 0 5050556 0 0 11393254 0 +FFQ 18 0 0 4376 0 0 0 6040 0 0 0 0 0 0 0 0 163399 0 0 0 0 0 0 51435 0 0 0 0 456360 0 0 0 0 0 1447415 0 0 0 5057035 0 0 11390118 0 +FFQ 19 0 0 4895 0 0 0 6152 0 0 0 0 0 0 0 0 164137 0 0 0 0 0 0 54177 0 0 0 0 459431 0 0 0 0 0 1452212 0 0 0 4724623 0 0 11710551 0 +FFQ 20 0 0 5278 0 0 0 6748 0 0 0 0 0 0 0 0 161669 0 0 0 0 0 0 57142 0 0 0 0 456404 0 0 0 0 0 1454134 0 0 0 4771944 0 0 11662859 0 +FFQ 21 0 0 6041 0 0 0 7654 0 0 0 0 0 0 0 0 160998 0 0 0 0 0 0 61704 0 0 0 0 453274 0 0 0 0 0 1456206 0 0 0 4832585 0 0 11597716 0 +FFQ 22 0 0 7025 0 0 0 8936 0 0 0 0 0 0 0 0 164004 0 0 0 0 0 0 69132 0 0 0 0 455154 0 0 0 0 0 1463092 0 0 0 4900894 0 0 11507941 0 +FFQ 23 0 0 8438 0 0 0 10369 0 0 0 0 0 0 0 0 166619 0 0 0 0 0 0 76781 0 0 0 0 451917 0 0 0 0 0 1473087 0 0 0 4980302 0 0 11408665 0 +FFQ 24 0 0 10137 0 0 0 12451 0 0 0 0 0 0 0 0 167661 0 0 0 0 0 0 86418 0 0 0 0 446268 0 0 0 0 0 1481414 0 0 0 4969528 0 0 11402301 0 +FFQ 25 0 0 12428 0 0 0 14892 0 0 0 0 0 0 0 0 173631 0 0 0 0 0 0 99776 0 0 0 0 442524 0 0 0 0 0 1493761 0 0 0 5016661 0 0 11322505 0 +FFQ 26 0 0 15335 0 0 0 25515 0 0 0 0 0 0 0 0 204814 0 0 0 0 0 0 111835 0 0 0 0 427338 0 0 0 0 0 1483190 0 0 0 5032460 0 0 11275691 0 +FFQ 27 0 0 18183 0 0 0 31691 0 0 0 0 0 0 0 0 211885 0 0 0 0 0 0 125826 0 0 0 0 425529 0 0 0 0 0 1495988 0 0 0 5036475 0 0 11230601 0 +FFQ 28 0 0 21234 0 0 0 38567 0 0 0 0 0 0 0 0 212153 0 0 0 0 0 0 138492 0 0 0 0 416049 0 0 0 0 0 1506817 0 0 0 5050848 0 0 11192018 0 +FFQ 29 0 0 24469 0 0 0 45921 0 0 0 0 0 0 0 0 208586 0 0 0 0 0 0 150099 0 0 0 0 406972 0 0 0 0 0 1510396 0 0 0 5061017 0 0 11168718 0 +FFQ 30 0 0 27790 0 0 0 54131 0 0 0 0 0 0 0 0 202787 0 0 0 0 0 0 159734 0 0 0 0 401982 0 0 0 0 0 1517830 0 0 0 5076275 0 0 11135649 0 +FFQ 31 0 0 31389 0 0 0 64745 0 0 0 0 0 0 0 0 197260 0 0 0 0 0 0 167040 0 0 0 0 398914 0 0 0 0 0 1522211 0 0 0 5081933 0 0 11112662 0 +FFQ 32 0 0 35265 0 0 0 73249 0 0 0 0 0 0 0 0 190526 0 0 0 0 0 0 171610 0 0 0 0 397631 0 0 0 0 0 1530272 0 0 0 5089484 0 0 11088092 0 +FFQ 33 0 0 39568 0 0 0 82173 0 0 0 0 0 0 0 0 181514 0 0 0 0 0 0 175016 0 0 0 0 397113 0 0 0 0 0 1534492 0 0 0 5098856 0 0 11067375 0 +FFQ 34 0 0 44025 0 0 0 92751 0 0 0 0 0 0 0 0 177863 0 0 0 0 0 0 177915 0 0 0 0 398138 0 0 0 0 0 1535653 0 0 0 5098622 0 0 11051118 0 +FFQ 35 0 0 49081 0 0 0 96325 0 0 0 0 0 0 0 0 171633 0 0 0 0 0 0 181304 0 0 0 0 399581 0 0 0 0 0 1542546 0 0 0 5100609 0 0 11034967 0 +FFQ 36 0 0 54552 0 0 0 101878 0 0 0 0 0 0 0 0 166948 0 0 0 0 0 0 185932 0 0 0 0 399740 0 0 0 0 0 1551353 0 0 0 5114688 0 0 11000912 0 +FFQ 37 0 0 60308 0 0 0 104644 0 0 0 0 0 0 0 0 164138 0 0 0 0 0 0 189071 0 0 0 0 399564 0 0 0 0 0 1556486 0 0 0 5126556 0 0 10975200 0 +FFQ 38 0 0 66655 0 0 0 108344 0 0 0 0 0 0 0 0 163526 0 0 0 0 0 0 194698 0 0 0 0 402462 0 0 0 0 0 1564062 0 0 0 5137844 0 0 10938345 0 +FFQ 39 0 0 74040 0 0 0 109236 0 0 0 0 0 0 0 0 163901 0 0 0 0 0 0 199818 0 0 0 0 402198 0 0 0 0 0 1573785 0 0 0 5152707 0 0 10900208 0 +FFQ 40 0 0 82117 0 0 0 113084 0 0 0 0 0 0 0 0 165347 0 0 0 0 0 0 205720 0 0 0 0 402517 0 0 0 0 0 1577961 0 0 0 5155840 0 0 10873211 0 +FFQ 41 0 0 91108 0 0 0 107424 0 0 0 0 0 0 0 0 165874 0 0 0 0 0 0 212167 0 0 0 0 403439 0 0 0 0 0 1590477 0 0 0 5168941 0 0 10836084 0 +FFQ 42 0 0 102306 0 0 0 106524 0 0 0 0 0 0 0 0 163227 0 0 0 0 0 0 217731 0 0 0 0 403568 0 0 0 0 0 1594692 0 0 0 5188518 0 0 10798521 0 +FFQ 43 0 0 113461 0 0 0 105566 0 0 0 0 0 0 0 0 163762 0 0 0 0 0 0 226845 0 0 0 0 402623 0 0 0 0 0 1602299 0 0 0 5194713 0 0 10765448 0 +FFQ 44 0 0 127992 0 0 0 103292 0 0 0 0 0 0 0 0 161844 0 0 0 0 0 0 234717 0 0 0 0 401632 0 0 0 0 0 1618498 0 0 0 5206467 0 0 10720171 0 +FFQ 45 0 0 146743 0 0 0 99382 0 0 0 0 0 0 0 0 157848 0 0 0 0 0 0 242460 0 0 0 0 401753 0 0 0 0 0 1630019 0 0 0 5184900 0 0 10711332 0 +FFQ 46 0 0 171139 0 0 0 94094 0 0 0 0 0 0 0 0 154818 0 0 0 0 0 0 248889 0 0 0 0 401705 0 0 0 0 0 1637377 0 0 0 5203803 0 0 10661983 0 +FFQ 47 0 0 200079 0 0 0 86401 0 0 0 0 0 0 0 0 149983 0 0 0 0 0 0 252443 0 0 0 0 400899 0 0 0 0 0 1643053 0 0 0 5213972 0 0 10610153 0 +FFQ 48 0 0 240223 0 0 0 76248 0 0 0 0 0 0 0 0 143739 0 0 0 0 0 0 255275 0 0 0 0 401446 0 0 0 0 0 1652184 0 0 0 5217727 0 0 10481218 0 +FFQ 49 0 0 290803 0 0 0 54802 0 0 0 0 0 0 0 0 131779 0 0 0 0 0 0 247788 0 0 0 0 392435 0 0 0 0 0 1629666 0 0 0 5126595 0 0 10156850 0 +FFQ 50 0 0 363909 0 0 0 25352 0 0 0 0 0 0 0 0 126109 0 0 0 0 0 0 245269 0 0 0 0 391546 0 0 0 0 0 1648220 0 0 0 5162006 0 0 10068307 0 +FFQ 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +# Last Fragment Qualitites. Use `grep ^LFQ | cut -f 2-` to extract this part. +# Columns correspond to qualities and rows to cycles. First column is the cycle number. +LFQ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 37 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 38 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 41 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 44 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 45 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 46 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 49 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +LFQ 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +# GC Content of first fragments. Use `grep ^GCF | cut -f 2-` to extract this part. +GCF 0.75 1080 +GCF 1.76 1554 +GCF 2.26 1567 +GCF 3.02 1566 +GCF 3.77 2850 +GCF 4.77 2881 +GCF 5.78 4731 +GCF 6.28 4791 +GCF 7.04 4792 +GCF 7.79 8136 +GCF 8.29 8231 +GCF 9.05 8233 +GCF 9.80 14137 +GCF 10.30 14252 +GCF 10.80 14293 +GCF 11.31 14294 +GCF 11.81 24969 +GCF 12.31 25354 +GCF 13.07 25452 +GCF 13.82 43286 +GCF 14.32 43288 +GCF 14.82 43868 +GCF 15.33 43887 +GCF 15.83 72345 +GCF 16.33 72346 +GCF 16.83 73323 +GCF 17.34 73346 +GCF 17.84 115892 +GCF 18.34 115893 +GCF 18.84 117007 +GCF 19.35 117404 +GCF 19.85 176022 +GCF 20.35 176024 +GCF 20.85 177820 +GCF 21.36 178297 +GCF 21.86 256493 +GCF 22.36 256500 +GCF 22.86 258736 +GCF 23.37 259378 +GCF 23.87 358005 +GCF 24.37 358006 +GCF 24.87 361025 +GCF 25.38 362008 +GCF 25.88 484259 +GCF 26.38 484258 +GCF 26.88 487623 +GCF 27.39 487628 +GCF 27.89 641238 +GCF 28.39 641467 +GCF 28.89 641483 +GCF 29.40 645895 +GCF 29.90 848565 +GCF 30.40 848762 +GCF 30.90 848764 +GCF 31.41 854323 +GCF 31.91 1093044 +GCF 32.66 1093138 +GCF 33.42 1096936 +GCF 33.92 1331028 +GCF 34.42 1331031 +GCF 34.92 1331203 +GCF 35.43 1335750 +GCF 35.93 1505123 +GCF 36.43 1505127 +GCF 36.93 1505175 +GCF 37.44 1508624 +GCF 37.94 1493366 +GCF 38.44 1493657 +GCF 38.94 1493818 +GCF 39.45 1494636 +GCF 39.95 1420236 +GCF 40.45 1420377 +GCF 40.95 1420370 +GCF 41.46 1419738 +GCF 41.96 1313182 +GCF 42.46 1313001 +GCF 42.96 1312999 +GCF 43.47 1313043 +GCF 43.97 1231538 +GCF 44.47 1231204 +GCF 44.97 1231192 +GCF 45.48 1231260 +GCF 45.98 1148968 +GCF 46.48 1148953 +GCF 46.98 1148367 +GCF 47.49 1148340 +GCF 47.99 1059806 +GCF 48.49 1059813 +GCF 49.25 1058887 +GCF 50.25 954205 +GCF 51.01 953262 +GCF 51.51 953251 +GCF 52.01 808649 +GCF 52.51 808641 +GCF 53.02 807799 +GCF 53.52 807784 +GCF 54.02 649395 +GCF 54.52 649184 +GCF 55.03 649178 +GCF 55.53 648304 +GCF 56.03 490867 +GCF 56.53 490638 +GCF 57.04 490632 +GCF 57.54 489794 +GCF 58.04 359200 +GCF 58.54 355526 +GCF 59.05 355524 +GCF 59.55 355031 +GCF 60.05 247759 +GCF 60.55 244574 +GCF 61.06 244459 +GCF 61.56 243954 +GCF 62.06 164265 +GCF 62.56 162114 +GCF 63.07 162022 +GCF 63.57 162018 +GCF 64.07 103863 +GCF 64.57 102491 +GCF 65.08 102446 +GCF 65.58 102445 +GCF 66.08 64175 +GCF 66.83 63383 +GCF 67.59 63356 +GCF 68.09 37977 +GCF 68.59 37240 +GCF 69.10 37235 +GCF 69.60 37220 +GCF 70.10 22861 +GCF 70.60 22555 +GCF 71.11 22552 +GCF 71.61 22533 +GCF 72.11 14370 +GCF 72.61 14369 +GCF 73.37 14229 +GCF 74.12 9740 +GCF 74.62 9736 +GCF 75.13 9636 +GCF 75.63 9635 +GCF 76.13 6719 +GCF 76.63 6701 +GCF 77.14 6630 +GCF 77.64 6632 +GCF 78.14 4609 +GCF 78.64 4605 +GCF 79.40 4595 +GCF 80.15 3154 +GCF 80.65 3156 +GCF 81.41 3128 +GCF 82.16 2097 +GCF 82.66 2096 +GCF 83.42 2082 +GCF 84.17 1523 +GCF 84.67 1522 +GCF 85.43 1519 +GCF 86.68 822 +GCF 87.69 821 +GCF 88.44 544 +GCF 89.20 543 +GCF 89.70 535 +GCF 90.70 303 +GCF 91.71 304 +GCF 92.71 169 +GCF 93.72 167 +GCF 94.97 74 +GCF 96.98 47 +GCF 98.99 19 +# GC Content of last fragments. Use `grep ^GCL | cut -f 2-` to extract this part. +# ACGT content per cycle. Use `grep ^GCC | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%]; and N and O counts as a percentage of all A/C/G/T bases [%] +GCC 1 29.99 19.73 19.50 30.79 0.22 0.00 +GCC 2 29.97 19.73 19.48 30.83 0.00 0.00 +GCC 3 29.53 20.19 19.82 30.46 0.00 0.00 +GCC 4 29.46 20.23 19.95 30.36 0.00 0.00 +GCC 5 29.50 20.16 19.89 30.46 0.00 0.00 +GCC 6 29.32 20.45 20.06 30.17 0.00 0.00 +GCC 7 29.42 20.32 19.92 30.34 0.00 0.00 +GCC 8 29.44 20.26 19.90 30.41 0.00 0.00 +GCC 9 29.45 20.23 19.91 30.41 0.00 0.00 +GCC 10 29.35 20.34 19.98 30.33 0.00 0.00 +GCC 11 29.39 20.25 19.92 30.43 0.00 0.00 +GCC 12 29.40 20.32 19.93 30.35 0.00 0.00 +GCC 13 29.45 20.28 19.86 30.42 0.00 0.00 +GCC 14 29.41 20.26 19.85 30.48 0.00 0.00 +GCC 15 29.40 20.31 19.91 30.38 0.00 0.00 +GCC 16 29.24 20.40 20.03 30.33 0.00 0.00 +GCC 17 29.29 20.29 20.02 30.39 0.00 0.00 +GCC 18 29.20 20.38 20.15 30.27 0.00 0.00 +GCC 19 29.21 20.39 20.14 30.25 0.00 0.00 +GCC 20 29.21 20.36 20.21 30.23 0.00 0.00 +GCC 21 29.18 20.39 20.26 30.17 0.00 0.00 +GCC 22 29.16 20.41 20.19 30.24 0.00 0.00 +GCC 23 29.12 20.41 20.23 30.24 0.00 0.00 +GCC 24 29.12 20.47 20.20 30.21 0.00 0.00 +GCC 25 29.13 20.47 20.20 30.20 0.00 0.00 +GCC 26 29.11 20.47 20.26 30.16 0.00 0.00 +GCC 27 29.07 20.49 20.26 30.19 0.00 0.00 +GCC 28 28.99 20.56 20.38 30.07 0.00 0.00 +GCC 29 29.09 20.48 20.30 30.13 0.00 0.00 +GCC 30 29.06 20.49 20.37 30.08 0.00 0.00 +GCC 31 29.00 20.58 20.36 30.06 0.00 0.00 +GCC 32 29.01 20.56 20.34 30.09 0.00 0.00 +GCC 33 29.00 20.59 20.31 30.10 0.00 0.00 +GCC 34 28.97 20.65 20.30 30.09 0.00 0.00 +GCC 35 29.00 20.59 20.26 30.14 0.00 0.00 +GCC 36 28.96 20.66 20.30 30.08 0.00 0.00 +GCC 37 28.98 20.73 20.26 30.03 0.00 0.00 +GCC 38 28.96 20.75 20.30 29.99 0.00 0.00 +GCC 39 28.98 20.73 20.34 29.94 0.00 0.00 +GCC 40 28.93 20.76 20.39 29.92 0.00 0.00 +GCC 41 28.97 20.69 20.35 29.99 0.00 0.00 +GCC 42 28.96 20.68 20.36 29.99 0.01 0.00 +GCC 43 28.93 20.73 20.36 29.98 0.00 0.00 +GCC 44 29.02 20.71 20.33 29.94 0.00 0.00 +GCC 45 29.04 20.69 20.30 29.98 0.00 0.00 +GCC 46 29.00 20.71 20.38 29.91 0.01 0.00 +GCC 47 29.01 20.70 20.39 29.89 0.00 0.00 +GCC 48 28.93 20.78 20.47 29.82 0.00 0.00 +GCC 49 28.54 21.12 20.84 29.50 0.02 0.00 +GCC 50 29.58 20.06 19.83 30.53 0.00 0.00 +# Insert sizes. Use `grep ^IS | cut -f 2-` to extract this part. The columns are: insert size, pairs total, inward oriented pairs, outward oriented pairs, other pairs +# Read lengths. Use `grep ^RL | cut -f 2-` to extract this part. The columns are: read length, count +RL 30 24 +RL 31 25 +RL 32 22 +RL 33 22 +RL 34 39 +RL 35 43 +RL 36 36 +RL 37 31 +RL 38 43 +RL 39 96 +RL 40 283 +RL 41 427 +RL 42 370 +RL 43 104 +RL 44 176 +RL 45 629 +RL 46 16825 +RL 47 88923 +RL 48 437342 +RL 50 18030718 +# Indel distribution. Use `grep ^ID | cut -f 2-` to extract this part. The columns are: length, number of insertions, number of deletions +ID 1 55991 87210 +ID 2 11097 13144 +ID 3 1413 1466 +# Indels per cycle. Use `grep ^IC | cut -f 2-` to extract this part. The columns are: cycle, number of insertions (fwd), .. (rev) , number of deletions (fwd), .. (rev) +IC 2 0 135 0 131 +IC 3 0 500 0 654 +IC 4 0 799 0 1454 +IC 5 0 1390 0 1639 +IC 6 0 1371 0 1820 +IC 7 0 1345 0 1970 +IC 8 0 1321 0 2059 +IC 9 0 1429 0 2103 +IC 10 0 1477 0 2217 +IC 11 0 1488 0 2270 +IC 12 0 1593 0 2376 +IC 13 0 1586 0 2410 +IC 14 0 1602 0 2512 +IC 15 0 1664 0 2466 +IC 16 0 1631 0 2573 +IC 17 0 1711 0 2657 +IC 18 0 1651 0 2522 +IC 19 0 1667 0 2561 +IC 20 0 1661 0 2595 +IC 21 0 1644 0 2607 +IC 22 0 1725 0 2630 +IC 23 0 1690 0 2566 +IC 24 0 1725 0 2682 +IC 25 0 1625 0 2561 +IC 26 0 1624 0 2554 +IC 27 0 1636 0 2528 +IC 28 0 1694 0 2587 +IC 29 0 1729 0 2622 +IC 30 0 1712 0 2609 +IC 31 0 1846 0 2834 +IC 32 0 1834 0 2772 +IC 33 0 1820 0 2755 +IC 34 0 1850 0 2757 +IC 35 0 1754 0 2768 +IC 36 0 1744 0 2569 +IC 37 0 1665 0 2504 +IC 38 0 1622 0 2483 +IC 39 0 1559 0 2523 +IC 40 0 1544 0 2495 +IC 41 0 1518 0 2378 +IC 42 0 1406 0 2210 +IC 43 0 1353 0 2026 +IC 44 0 1295 0 1795 +IC 45 0 1290 0 1168 +IC 46 0 779 0 587 +IC 47 0 529 0 202 +IC 48 0 192 0 59 +IC 49 0 76 0 0 +# Coverage distribution. Use `grep ^COV | cut -f 2-` to extract this part. +COV [1-1] 1 582941672 +COV [2-2] 2 97104308 +COV [3-3] 3 11593609 +COV [4-4] 4 1244538 +COV [5-5] 5 189629 +COV [6-6] 6 63129 +COV [7-7] 7 34669 +COV [8-8] 8 22305 +COV [9-9] 9 16271 +COV [10-10] 10 12620 +COV [11-11] 11 9896 +COV [12-12] 12 8348 +COV [13-13] 13 6759 +COV [14-14] 14 5217 +COV [15-15] 15 4028 +COV [16-16] 16 3370 +COV [17-17] 17 2988 +COV [18-18] 18 2649 +COV [19-19] 19 2532 +COV [20-20] 20 2332 +COV [21-21] 21 2037 +COV [22-22] 22 2291 +COV [23-23] 23 2356 +COV [24-24] 24 2209 +COV [25-25] 25 2242 +COV [26-26] 26 2173 +COV [27-27] 27 1819 +COV [28-28] 28 1955 +COV [29-29] 29 1860 +COV [30-30] 30 1861 +COV [31-31] 31 1704 +COV [32-32] 32 1602 +COV [33-33] 33 1596 +COV [34-34] 34 1526 +COV [35-35] 35 1392 +COV [36-36] 36 1437 +COV [37-37] 37 1401 +COV [38-38] 38 1429 +COV [39-39] 39 1317 +COV [40-40] 40 1334 +COV [41-41] 41 1242 +COV [42-42] 42 1199 +COV [43-43] 43 1198 +COV [44-44] 44 1031 +COV [45-45] 45 1130 +COV [46-46] 46 1146 +COV [47-47] 47 1000 +COV [48-48] 48 1025 +COV [49-49] 49 1051 +COV [50-50] 50 1080 +COV [51-51] 51 1077 +COV [52-52] 52 1071 +COV [53-53] 53 1005 +COV [54-54] 54 965 +COV [55-55] 55 931 +COV [56-56] 56 1060 +COV [57-57] 57 1035 +COV [58-58] 58 965 +COV [59-59] 59 938 +COV [60-60] 60 1023 +COV [61-61] 61 1011 +COV [62-62] 62 995 +COV [63-63] 63 966 +COV [64-64] 64 881 +COV [65-65] 65 818 +COV [66-66] 66 810 +COV [67-67] 67 772 +COV [68-68] 68 780 +COV [69-69] 69 717 +COV [70-70] 70 566 +COV [71-71] 71 587 +COV [72-72] 72 557 +COV [73-73] 73 515 +COV [74-74] 74 530 +COV [75-75] 75 531 +COV [76-76] 76 435 +COV [77-77] 77 418 +COV [78-78] 78 443 +COV [79-79] 79 433 +COV [80-80] 80 361 +COV [81-81] 81 358 +COV [82-82] 82 351 +COV [83-83] 83 339 +COV [84-84] 84 274 +COV [85-85] 85 303 +COV [86-86] 86 243 +COV [87-87] 87 299 +COV [88-88] 88 258 +COV [89-89] 89 275 +COV [90-90] 90 235 +COV [91-91] 91 229 +COV [92-92] 92 208 +COV [93-93] 93 234 +COV [94-94] 94 205 +COV [95-95] 95 240 +COV [96-96] 96 253 +COV [97-97] 97 199 +COV [98-98] 98 200 +COV [99-99] 99 216 +COV [100-100] 100 223 +COV [101-101] 101 202 +COV [102-102] 102 187 +COV [103-103] 103 189 +COV [104-104] 104 186 +COV [105-105] 105 229 +COV [106-106] 106 182 +COV [107-107] 107 180 +COV [108-108] 108 181 +COV [109-109] 109 190 +COV [110-110] 110 149 +COV [111-111] 111 197 +COV [112-112] 112 189 +COV [113-113] 113 199 +COV [114-114] 114 208 +COV [115-115] 115 167 +COV [116-116] 116 166 +COV [117-117] 117 134 +COV [118-118] 118 165 +COV [119-119] 119 146 +COV [120-120] 120 149 +COV [121-121] 121 144 +COV [122-122] 122 169 +COV [123-123] 123 148 +COV [124-124] 124 140 +COV [125-125] 125 151 +COV [126-126] 126 141 +COV [127-127] 127 162 +COV [128-128] 128 149 +COV [129-129] 129 133 +COV [130-130] 130 143 +COV [131-131] 131 164 +COV [132-132] 132 141 +COV [133-133] 133 121 +COV [134-134] 134 136 +COV [135-135] 135 150 +COV [136-136] 136 134 +COV [137-137] 137 131 +COV [138-138] 138 139 +COV [139-139] 139 117 +COV [140-140] 140 141 +COV [141-141] 141 138 +COV [142-142] 142 116 +COV [143-143] 143 120 +COV [144-144] 144 127 +COV [145-145] 145 107 +COV [146-146] 146 130 +COV [147-147] 147 137 +COV [148-148] 148 149 +COV [149-149] 149 132 +COV [150-150] 150 125 +COV [151-151] 151 102 +COV [152-152] 152 105 +COV [153-153] 153 111 +COV [154-154] 154 115 +COV [155-155] 155 104 +COV [156-156] 156 104 +COV [157-157] 157 120 +COV [158-158] 158 104 +COV [159-159] 159 123 +COV [160-160] 160 126 +COV [161-161] 161 99 +COV [162-162] 162 125 +COV [163-163] 163 103 +COV [164-164] 164 124 +COV [165-165] 165 113 +COV [166-166] 166 103 +COV [167-167] 167 141 +COV [168-168] 168 121 +COV [169-169] 169 118 +COV [170-170] 170 130 +COV [171-171] 171 158 +COV [172-172] 172 121 +COV [173-173] 173 101 +COV [174-174] 174 110 +COV [175-175] 175 123 +COV [176-176] 176 121 +COV [177-177] 177 101 +COV [178-178] 178 106 +COV [179-179] 179 108 +COV [180-180] 180 103 +COV [181-181] 181 115 +COV [182-182] 182 99 +COV [183-183] 183 122 +COV [184-184] 184 102 +COV [185-185] 185 104 +COV [186-186] 186 123 +COV [187-187] 187 104 +COV [188-188] 188 115 +COV [189-189] 189 97 +COV [190-190] 190 121 +COV [191-191] 191 89 +COV [192-192] 192 118 +COV [193-193] 193 122 +COV [194-194] 194 104 +COV [195-195] 195 85 +COV [196-196] 196 96 +COV [197-197] 197 87 +COV [198-198] 198 92 +COV [199-199] 199 78 +COV [200-200] 200 92 +COV [201-201] 201 96 +COV [202-202] 202 75 +COV [203-203] 203 88 +COV [204-204] 204 87 +COV [205-205] 205 100 +COV [206-206] 206 91 +COV [207-207] 207 79 +COV [208-208] 208 89 +COV [209-209] 209 92 +COV [210-210] 210 91 +COV [211-211] 211 73 +COV [212-212] 212 112 +COV [213-213] 213 119 +COV [214-214] 214 98 +COV [215-215] 215 95 +COV [216-216] 216 93 +COV [217-217] 217 95 +COV [218-218] 218 95 +COV [219-219] 219 79 +COV [220-220] 220 76 +COV [221-221] 221 61 +COV [222-222] 222 90 +COV [223-223] 223 74 +COV [224-224] 224 64 +COV [225-225] 225 75 +COV [226-226] 226 77 +COV [227-227] 227 74 +COV [228-228] 228 79 +COV [229-229] 229 63 +COV [230-230] 230 57 +COV [231-231] 231 68 +COV [232-232] 232 66 +COV [233-233] 233 65 +COV [234-234] 234 75 +COV [235-235] 235 71 +COV [236-236] 236 63 +COV [237-237] 237 72 +COV [238-238] 238 95 +COV [239-239] 239 67 +COV [240-240] 240 86 +COV [241-241] 241 81 +COV [242-242] 242 77 +COV [243-243] 243 99 +COV [244-244] 244 80 +COV [245-245] 245 68 +COV [246-246] 246 66 +COV [247-247] 247 61 +COV [248-248] 248 82 +COV [249-249] 249 75 +COV [250-250] 250 59 +COV [251-251] 251 74 +COV [252-252] 252 79 +COV [253-253] 253 78 +COV [254-254] 254 61 +COV [255-255] 255 79 +COV [256-256] 256 74 +COV [257-257] 257 71 +COV [258-258] 258 82 +COV [259-259] 259 77 +COV [260-260] 260 76 +COV [261-261] 261 65 +COV [262-262] 262 65 +COV [263-263] 263 90 +COV [264-264] 264 70 +COV [265-265] 265 69 +COV [266-266] 266 82 +COV [267-267] 267 65 +COV [268-268] 268 91 +COV [269-269] 269 74 +COV [270-270] 270 83 +COV [271-271] 271 79 +COV [272-272] 272 69 +COV [273-273] 273 63 +COV [274-274] 274 73 +COV [275-275] 275 80 +COV [276-276] 276 68 +COV [277-277] 277 69 +COV [278-278] 278 64 +COV [279-279] 279 60 +COV [280-280] 280 67 +COV [281-281] 281 55 +COV [282-282] 282 54 +COV [283-283] 283 59 +COV [284-284] 284 74 +COV [285-285] 285 63 +COV [286-286] 286 69 +COV [287-287] 287 72 +COV [288-288] 288 72 +COV [289-289] 289 73 +COV [290-290] 290 61 +COV [291-291] 291 72 +COV [292-292] 292 72 +COV [293-293] 293 64 +COV [294-294] 294 68 +COV [295-295] 295 73 +COV [296-296] 296 60 +COV [297-297] 297 65 +COV [298-298] 298 59 +COV [299-299] 299 71 +COV [300-300] 300 51 +COV [301-301] 301 55 +COV [302-302] 302 69 +COV [303-303] 303 65 +COV [304-304] 304 49 +COV [305-305] 305 59 +COV [306-306] 306 56 +COV [307-307] 307 66 +COV [308-308] 308 68 +COV [309-309] 309 58 +COV [310-310] 310 67 +COV [311-311] 311 59 +COV [312-312] 312 50 +COV [313-313] 313 64 +COV [314-314] 314 57 +COV [315-315] 315 62 +COV [316-316] 316 56 +COV [317-317] 317 46 +COV [318-318] 318 50 +COV [319-319] 319 51 +COV [320-320] 320 51 +COV [321-321] 321 49 +COV [322-322] 322 52 +COV [323-323] 323 51 +COV [324-324] 324 46 +COV [325-325] 325 50 +COV [326-326] 326 50 +COV [327-327] 327 53 +COV [328-328] 328 59 +COV [329-329] 329 54 +COV [330-330] 330 55 +COV [331-331] 331 58 +COV [332-332] 332 58 +COV [333-333] 333 52 +COV [334-334] 334 52 +COV [335-335] 335 50 +COV [336-336] 336 60 +COV [337-337] 337 63 +COV [338-338] 338 53 +COV [339-339] 339 50 +COV [340-340] 340 57 +COV [341-341] 341 69 +COV [342-342] 342 53 +COV [343-343] 343 46 +COV [344-344] 344 63 +COV [345-345] 345 53 +COV [346-346] 346 50 +COV [347-347] 347 52 +COV [348-348] 348 48 +COV [349-349] 349 52 +COV [350-350] 350 44 +COV [351-351] 351 59 +COV [352-352] 352 51 +COV [353-353] 353 51 +COV [354-354] 354 48 +COV [355-355] 355 52 +COV [356-356] 356 81 +COV [357-357] 357 49 +COV [358-358] 358 58 +COV [359-359] 359 51 +COV [360-360] 360 39 +COV [361-361] 361 37 +COV [362-362] 362 39 +COV [363-363] 363 50 +COV [364-364] 364 41 +COV [365-365] 365 39 +COV [366-366] 366 46 +COV [367-367] 367 58 +COV [368-368] 368 40 +COV [369-369] 369 52 +COV [370-370] 370 41 +COV [371-371] 371 42 +COV [372-372] 372 45 +COV [373-373] 373 40 +COV [374-374] 374 43 +COV [375-375] 375 53 +COV [376-376] 376 42 +COV [377-377] 377 55 +COV [378-378] 378 47 +COV [379-379] 379 45 +COV [380-380] 380 40 +COV [381-381] 381 43 +COV [382-382] 382 39 +COV [383-383] 383 51 +COV [384-384] 384 43 +COV [385-385] 385 58 +COV [386-386] 386 43 +COV [387-387] 387 55 +COV [388-388] 388 50 +COV [389-389] 389 42 +COV [390-390] 390 40 +COV [391-391] 391 54 +COV [392-392] 392 40 +COV [393-393] 393 41 +COV [394-394] 394 41 +COV [395-395] 395 33 +COV [396-396] 396 36 +COV [397-397] 397 29 +COV [398-398] 398 47 +COV [399-399] 399 49 +COV [400-400] 400 31 +COV [401-401] 401 37 +COV [402-402] 402 34 +COV [403-403] 403 38 +COV [404-404] 404 40 +COV [405-405] 405 44 +COV [406-406] 406 47 +COV [407-407] 407 52 +COV [408-408] 408 40 +COV [409-409] 409 50 +COV [410-410] 410 38 +COV [411-411] 411 40 +COV [412-412] 412 35 +COV [413-413] 413 39 +COV [414-414] 414 36 +COV [415-415] 415 44 +COV [416-416] 416 42 +COV [417-417] 417 44 +COV [418-418] 418 53 +COV [419-419] 419 51 +COV [420-420] 420 41 +COV [421-421] 421 36 +COV [422-422] 422 46 +COV [423-423] 423 35 +COV [424-424] 424 38 +COV [425-425] 425 33 +COV [426-426] 426 55 +COV [427-427] 427 47 +COV [428-428] 428 34 +COV [429-429] 429 35 +COV [430-430] 430 43 +COV [431-431] 431 42 +COV [432-432] 432 35 +COV [433-433] 433 40 +COV [434-434] 434 34 +COV [435-435] 435 33 +COV [436-436] 436 42 +COV [437-437] 437 42 +COV [438-438] 438 34 +COV [439-439] 439 47 +COV [440-440] 440 44 +COV [441-441] 441 39 +COV [442-442] 442 28 +COV [443-443] 443 37 +COV [444-444] 444 45 +COV [445-445] 445 32 +COV [446-446] 446 34 +COV [447-447] 447 40 +COV [448-448] 448 31 +COV [449-449] 449 38 +COV [450-450] 450 34 +COV [451-451] 451 40 +COV [452-452] 452 27 +COV [453-453] 453 59 +COV [454-454] 454 45 +COV [455-455] 455 41 +COV [456-456] 456 44 +COV [457-457] 457 42 +COV [458-458] 458 52 +COV [459-459] 459 37 +COV [460-460] 460 48 +COV [461-461] 461 43 +COV [462-462] 462 40 +COV [463-463] 463 40 +COV [464-464] 464 39 +COV [465-465] 465 38 +COV [466-466] 466 38 +COV [467-467] 467 30 +COV [468-468] 468 24 +COV [469-469] 469 37 +COV [470-470] 470 33 +COV [471-471] 471 26 +COV [472-472] 472 28 +COV [473-473] 473 30 +COV [474-474] 474 35 +COV [475-475] 475 22 +COV [476-476] 476 31 +COV [477-477] 477 26 +COV [478-478] 478 31 +COV [479-479] 479 36 +COV [480-480] 480 37 +COV [481-481] 481 22 +COV [482-482] 482 31 +COV [483-483] 483 39 +COV [484-484] 484 38 +COV [485-485] 485 40 +COV [486-486] 486 31 +COV [487-487] 487 41 +COV [488-488] 488 40 +COV [489-489] 489 38 +COV [490-490] 490 28 +COV [491-491] 491 24 +COV [492-492] 492 35 +COV [493-493] 493 23 +COV [494-494] 494 39 +COV [495-495] 495 23 +COV [496-496] 496 24 +COV [497-497] 497 20 +COV [498-498] 498 31 +COV [499-499] 499 23 +COV [500-500] 500 38 +COV [501-501] 501 23 +COV [502-502] 502 27 +COV [503-503] 503 29 +COV [504-504] 504 17 +COV [505-505] 505 34 +COV [506-506] 506 36 +COV [507-507] 507 20 +COV [508-508] 508 25 +COV [509-509] 509 31 +COV [510-510] 510 26 +COV [511-511] 511 25 +COV [512-512] 512 39 +COV [513-513] 513 21 +COV [514-514] 514 25 +COV [515-515] 515 49 +COV [516-516] 516 28 +COV [517-517] 517 31 +COV [518-518] 518 33 +COV [519-519] 519 27 +COV [520-520] 520 35 +COV [521-521] 521 30 +COV [522-522] 522 34 +COV [523-523] 523 22 +COV [524-524] 524 27 +COV [525-525] 525 30 +COV [526-526] 526 30 +COV [527-527] 527 29 +COV [528-528] 528 25 +COV [529-529] 529 23 +COV [530-530] 530 27 +COV [531-531] 531 18 +COV [532-532] 532 19 +COV [533-533] 533 19 +COV [534-534] 534 31 +COV [535-535] 535 27 +COV [536-536] 536 26 +COV [537-537] 537 23 +COV [538-538] 538 24 +COV [539-539] 539 23 +COV [540-540] 540 21 +COV [541-541] 541 37 +COV [542-542] 542 31 +COV [543-543] 543 26 +COV [544-544] 544 29 +COV [545-545] 545 25 +COV [546-546] 546 26 +COV [547-547] 547 22 +COV [548-548] 548 32 +COV [549-549] 549 36 +COV [550-550] 550 24 +COV [551-551] 551 33 +COV [552-552] 552 18 +COV [553-553] 553 31 +COV [554-554] 554 21 +COV [555-555] 555 25 +COV [556-556] 556 20 +COV [557-557] 557 25 +COV [558-558] 558 33 +COV [559-559] 559 18 +COV [560-560] 560 37 +COV [561-561] 561 24 +COV [562-562] 562 23 +COV [563-563] 563 27 +COV [564-564] 564 26 +COV [565-565] 565 28 +COV [566-566] 566 23 +COV [567-567] 567 24 +COV [568-568] 568 29 +COV [569-569] 569 26 +COV [570-570] 570 19 +COV [571-571] 571 21 +COV [572-572] 572 25 +COV [573-573] 573 15 +COV [574-574] 574 21 +COV [575-575] 575 17 +COV [576-576] 576 16 +COV [577-577] 577 26 +COV [578-578] 578 22 +COV [579-579] 579 25 +COV [580-580] 580 28 +COV [581-581] 581 25 +COV [582-582] 582 33 +COV [583-583] 583 13 +COV [584-584] 584 19 +COV [585-585] 585 28 +COV [586-586] 586 28 +COV [587-587] 587 22 +COV [588-588] 588 23 +COV [589-589] 589 28 +COV [590-590] 590 20 +COV [591-591] 591 18 +COV [592-592] 592 36 +COV [593-593] 593 26 +COV [594-594] 594 28 +COV [595-595] 595 28 +COV [596-596] 596 18 +COV [597-597] 597 28 +COV [598-598] 598 21 +COV [599-599] 599 25 +COV [600-600] 600 20 +COV [601-601] 601 22 +COV [602-602] 602 21 +COV [603-603] 603 34 +COV [604-604] 604 21 +COV [605-605] 605 28 +COV [606-606] 606 19 +COV [607-607] 607 22 +COV [608-608] 608 19 +COV [609-609] 609 20 +COV [610-610] 610 19 +COV [611-611] 611 33 +COV [612-612] 612 20 +COV [613-613] 613 19 +COV [614-614] 614 20 +COV [615-615] 615 34 +COV [616-616] 616 26 +COV [617-617] 617 22 +COV [618-618] 618 16 +COV [619-619] 619 14 +COV [620-620] 620 26 +COV [621-621] 621 28 +COV [622-622] 622 29 +COV [623-623] 623 26 +COV [624-624] 624 32 +COV [625-625] 625 36 +COV [626-626] 626 24 +COV [627-627] 627 21 +COV [628-628] 628 20 +COV [629-629] 629 26 +COV [630-630] 630 32 +COV [631-631] 631 17 +COV [632-632] 632 22 +COV [633-633] 633 27 +COV [634-634] 634 17 +COV [635-635] 635 20 +COV [636-636] 636 27 +COV [637-637] 637 24 +COV [638-638] 638 21 +COV [639-639] 639 19 +COV [640-640] 640 38 +COV [641-641] 641 22 +COV [642-642] 642 18 +COV [643-643] 643 27 +COV [644-644] 644 19 +COV [645-645] 645 22 +COV [646-646] 646 24 +COV [647-647] 647 20 +COV [648-648] 648 15 +COV [649-649] 649 28 +COV [650-650] 650 25 +COV [651-651] 651 26 +COV [652-652] 652 25 +COV [653-653] 653 22 +COV [654-654] 654 22 +COV [655-655] 655 21 +COV [656-656] 656 16 +COV [657-657] 657 17 +COV [658-658] 658 19 +COV [659-659] 659 22 +COV [660-660] 660 18 +COV [661-661] 661 31 +COV [662-662] 662 14 +COV [663-663] 663 18 +COV [664-664] 664 13 +COV [665-665] 665 21 +COV [666-666] 666 25 +COV [667-667] 667 17 +COV [668-668] 668 21 +COV [669-669] 669 17 +COV [670-670] 670 20 +COV [671-671] 671 23 +COV [672-672] 672 18 +COV [673-673] 673 26 +COV [674-674] 674 23 +COV [675-675] 675 16 +COV [676-676] 676 18 +COV [677-677] 677 13 +COV [678-678] 678 20 +COV [679-679] 679 20 +COV [680-680] 680 21 +COV [681-681] 681 21 +COV [682-682] 682 22 +COV [683-683] 683 19 +COV [684-684] 684 20 +COV [685-685] 685 32 +COV [686-686] 686 26 +COV [687-687] 687 19 +COV [688-688] 688 19 +COV [689-689] 689 21 +COV [690-690] 690 24 +COV [691-691] 691 19 +COV [692-692] 692 30 +COV [693-693] 693 23 +COV [694-694] 694 16 +COV [695-695] 695 17 +COV [696-696] 696 26 +COV [697-697] 697 25 +COV [698-698] 698 20 +COV [699-699] 699 33 +COV [700-700] 700 30 +COV [701-701] 701 23 +COV [702-702] 702 33 +COV [703-703] 703 24 +COV [704-704] 704 18 +COV [705-705] 705 31 +COV [706-706] 706 22 +COV [707-707] 707 36 +COV [708-708] 708 32 +COV [709-709] 709 34 +COV [710-710] 710 27 +COV [711-711] 711 23 +COV [712-712] 712 23 +COV [713-713] 713 31 +COV [714-714] 714 43 +COV [715-715] 715 34 +COV [716-716] 716 21 +COV [717-717] 717 19 +COV [718-718] 718 29 +COV [719-719] 719 21 +COV [720-720] 720 24 +COV [721-721] 721 25 +COV [722-722] 722 26 +COV [723-723] 723 19 +COV [724-724] 724 33 +COV [725-725] 725 25 +COV [726-726] 726 19 +COV [727-727] 727 27 +COV [728-728] 728 22 +COV [729-729] 729 18 +COV [730-730] 730 20 +COV [731-731] 731 22 +COV [732-732] 732 19 +COV [733-733] 733 19 +COV [734-734] 734 22 +COV [735-735] 735 21 +COV [736-736] 736 24 +COV [737-737] 737 29 +COV [738-738] 738 17 +COV [739-739] 739 29 +COV [740-740] 740 30 +COV [741-741] 741 30 +COV [742-742] 742 26 +COV [743-743] 743 26 +COV [744-744] 744 29 +COV [745-745] 745 27 +COV [746-746] 746 23 +COV [747-747] 747 21 +COV [748-748] 748 26 +COV [749-749] 749 24 +COV [750-750] 750 30 +COV [751-751] 751 22 +COV [752-752] 752 31 +COV [753-753] 753 29 +COV [754-754] 754 17 +COV [755-755] 755 22 +COV [756-756] 756 30 +COV [757-757] 757 30 +COV [758-758] 758 20 +COV [759-759] 759 25 +COV [760-760] 760 24 +COV [761-761] 761 33 +COV [762-762] 762 24 +COV [763-763] 763 20 +COV [764-764] 764 12 +COV [765-765] 765 16 +COV [766-766] 766 24 +COV [767-767] 767 19 +COV [768-768] 768 19 +COV [769-769] 769 22 +COV [770-770] 770 14 +COV [771-771] 771 17 +COV [772-772] 772 16 +COV [773-773] 773 23 +COV [774-774] 774 17 +COV [775-775] 775 18 +COV [776-776] 776 21 +COV [777-777] 777 15 +COV [778-778] 778 15 +COV [779-779] 779 18 +COV [780-780] 780 24 +COV [781-781] 781 16 +COV [782-782] 782 22 +COV [783-783] 783 22 +COV [784-784] 784 10 +COV [785-785] 785 16 +COV [786-786] 786 13 +COV [787-787] 787 9 +COV [788-788] 788 20 +COV [789-789] 789 23 +COV [790-790] 790 16 +COV [791-791] 791 23 +COV [792-792] 792 22 +COV [793-793] 793 24 +COV [794-794] 794 15 +COV [795-795] 795 26 +COV [796-796] 796 23 +COV [797-797] 797 23 +COV [798-798] 798 12 +COV [799-799] 799 12 +COV [800-800] 800 20 +COV [801-801] 801 21 +COV [802-802] 802 15 +COV [803-803] 803 17 +COV [804-804] 804 17 +COV [805-805] 805 11 +COV [806-806] 806 10 +COV [807-807] 807 18 +COV [808-808] 808 16 +COV [809-809] 809 19 +COV [810-810] 810 22 +COV [811-811] 811 19 +COV [812-812] 812 10 +COV [813-813] 813 17 +COV [814-814] 814 10 +COV [815-815] 815 21 +COV [816-816] 816 28 +COV [817-817] 817 11 +COV [818-818] 818 19 +COV [819-819] 819 21 +COV [820-820] 820 12 +COV [821-821] 821 18 +COV [822-822] 822 11 +COV [823-823] 823 21 +COV [824-824] 824 13 +COV [825-825] 825 16 +COV [826-826] 826 18 +COV [827-827] 827 20 +COV [828-828] 828 23 +COV [829-829] 829 12 +COV [830-830] 830 20 +COV [831-831] 831 9 +COV [832-832] 832 19 +COV [833-833] 833 14 +COV [834-834] 834 23 +COV [835-835] 835 18 +COV [836-836] 836 20 +COV [837-837] 837 14 +COV [838-838] 838 18 +COV [839-839] 839 15 +COV [840-840] 840 18 +COV [841-841] 841 8 +COV [842-842] 842 22 +COV [843-843] 843 15 +COV [844-844] 844 23 +COV [845-845] 845 16 +COV [846-846] 846 20 +COV [847-847] 847 18 +COV [848-848] 848 13 +COV [849-849] 849 14 +COV [850-850] 850 19 +COV [851-851] 851 18 +COV [852-852] 852 19 +COV [853-853] 853 20 +COV [854-854] 854 16 +COV [855-855] 855 11 +COV [856-856] 856 18 +COV [857-857] 857 9 +COV [858-858] 858 15 +COV [859-859] 859 25 +COV [860-860] 860 17 +COV [861-861] 861 18 +COV [862-862] 862 14 +COV [863-863] 863 22 +COV [864-864] 864 9 +COV [865-865] 865 15 +COV [866-866] 866 20 +COV [867-867] 867 9 +COV [868-868] 868 20 +COV [869-869] 869 15 +COV [870-870] 870 19 +COV [871-871] 871 12 +COV [872-872] 872 23 +COV [873-873] 873 13 +COV [874-874] 874 25 +COV [875-875] 875 15 +COV [876-876] 876 15 +COV [877-877] 877 22 +COV [878-878] 878 19 +COV [879-879] 879 12 +COV [880-880] 880 22 +COV [881-881] 881 16 +COV [882-882] 882 23 +COV [883-883] 883 12 +COV [884-884] 884 15 +COV [885-885] 885 18 +COV [886-886] 886 16 +COV [887-887] 887 11 +COV [888-888] 888 19 +COV [889-889] 889 24 +COV [890-890] 890 17 +COV [891-891] 891 11 +COV [892-892] 892 24 +COV [893-893] 893 25 +COV [894-894] 894 16 +COV [895-895] 895 21 +COV [896-896] 896 17 +COV [897-897] 897 23 +COV [898-898] 898 18 +COV [899-899] 899 16 +COV [900-900] 900 14 +COV [901-901] 901 25 +COV [902-902] 902 19 +COV [903-903] 903 19 +COV [904-904] 904 18 +COV [905-905] 905 15 +COV [906-906] 906 9 +COV [907-907] 907 20 +COV [908-908] 908 11 +COV [909-909] 909 14 +COV [910-910] 910 20 +COV [911-911] 911 12 +COV [912-912] 912 15 +COV [913-913] 913 8 +COV [914-914] 914 20 +COV [915-915] 915 15 +COV [916-916] 916 19 +COV [917-917] 917 16 +COV [918-918] 918 13 +COV [919-919] 919 23 +COV [920-920] 920 7 +COV [921-921] 921 17 +COV [922-922] 922 16 +COV [923-923] 923 13 +COV [924-924] 924 15 +COV [925-925] 925 7 +COV [926-926] 926 12 +COV [927-927] 927 3 +COV [928-928] 928 16 +COV [929-929] 929 10 +COV [930-930] 930 12 +COV [931-931] 931 11 +COV [932-932] 932 15 +COV [933-933] 933 12 +COV [934-934] 934 18 +COV [935-935] 935 15 +COV [936-936] 936 16 +COV [937-937] 937 10 +COV [938-938] 938 11 +COV [939-939] 939 16 +COV [940-940] 940 20 +COV [941-941] 941 18 +COV [942-942] 942 20 +COV [943-943] 943 17 +COV [944-944] 944 14 +COV [945-945] 945 10 +COV [946-946] 946 15 +COV [947-947] 947 12 +COV [948-948] 948 7 +COV [949-949] 949 9 +COV [950-950] 950 16 +COV [951-951] 951 9 +COV [952-952] 952 25 +COV [953-953] 953 16 +COV [954-954] 954 12 +COV [955-955] 955 12 +COV [956-956] 956 24 +COV [957-957] 957 19 +COV [958-958] 958 11 +COV [959-959] 959 14 +COV [960-960] 960 18 +COV [961-961] 961 17 +COV [962-962] 962 14 +COV [963-963] 963 18 +COV [964-964] 964 15 +COV [965-965] 965 14 +COV [966-966] 966 9 +COV [967-967] 967 7 +COV [968-968] 968 12 +COV [969-969] 969 20 +COV [970-970] 970 20 +COV [971-971] 971 13 +COV [972-972] 972 14 +COV [973-973] 973 11 +COV [974-974] 974 12 +COV [975-975] 975 16 +COV [976-976] 976 13 +COV [977-977] 977 16 +COV [978-978] 978 11 +COV [979-979] 979 11 +COV [980-980] 980 22 +COV [981-981] 981 13 +COV [982-982] 982 16 +COV [983-983] 983 19 +COV [984-984] 984 17 +COV [985-985] 985 16 +COV [986-986] 986 13 +COV [987-987] 987 14 +COV [988-988] 988 24 +COV [989-989] 989 10 +COV [990-990] 990 16 +COV [991-991] 991 12 +COV [992-992] 992 16 +COV [993-993] 993 18 +COV [994-994] 994 16 +COV [995-995] 995 15 +COV [996-996] 996 14 +COV [997-997] 997 20 +COV [998-998] 998 17 +COV [999-999] 999 13 +COV [1000-1000] 1000 19 +COV [1000<] 1000 19954 +# GC-depth. Use `grep ^GCD | cut -f 2-` to extract this part. The columns are: GC%, unique sequence percentiles, 10th, 25th, 50th, 75th and 90th depth percentile +GCD 0.0 0.004 0.000 0.000 0.002 0.002 0.002 +GCD 2.0 0.008 0.002 0.002 0.002 0.007 0.012 +GCD 3.0 0.010 0.005 0.005 0.005 0.007 0.007 +GCD 4.0 0.014 0.002 0.002 0.002 0.002 0.005 +GCD 5.0 0.015 0.005 0.005 0.005 0.005 0.005 +GCD 5.3 0.015 0.020 0.020 0.020 0.020 0.020 +GCD 6.0 0.016 0.002 0.002 0.002 0.002 0.002 +GCD 7.0 0.018 0.005 0.005 0.005 0.005 0.005 +GCD 10.0 0.020 0.002 0.002 0.002 0.010 0.010 +GCD 11.0 0.021 0.005 0.005 0.005 0.005 0.005 +GCD 12.0 0.024 0.002 0.002 0.002 0.002 0.002 +GCD 13.0 0.026 0.005 0.005 0.005 0.005 0.005 +GCD 14.0 0.032 0.002 0.002 0.002 0.005 0.005 +GCD 15.0 0.035 0.005 0.005 0.005 0.005 0.017 +GCD 16.0 0.039 0.002 0.002 0.002 0.002 0.005 +GCD 17.0 0.042 0.007 0.007 0.007 0.010 0.010 +GCD 18.0 0.045 0.002 0.002 0.002 0.007 0.007 +GCD 19.0 0.049 0.005 0.005 0.007 0.015 0.020 +GCD 20.0 0.057 0.002 0.002 0.002 0.005 0.007 +GCD 21.0 0.061 0.002 0.002 0.005 0.005 0.007 +GCD 22.0 0.067 0.002 0.002 0.002 0.005 0.007 +GCD 23.0 0.074 0.002 0.005 0.007 0.010 0.022 +GCD 24.0 0.092 0.002 0.002 0.002 0.005 0.010 +GCD 25.0 0.104 0.002 0.005 0.005 0.005 0.005 +GCD 26.0 0.120 0.002 0.002 0.002 0.005 0.010 +GCD 27.0 0.132 0.002 0.005 0.007 0.015 0.919 +GCD 28.0 0.146 0.002 0.002 0.005 0.012 0.044 +GCD 29.0 0.158 0.005 0.005 0.007 0.010 0.020 +GCD 30.0 0.180 0.002 0.002 0.012 0.027 0.301 +GCD 31.0 0.231 0.007 0.020 0.252 0.309 0.321 +GCD 32.0 0.420 0.007 0.235 0.287 0.316 0.336 +GCD 33.0 1.041 0.235 0.272 0.299 0.321 0.345 +GCD 34.0 2.836 0.245 0.277 0.301 0.326 0.345 +GCD 35.0 6.596 0.250 0.279 0.306 0.331 0.353 +GCD 36.0 12.687 0.252 0.282 0.309 0.333 0.355 +GCD 37.0 21.038 0.252 0.284 0.309 0.336 0.360 +GCD 38.0 30.804 0.257 0.287 0.314 0.338 0.363 +GCD 39.0 40.686 0.255 0.287 0.314 0.341 0.368 +GCD 40.0 50.215 0.255 0.289 0.316 0.341 0.368 +GCD 41.0 58.702 0.252 0.287 0.316 0.343 0.370 +GCD 42.0 66.415 0.252 0.284 0.314 0.341 0.370 +GCD 43.0 73.332 0.255 0.287 0.314 0.341 0.370 +GCD 44.0 79.256 0.252 0.284 0.314 0.341 0.370 +GCD 45.0 84.178 0.255 0.284 0.314 0.341 0.372 +GCD 46.0 88.180 0.250 0.282 0.311 0.341 0.375 +GCD 47.0 91.323 0.247 0.282 0.311 0.341 0.375 +GCD 48.0 93.860 0.245 0.277 0.309 0.341 0.375 +GCD 49.0 95.781 0.250 0.279 0.309 0.341 0.375 +GCD 50.0 97.275 0.240 0.277 0.309 0.336 0.375 +GCD 51.0 98.341 0.247 0.277 0.306 0.341 0.380 +GCD 52.0 99.080 0.240 0.274 0.306 0.341 0.382 +GCD 53.0 99.534 0.240 0.274 0.306 0.345 0.399 +GCD 54.0 99.780 0.221 0.265 0.299 0.336 0.390 +GCD 55.0 99.894 0.233 0.267 0.296 0.326 0.350 +GCD 56.0 99.952 0.211 0.255 0.299 0.331 0.392 +GCD 57.0 99.976 0.223 0.265 0.309 0.345 0.639 +GCD 58.0 99.985 0.002 0.265 0.490 0.615 0.649 +GCD 59.0 99.988 0.260 0.260 0.289 0.835 0.835 +GCD 60.0 99.992 0.002 0.002 0.123 0.289 0.404 +GCD 62.0 99.995 0.002 0.002 0.002 0.002 0.713 +GCD 63.0 99.996 0.652 0.652 0.652 0.711 0.711 +GCD 64.0 99.998 0.622 0.622 0.622 11.324 11.324 +GCD 65.0 99.999 0.002 0.002 0.002 0.002 0.002 +GCD 66.0 100.000 0.002 0.002 0.002 0.002 0.002 diff --git a/src/multiqc/test_data/script.sh b/src/multiqc/test_data/script.sh new file mode 100644 index 00000000..614b032e --- /dev/null +++ b/src/multiqc/test_data/script.sh @@ -0,0 +1,9 @@ +# multiqc test data + +# Test data from https://github.com/snakemake/snakemake-wrappers/tree/master/bio/busco/test + +if [ ! -d /tmp/snakemake-wrappers ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers /tmp/snakemake-wrappers +fi + +cp -r /tmp/snakemake-wrappers/bio/multiqc/test/samtools_stats/* src/multiqc/test_data diff --git a/src/pear/config.vsh.yaml b/src/pear/config.vsh.yaml new file mode 100644 index 00000000..d6dbe6c9 --- /dev/null +++ b/src/pear/config.vsh.yaml @@ -0,0 +1,161 @@ +name: pear +description: | + PEAR is an ultrafast, memory-efficient and highly accurate pair-end read merger. It is fully parallelized and can run with as low as just a few kilobytes of memory. + + PEAR evaluates all possible paired-end read overlaps and without requiring the target fragment size as input. In addition, it implements a statistical test for minimizing false-positive results. Together with a highly optimized implementation, it can merge millions of paired end reads within a couple of minutes on a standard desktop computer. +keywords: [ "pair-end", "read", "merge" ] +links: + homepage: https://cme.h-its.org/exelixis/web/software/pear + repository: https://github.com/tseemann/PEAR + documentation: https://cme.h-its.org/exelixis/web/software/pear/doc.html +references: + doi: 10.1093/bioinformatics/btt593 +license: "CC-BY-NC-SA-3.0" +requirements: + commands: [ pear , gzip ] +argument_groups: + - name: Inputs + arguments: + - name: --forward_fastq + alternatives: -f + type: file + description: Forward paired-end FASTQ file + required: true + example: "forward.fastq" + - name: --reverse_fastq + alternatives: -r + type: file + description: Reverse paired-end FASTQ file + required: true + example: "reverse.fastq" + - name: Outputs + arguments: + - name: --assembled + type: file + description: The output file containing assembled reads. Can be compressed with gzip. + required: true + direction: output + - name: --unassembled_forward + type: file + description: The output file containing forward reads that could not be assembled. Can be compressed with gzip. + required: true + direction: output + - name: --unassembled_reverse + type: file + description: The output file containing reverse reads that could not be assembled. Can be compressed with gzip. + required: true + direction: output + - name: --discarded + type: file + description: The output file containing reads that were discarded due to too low quality or too many uncalled bases. Can be compressed with gzip. + required: true + direction: output + - name: Arguments + arguments: + - name: --p_value + alternatives: -p + type: double + description: | + Specify a p-value for the statistical test. If the computed p-value of a possible assembly exceeds the specified p-value then paired-end read will not be assembled. Valid options are: 0.0001, 0.001, 0.01, 0.05 and 1.0. Setting 1.0 disables the test. + example: 0.01 + required: false + - name: --min_overlap + alternatives: -v + type: integer + description: | + Specify the minimum overlap size. The minimum overlap may be set to 1 when the statistical test is used. However, further restricting the minimum overlap size to a proper value may reduce false-positive assembles. + required: false + example: 10 + - name: --max_assembly_length + alternatives: -m + type: integer + description: | + Specify the maximum possible length of the assembled sequences. Setting this value to 0 disables the restriction and assembled sequences may be arbitrary long. + required: false + example: 0 + - name: --min_assembly_length + alternatives: -n + type: integer + description: | + Specify the minimum possible length of the assembled sequences. Setting this value to 0 disables the restriction and assembled sequences may be arbitrary short. + required: false + example: 0 + - name: --min_trim_length + alternatives: -t + type: integer + description: | + Specify the minimum length of reads after trimming the low quality part (see option -q) + required: false + example: 1 + - name: --quality_threshold + alternatives: -q + type: integer + description: | + Specify the quality threshold for trimming the low quality part of a read. If the quality scores of two consecutive bases are strictly less than the specified threshold, the rest of the read will be trimmed. + required: false + example: 0 + - name: --max_uncalled_base + alternatives: -u + type: double + description: | + Specify the maximal proportion of uncalled bases in a read. Setting this value to 0 will cause PEAR to discard all reads containing uncalled bases. The other extreme setting is 1 which causes PEAR to process all reads independent on the number of uncalled bases. + example: 1.0 + required: false + - name: --test_method + alternatives: -g + type: integer + description: | + Specify the type of statistical test. Two options are available. 1: Given the minimum allowed overlap, test using the highest OES. Note that due to its discrete nature, this test usually yields a lower p-value for the assembled read than the cut- off (specified by -p). For example, setting the cut-off to 0.05 using this test, the assembled reads might have an actual p-value of 0.02. + 2. Use the acceptance probability (m.a.p). This test methods computes the same probability as test method 1. However, it assumes that the minimal overlap is the observed overlap with the highest OES, instead of the one specified by -v. Therefore, this is not a valid statistical test and the 'p-value' is in fact the maximal probability for accepting the assembly. Nevertheless, we observed in practice that for the case the actual overlap sizes are relatively small, test 2 can correctly assemble more reads with only slightly higher false-positive rate. + required: false + example: 1 + - name: --emperical_freqs + alternatives: -e + type: boolean_true + description: | + Disable empirical base frequencies. + - name: --score_method + alternatives: -s + type: integer + description: | + Specify the scoring method. 1. OES with +1 for match and -1 for mismatch. 2: Assembly score (AS). Use +1 for match and -1 for mismatch multiplied by base quality scores. 3: Ignore quality scores and use +1 for a match and -1 for a mismatch. + required: false + example: 2 + - name: --phred_base + alternatives: -b + type: integer + description: | + Base PHRED quality score. + required: false + example: 33 + - name: --cap + alternatives: -c + type: integer + description: | + Specify the upper bound for the resulting quality score. If set to zero, capping is disabled. + required: false + example: 40 + - name: --nbase + alternatives: -z + type: boolean_true + description: | + When merging a base-pair that consists of two non-equal bases out of which none is degenerate, set the merged base to N and use the highest quality score of the two bases +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/pear:0.9.6--h9d449c0_10 + setup: + - type: docker + run: | + version=$(pear -h | grep 'PEAR v' | sed 's/PEAR v//' | sed 's/ .*//') && \ + echo "pear: $version" > /var/software_versions.txt +runners: + - type: executable + - type: nextflow \ No newline at end of file diff --git a/src/pear/help.txt b/src/pear/help.txt new file mode 100644 index 00000000..d8e42285 --- /dev/null +++ b/src/pear/help.txt @@ -0,0 +1,91 @@ +```bash +pear -h +``` + + ____ _____ _ ____ +| _ \| ____| / \ | _ \ +| |_) | _| / _ \ | |_) | +| __/| |___ / ___ \| _ < +|_| |_____/_/ \_\_| \_\ +PEAR v0.9.6 [January 15, 2015] - [+bzlib +zlib] + +Citation - PEAR: a fast and accurate Illumina Paired-End reAd mergeR +Zhang et al (2014) Bioinformatics 30(5): 614-620 | doi:10.1093/bioinformatics/btt593 + +License: Creative Commons Licence +Bug-reports and requests to: Tomas.Flouri@h-its.org and Jiajie.Zhang@h-its.org + + +Usage: pear +Standard (mandatory): + -f, --forward-fastq Forward paired-end FASTQ file. + -r, --reverse-fastq Reverse paired-end FASTQ file. + -o, --output Output filename. +Optional: + -p, --p-value Specify a p-value for the statistical test. If the computed + p-value of a possible assembly exceeds the specified p-value + then paired-end read will not be assembled. Valid options + are: 0.0001, 0.001, 0.01, 0.05 and 1.0. Setting 1.0 disables + the test. (default: 0.01) + -v, --min-overlap Specify the minimum overlap size. The minimum overlap may be + set to 1 when the statistical test is used. However, further + restricting the minimum overlap size to a proper value may + reduce false-positive assembles. (default: 10) + -m, --max-assembly-length Specify the maximum possible length of the assembled + sequences. Setting this value to 0 disables the restriction + and assembled sequences may be arbitrary long. (default: 0) + -n, --min-assembly-length Specify the minimum possible length of the assembled + sequences. Setting this value to 0 disables the restriction + and assembled sequences may be arbitrary short. (default: + 50) + -t, --min-trim-length Specify the minimum length of reads after trimming the low + quality part (see option -q). (default: 1) + -q, --quality-threshold Specify the quality score threshold for trimming the low + quality part of a read. If the quality scores of two + consecutive bases are strictly less than the specified + threshold, the rest of the read will be trimmed. (default: + 0) + -u, --max-uncalled-base Specify the maximal proportion of uncalled bases in a read. + Setting this value to 0 will cause PEAR to discard all reads + containing uncalled bases. The other extreme setting is 1 + which causes PEAR to process all reads independent on the + number of uncalled bases. (default: 1) + -g, --test-method Specify the type of statistical test. Two options are + available. (default: 1) + 1: Given the minimum allowed overlap, test using the highest + OES. Note that due to its discrete nature, this test usually + yields a lower p-value for the assembled read than the cut- + off (specified by -p). For example, setting the cut-off to + 0.05 using this test, the assembled reads might have an + actual p-value of 0.02. + + 2. Use the acceptance probability (m.a.p). This test methods + computes the same probability as test method 1. However, it + assumes that the minimal overlap is the observed overlap + with the highest OES, instead of the one specified by -v. + Therefore, this is not a valid statistical test and the + 'p-value' is in fact the maximal probability for accepting + the assembly. Nevertheless, we observed in practice that for + the case the actual overlap sizes are relatively small, test + 2 can correctly assemble more reads with only slightly + higher false-positive rate. + -e, --empirical-freqs Disable empirical base frequencies. (default: use empirical + base frequencies) + -s, --score-method Specify the scoring method. (default: 2) + 1. OES with +1 for match and -1 for mismatch. + 2: Assembly score (AS). Use +1 for match and -1 for mismatch + multiplied by base quality scores. + 3: Ignore quality scores and use +1 for a match and -1 for a + mismatch. + -b, --phred-base Base PHRED quality score. (default: 33) + -y, --memory Specify the amount of memory to be used. The number may be + followed by one of the letters K, M, or G denoting + Kilobytes, Megabytes and Gigabytes, respectively. Bytes are + assumed in case no letter is specified. + -c, --cap Specify the upper bound for the resulting quality score. If + set to zero, capping is disabled. (default: 40) + -j, --threads Number of threads to use + -z, --nbase When merging a base-pair that consists of two non-equal + bases out of which none is degenerate, set the merged base + to N and use the highest quality score of the two bases + -h, --help This help screen. \ No newline at end of file diff --git a/src/pear/script.sh b/src/pear/script.sh new file mode 100644 index 00000000..f7d6a28f --- /dev/null +++ b/src/pear/script.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +[[ "$par_emperical_freqs" == "false" ]] && unset par_emperical_freqs +[[ "$par_nbase" == "false" ]] && unset par_nbase + +if [[ "${par_forward_fastq##*.}" == "gz" ]]; then + gunzip $par_forward_fastq + par_forward_fastq=${par_forward_fastq%.*} +fi +if [[ "${par_reverse_fastq##*.}" == "gz" ]]; then + gunzip $par_reverse_fastq + par_reverse_fastq=${par_reverse_fastq%.*} +fi + +output_dir=$(mktemp -d -p "$meta_temp_dir" "pear.XXXXXX") + +pear \ + -f "$par_forward_fastq" \ + -r "$par_reverse_fastq" \ + -o "$output_dir" \ + ${par_p_value:+-p "${par_p_value}"} \ + ${par_min_overlap:+-v "${par_min_overlap}"} \ + ${par_max_assembly_length:+-m "${par_max_assembly_length}"} \ + ${par_min_assembly_length:+-n "${par_min_assembly_length}"} \ + ${par_min_trim_length:+-t "${par_min_trim_length}"} \ + ${par_quality_threshold:+-q "${par_quality_threshold}"} \ + ${par_max_uncalled_base:+-u "${par_max_uncalled_base}"} \ + ${par_test_method:+-g "${par_test_method}"} \ + ${par_score_method:+-s "${par_score_method}"} \ + ${par_phred_base:+-b "${par_phred_base}"} \ + ${meta_memory_mb:+--memory "${meta_memory_mb}M"} \ + ${par_cap:+-c "${par_cap}"} \ + ${meta_cpus:+-j "${meta_cpus}"} \ + ${par_emperical_freqs:+-e} \ + ${par_nbase:+-z} + + +if [[ "${par_assembled##*.}" == "gz" ]]; then + gzip -9 -c ${output_dir}.assembled.fastq > ${par_assembled} +else + mv ${output_dir}.assembled.fastq ${par_assembled} +fi + +if [[ "${par_unassembled_forward##*.}" == "gz" ]]; then + gzip -9 -c ${output_dir}.unassembled.forward.fastq > ${par_unassembled_forward} +else + mv ${output_dir}.unassembled.forward.fastq ${par_unassembled_forward} +fi + +if [[ "${par_unassembled_reverse##*.}" == "gz" ]]; then + gzip -9 -c ${output_dir}.unassembled.reverse.fastq > ${par_unassembled_reverse} +else + mv ${output_dir}.unassembled.reverse.fastq ${par_unassembled_reverse} +fi + +if [[ "${par_discarded##*.}" == "gz" ]]; then + gzip -9 -c ${output_dir}.discarded.fastq > ${par_discarded} +else + mv ${output_dir}.discarded.fastq ${par_discarded} +fi diff --git a/src/pear/test.sh b/src/pear/test.sh new file mode 100644 index 00000000..67870bf4 --- /dev/null +++ b/src/pear/test.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -e + +dir_in="${meta_resources_dir%/}/test_data" + +echo "> Run PEAR" +"$meta_executable" \ + --forward_fastq "$dir_in/a.1.fastq" \ + --reverse_fastq "$dir_in/a.2.fastq" \ + --assembled "test.assembled.fastq.gz" \ + --unassembled_forward "test.unassembled.forward.fastq.gz" \ + --unassembled_reverse "test.unassembled.reverse.fastq.gz" \ + --discarded "test.discarded.fastq.gz" \ + --p_value 0.01 + +echo ">> Checking output" +[ ! -f "test.assembled.fastq.gz" ] && echo "Output file test.assembled.fastq.gz does not exist" && exit 1 +[ ! -f "test.unassembled.forward.fastq.gz" ] && echo "Output file test.unassembled.forward.fastq.gz does not exist" && exit 1 +[ ! -f "test.unassembled.reverse.fastq.gz" ] && echo "Output file test.unassembled.reverse.fastq.gz does not exist" && exit 1 +[ ! -f "test.discarded.fastq.gz" ] && echo "Output file ftest.discarded.fastq.gz does not exist" && exit 1 + +echo "> Test successful" \ No newline at end of file diff --git a/src/pear/test_data/a.1.fastq b/src/pear/test_data/a.1.fastq new file mode 100644 index 00000000..42735560 --- /dev/null +++ b/src/pear/test_data/a.1.fastq @@ -0,0 +1,4 @@ +@1 +ACGGCAT ++ +!!!!!!! diff --git a/src/pear/test_data/a.2.fastq b/src/pear/test_data/a.2.fastq new file mode 100644 index 00000000..42735560 --- /dev/null +++ b/src/pear/test_data/a.2.fastq @@ -0,0 +1,4 @@ +@1 +ACGGCAT ++ +!!!!!!! diff --git a/src/pear/test_data/script.sh b/src/pear/test_data/script.sh new file mode 100755 index 00000000..016910a8 --- /dev/null +++ b/src/pear/test_data/script.sh @@ -0,0 +1,10 @@ +# pear test data + +# Test data was obtained from https://github.com/snakemake/snakemake-wrappers/tree/master/bio/fastp/test + +if [ ! -d /tmp/snakemake-wrappers ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers /tmp/snakemake-wrappers +fi + +cp -r /tmp/snakemake-wrappers/bio/fastp/test/reads/pe/* src/pear/test_data + diff --git a/src/salmon/salmon_index/config.vsh.yaml b/src/salmon/salmon_index/config.vsh.yaml new file mode 100644 index 00000000..f24cd3a9 --- /dev/null +++ b/src/salmon/salmon_index/config.vsh.yaml @@ -0,0 +1,113 @@ +name: salmon_index +namespace: salmon +description: | + Salmon is a tool for wicked-fast transcript quantification from RNA-seq data. It can either make use of pre-computed alignments (in the form of a SAM/BAM file) to the transcripts rather than the raw reads, or can be run in the mapping-based mode. This component creates a salmon index for the transcriptome to use Salmon in the mapping-based mode. It is generally recommend that you build a decoy-aware transcriptome file. This is done using the entire genome of the organism as the decoy sequence by concatenating the genome to the end of the transcriptome to be indexed and populating the decoys.txt file with the chromosome names. +keywords: ["Transcriptome", "Index"] +links: + homepage: https://salmon.readthedocs.io/en/latest/salmon.html + documentation: https://salmon.readthedocs.io/en/latest/salmon.html + repository: https://github.com/COMBINE-lab/salmon +references: + doi: 10.1038/nmeth.4197 +license: GPL-3.0 +requirements: + commands: [ salmon ] + +argument_groups: + - name: Inputs + arguments: + - name: --genome + type: file + description: | + Genome of the organism to prepare the set of decoy sequences. Required to build decoy-aware transccriptome. + required: false + example: genome.fasta + - name: --transcripts + alternatives: ["-t"] + type: file + description: | + Transcript fasta file. + required: true + example: transcriptome.fasta + - name: --kmer_len + alternatives: ["-k"] + type: integer + description: | + The size of k-mers that should be used for the quasi index. + required: false + example: 31 + - name: --gencode + type: boolean_true + description: | + This flag will expect the input transcript fasta to be in GENCODE format, and will split the transcript name at the first '|' character. These reduced names will be used in the output and when looking for these transcripts in a gene to transcript GTF. + - name: --features + type: boolean_true + description: | + This flag will expect the input reference to be in the tsv file format, and will split the feature name at the first 'tab' character. These reduced names will be used in the output and when looking for the sequence of the features.GTF. + - name: --keep_duplicates + type: boolean_true + description: | + This flag will disable the default indexing behavior of discarding sequence-identical duplicate transcripts. If this flag is passed, then duplicate transcripts that appear in the input will be retained and quantified separately. + - name: --keep_fixed_fasta + type: boolean_true + description: | + Retain the fixed fasta file (without short transcripts and duplicates, clipped, etc.) generated during indexing. + - name: --filter_size + alternatives: ["-f"] + type: integer + description: | + The size of the Bloom filter that will be used by TwoPaCo during indexing. The filter will be of size 2^{filter_size}. The default value of -1 means that the filter size will be automatically set based on the number of distinct k-mers in the input, as estimated by nthll. + required: false + example: -1 + - name: --sparse + type: boolean_true + description: | + Build the index using a sparse sampling of k-mer positions This will require less memory (especially during quantification), but will take longer to construct and can slow down mapping / alignment. + - name: --decoys + alternatives: ["-d"] + type: file + description: | + Treat these sequences ids from the reference as the decoys that may have sequence homologous to some known transcript. For example in case of the genome, provide a list of chromosome names (one per line). + required: false + example: decoys.txt + - name: --no_clip + type: boolean_true + description: | + Don't clip poly-A tails from the ends of target sequences. + - name: --type + alternatives: ["-n"] + type: string + description: | + The type of index to build; the only option is "puff" in this version of salmon. + required: false + example: puff + + - name: Output + arguments: + - name: --index + alternatives: ["-i"] + type: file + direction: output + description: | + Salmon index + required: true + example: Salmon_index + +resources: + - type: bash_script + path: script.sh + +test_resources: + - type: bash_script + path: test.sh + +engines: + - type: docker + image: quay.io/biocontainers/salmon:1.10.2--hecfa306_0 + setup: + - type: docker + run: | + salmon index -v 2>&1 | sed 's/salmon \([0-9.]*\)/salmon: \1/' > /var/software_versions.txt +runners: + - type: executable + - type: nextflow \ No newline at end of file diff --git a/src/salmon/salmon_index/help.txt b/src/salmon/salmon_index/help.txt new file mode 100644 index 00000000..bcca44d0 --- /dev/null +++ b/src/salmon/salmon_index/help.txt @@ -0,0 +1,66 @@ +```bash +salmon index -h +``` + +Version Info: This is the most recent version of salmon. + +Index +========== +Creates a salmon index. + +Command Line Options: + -v [ --version ] print version string + -h [ --help ] produce help message + -t [ --transcripts ] arg Transcript fasta file. + -k [ --kmerLen ] arg (=31) The size of k-mers that should be used for the + quasi index. + -i [ --index ] arg salmon index. + --gencode This flag will expect the input transcript + fasta to be in GENCODE format, and will split + the transcript name at the first '|' character. + These reduced names will be used in the output + and when looking for these transcripts in a + gene to transcript GTF. + --features This flag will expect the input reference to be + in the tsv file format, and will split the + feature name at the first 'tab' character. + These reduced names will be used in the output + and when looking for the sequence of the + features.GTF. + --keepDuplicates This flag will disable the default indexing + behavior of discarding sequence-identical + duplicate transcripts. If this flag is passed, + then duplicate transcripts that appear in the + input will be retained and quantified + separately. + -p [ --threads ] arg (=2) Number of threads to use during indexing. + --keepFixedFasta Retain the fixed fasta file (without short + transcripts and duplicates, clipped, etc.) + generated during indexing + -f [ --filterSize ] arg (=-1) The size of the Bloom filter that will be used + by TwoPaCo during indexing. The filter will be + of size 2^{filterSize}. The default value of -1 + means that the filter size will be + automatically set based on the number of + distinct k-mers in the input, as estimated by + nthll. + --tmpdir arg The directory location that will be used for + TwoPaCo temporary files; it will be created if + need be and be removed prior to indexing + completion. The default value will cause a + (temporary) subdirectory of the salmon index + directory to be used for this purpose. + --sparse Build the index using a sparse sampling of + k-mer positions This will require less memory + (especially during quantification), but will + take longer to construct and can slow down + mapping / alignment + -d [ --decoys ] arg Treat these sequences ids from the reference as + the decoys that may have sequence homologous to + some known transcript. for example in case of + the genome, provide a list of chromosome name + --- one per line + -n [ --no-clip ] Don't clip poly-A tails from the ends of target + sequences + --type arg (=puff) The type of index to build; the only option is + "puff" in this version of salmon. diff --git a/src/salmon/salmon_index/script.sh b/src/salmon/salmon_index/script.sh new file mode 100644 index 00000000..c2b9e7a0 --- /dev/null +++ b/src/salmon/salmon_index/script.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +set -e + +## VIASH START +## VIASH END + +[[ "$par_gencode" == "false" ]] && unset par_gencode +[[ "$par_features" == "false" ]] && unset par_features +[[ "$par_keep_duplicates" == "false" ]] && unset par_keep_duplicates +[[ "$par_keep_fixed_fasta" == "false" ]] && unset par_keep_fixed_fasta +[[ "$par_sparse" == "false" ]] && unset par_sparse +[[ "$par_no_clip" == "false" ]] && unset par_no_clip + +tmp_dir=$(mktemp -d -p "$meta_temp_dir" "${meta_functionality_name}_XXXXXX") +mkdir -p "$tmp_dir/temp" + +if [[ -f "$par_genome" ]] && [[ ! "$par_decoys" ]]; then + filename="$(basename -- $par_genome)" + decoys="decoys.txt" + if [ ${filename##*.} == "gz" ]; then + grep '^>' <(gunzip -c $par_genome) | cut -d ' ' -f 1 > $decoys + gentrome="gentrome.fa.gz" + else + grep '^>' $par_genome | cut -d ' ' -f 1 > $decoys + gentrome="gentrome.fa" + fi + sed -i.bak -e 's/>//g' $decoys + cat $par_transcripts $par_genome > $gentrome +else + gentrome=$par_transcripts + decoys=$par_decoys +fi + +salmon index \ + -t "$gentrome" \ + --tmpdir "$tmp_dir/temp" \ + ${meta_cpus:+--threads "${meta_cpus}"} \ + -i "$par_index" \ + ${par_kmer_len:+-k "${par_kmer_len}"} \ + ${par_gencode:+--gencode} \ + ${par_features:+--features} \ + ${par_keep_duplicates:+--keepDuplicates} \ + ${par_keep_fixed_fasta:+--keepFixedFasta} \ + ${par_filter_size:+-f "${par_filter_size}"} \ + ${par_sparse:+--sparse} \ + ${decoys:+-d "${decoys}"} \ + ${par_no_clip:+--no-clip} \ + ${par_type:+--type "${par_type}"} \ No newline at end of file diff --git a/src/salmon/salmon_index/test.sh b/src/salmon/salmon_index/test.sh new file mode 100644 index 00000000..091f11a9 --- /dev/null +++ b/src/salmon/salmon_index/test.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -e + +echo "> Prepare test data" + +dir_in="test_data" +mkdir -p "$dir_in" + +cat > "$dir_in/transcriptome.fasta" <<'EOF' +>contig1 +AGCTCCAGATTCGCTCAGGCCCTTGATCATCAGTCGTCGTCGTCTTCGATTTGCCAGAGG +AGTTTAGATGAAGAATGTCAAGGATGTTCCTCCCTGCCCTCCCATCTAGCCAAGAACATT +TCCAAGAAGATAAAACTGTCACTGAGACAGGTCTGGATGCGCCCTAGGGGCAAATAGAGA +>contig2 +AGGCCTTTACCACATTGCTGCTGGCTATAGGAAGTCCCAGGTACTAGCCTGAAACAGCTG +ATATTTGGGGCTGTCACAGACAATATGGCCACCCCTTGGTCTTTATGCATGAAGATTATG +TAAAGGTTTTTATTAAAAAATATATATATATATATAAATGATCTAGATTATTTTCCTCTT +TCTGAAGTACTTTCTTAAAAAAATAAAATTAAATGTTTATAGTATTCCCGGT +EOF + +printf ">>> Run salmon_index" +"$meta_executable" \ + --transcripts $dir_in/transcriptome.fasta \ + --index index \ + --kmer_len 31 + +printf ">>> Checking whether output exists" +[ ! -d "index" ] && echo "'index' does not exist!" && exit 1 +[ -z "$(ls -A 'index')" ] && echo "'index' is empty!" && exit 1 +[ ! -f "index/info.json" ] && echo "Salmon index does not contain 'info.json'! Not all files were generated correctly!" && exit 1 +[ $(grep '"k": [0-9]*' index/info.json | cut -d':' -f 2) != '31,' ] && printf "The generated Salmon index seems to be incorrect!" && exit 1 + +echo "All tests succeeded!" +exit 0 \ No newline at end of file diff --git a/src/salmon/salmon_quant/config.vsh.yaml b/src/salmon/salmon_quant/config.vsh.yaml new file mode 100644 index 00000000..b7e303f4 --- /dev/null +++ b/src/salmon/salmon_quant/config.vsh.yaml @@ -0,0 +1,592 @@ +name: salmon_quant +namespace: salmon +description: | + Salmon is a tool for wicked-fast transcript quantification from RNA-seq data. It can either make use of pre-computed alignments (in the form of a SAM/BAM file) to the transcripts rather than the raw reads, or can be run in the mapping-based mode. +keywords: ["Transcriptome", "Quantification"] +links: + homepage: https://salmon.readthedocs.io/en/latest/salmon.html + documentation: https://salmon.readthedocs.io/en/latest/salmon.html + repository: https://github.com/COMBINE-lab/salmon +references: + doi: "10.1038/nmeth.4197" +license: GPL-3.0 +requirements: + commands: [ salmon ] + +argument_groups: + - name: Common input options + arguments: + - name: --lib_type + alternatives: ["-l"] + type: string + description: | + Format string describing the library. + The library type string consists of three parts: + 1. Relative orientation of the reads: This part is only provided if the library is paired-end, THe possible options are + I = inward + O = outward + M = matching + 2. Strandedness of the library: This part specifies whether the protocol is stranded or unstranded. The options are: + S = stranded + U = unstranded + 3. Directionality of the reads: If the library is stranded, the final part of the library string is used to specify the strand from which the read originates. The possible values are + F = read 1 (or single-end read) comes from the forward strand + R = read 1 (or single-end read) comes from the reverse strand + required: false + default: 'A' + choices: ['A', 'U', 'SF', 'SR', 'IU', 'IS', 'ISF', 'ISR', 'OU', 'OS', 'OSF', 'OSR', 'MU', 'MS', 'MSF', 'MSR'] + - name: Mapping input options + arguments: + - name: --index + alternatives: ["-i"] + type: file + description: | + Salmon index. + required: false + example: transcriptome_index + - name: --unmated_reads + alternatives: ["-r"] + type: file + description: | + List of files containing unmated reads of (e.g. single-end reads). + required: false + multiple: true + example: sample.fq.gz + - name: --mates1 + alternatives: ["-m1"] + type: file + description: | + File containing the #1 mates. + required: false + multiple: true + example: sample_1.fq.gz + - name: --mates2 + alternatives: ["-m2"] + type: file + description: | + File containing the #2 mates. + required: false + multiple: true + example: sample_2.fq.gz + + - name: Alignment input options + arguments: + - name: --discard_orphans + type: boolean_true + description: | + Discard orphan alignments in the input [for alignment-based mode only]. If this flag is passed, then only paired alignments will be considered toward quantification estimates. The default behavior is to consider orphan alignments if no valid paired mappings exist. + - name: --alignments + alternatives: ["-a"] + type: file + description: | + Input alignment (BAM) file(s). + required: false + multiple: true + example: sample.fq.gz + - name: --eqclasses + alternatives: ["-e"] + type: file + description: | + input salmon weighted equivalence class file. + required: false + - name: --targets + alternatives: ["-t"] + type: file + description: | + FASTA format file containing target transcripts. + required: false + example: transcripts.fasta + - name: --ont + type: boolean_true + description: | + Use alignment model for Oxford Nanopore long reads + + - name: Output + arguments: + - name: --output + alternatives: ["-o"] + type: file + direction: output + description: | + Output quantification directory. + required: true + example: quant_output + - name: --quant_results + type: file + direction: output + description: | + Salmon quantification file. + required: true + example: quant.sf + + - name: Basic options + arguments: + - name: --seq_bias + type: boolean_true + description: | + Perform sequence-specific bias correction. + - name: --gc_bias + type: boolean_true + description: | + Perform fragment GC bias correction [beta for single-end reads]. + - name: --pos_bias + type: boolean_true + description: | + Perform positional bias correction. + - name: --incompat_prior + type: double + description: | + Set the prior probability that an alignment that disagrees with the specified library type (--lib_type) results from the true fragment origin. Setting this to 0 specifies that alignments that disagree with the library type should be "impossible", while setting it to 1 says that alignments that disagree with the library type are no less likely than those that do. + required: false + min: 0 + max: 1 + example: 0 + - name: --gene_map + alternatives: ["-g"] + type: file + description: | + File containing a mapping of transcripts to genes. If this file is provided salmon will output both quant.sf and quant.genes.sf files, where the latter contains aggregated gene-level abundance estimates. The transcript to gene mapping should be provided as either a GTF file, or a in a simple tab-delimited format where each line contains the name of a transcript and the gene to which it belongs separated by a tab. The extension of the file is used to determine how the file should be parsed. Files ending in '.gtf', '.gff' or '.gff3' are assumed to be in GTF format; files with any other extension are assumed to be in the simple format. In GTF / GFF format, the "transcript_id" is assumed to contain the transcript identifier and the "gene_id" is assumed to contain the corresponding gene identifier. + required: false + example: gene_map.gtf + - name: --aux_target_file + type: file + description: | + A file containing a list of "auxiliary" targets. These are valid targets (i.e., not decoys) to which fragments are allowed to map and be assigned, and which will be quantified, but for which auxiliary models like sequence-specific and fragment-GC bias correction should not be applied. + required: false + example: auxilary_targets.txt + - name: --meta + type: boolean_true + description: | + If you're using Salmon on a metagenomic dataset, consider setting this flag to disable parts of the abundance estimation model that make less sense for metagenomic data. + - name: --score_exp + type: double + description: | + The factor by which sub-optimal alignment scores are downweighted to produce a probability. If the best alignment score for the current read is S, and the score for a particular alignment is w, then the probability will be computed porportional to exp( - scoreExp * (S-w) ). + required: false + example: 1 + + - name: Options specific to mapping mode + arguments: + - name: --discard_orphans_quasi + type: boolean_true + description: | + [selective-alignment mode only] + Discard orphan mappings in selective-alignment mode. If this flag is passed then only paired mappings will be considered toward quantification estimates. The default behavior is to consider orphan mappings if no valid paired mappings exist. This flag is independent of the option to write the orphaned mappings to file (--writeOrphanLinks). + - name: --consensus_slack + type: double + description: | + [selective-alignment mode only] + The amount of slack allowed in the selective-alignment filtering mechanism. If this is set to a fraction, X, greater than 0 (and in [0,1)), then uniMEM chains with scores below (100 * X)% of the best chain score for a read, and read pairs with a sum of chain scores below (100 * X)% of the best chain score for a read pair will be discounted as a mapping candidates. The default value of this option is 0.35. + required: false + min: 0 + max: 0.999999999 + example: 0.35 + - name: --pre_merge_chain_sub_thresh + type: double + description: | + [selective-alignment mode only] + The threshold of sub-optimal chains, compared to the best chain on a given target, that will be retained and passed to the next phase of mapping. Specifically, if the best chain for a read (or read-end in paired-end mode) to target t has score X_t, then all chains for this read with score >= X_t * preMergeChainSubThresh will be retained and passed to subsequent mapping phases. This value must be in the range [0, 1]. + required: false + min: 0 + max: 1 + example: 0.75 + - name: --post_merge_chain_sub_thresh + type: double + description: | + [selective-alignment mode only] + The threshold of sub-optimal chains, compared to the best chain on a given target, that will be retained and passed to the next phase of mapping. This is different than post_merge_chain_sub_thresh, because this is applied to pairs of chains (from the ends of paired-end reads) after merging (i.e. after checking concordancy constraints etc.). Specifically, if the best chain pair to target t has score X_t, then all chain pairs for this read pair with score >= X_t * post_merge_chain_sub_thresh will be retained and passed to subsequent mapping phases. This value must be in the range [0, 1]. Note: This option is only meaningful for paired-end libraries, and is ignored for single-end libraries. + required: false + min: 0 + max: 1 + example: 0.9 + - name: --orphan_chain_sub_thresh + type: double + description: | + [selective-alignment mode only] + This threshold sets a global sub-optimality threshold for chains corresponding to orphan mappings. That is, if the merging procedure results in no concordant mappings then only orphan mappings with a chain score >= orphan_chain_sub_thresh * bestChainScore will be retained and passed to subsequent mapping phases. This value must be in the range [0, 1]. Note: This option is only meaningful for paired-end libraries, and is ignored for single-end libraries. + required: false + min: 0 + max: 1 + example: 0.95 + - name: --min_score_fraction + type: double + description: | + [selective-alignment mode only] + The fraction of the optimal possible alignment score that a mapping must achieve in order to be considered "valid" --- should be in (0,1]. Default 0.65 + required: false + min: 0.000000001 + max: 1 + example: 0.65 + - name: --mismatch_seed_skip + type: integer + description: | + [selective-alignment mode only] + After a k-mer hit is extended to a uni-MEM, the uni-MEM extension can terminate for one of 3 reasons; the end of the read, the end of the unitig, or a mismatch. If the extension ends because of a mismatch, this is likely the result of a sequencing error. To avoid looking up many k-mers that will likely fail to be located in the index, the search procedure skips by a factor of mismatch_seed_skip until it either (1) finds another match or (2) is k-bases past the mismatch position. This value controls that skip length. A smaller value can increase sensitivity, while a larger value can speed up seeding. + required: false + example: 3 + - name: --disable_chaining_heuristic + type: boolean_true + description: | + [selective-alignment mode only] + By default, the heuristic of (Li 2018) is implemented, which terminates the chaining DP once a given number of valid backpointers are found. This speeds up the seed (MEM) chaining step, but may result in sub-optimal chains in complex situations (e.g. sequences with many repeats and overlapping repeats). Passing this flag will disable the chaining heuristic, and perform the full chaining dynamic program, guaranteeing the optimal chain is found in this step. + - name: --decoy_threshold + type: double + description: | + [selective-alignment mode only] + For an alignemnt to an annotated transcript to be considered invalid, it must have an alignment score < (decoy_threshold * bestDecoyScore). A value of 1.0 means that any alignment strictly worse than the best decoy alignment will be discarded. A smaller value will allow reads to be allocated to transcripts even if they strictly align better to the decoy sequence. + required: false + min: 0 + max: 1 + example: 1 + - name: --ma + type: integer + description: | + [selective-alignment mode only] + The value given to a match between read and reference nucleotides in an alignment. + required: false + example: 2 + - name: --mp + type: integer + description: | + [selective-alignment mode only] + The value given to a mis-match between read and reference nucleotides in an alignment. + required: false + example: -4 + - name: --go + type: integer + description: | + [selective-alignment mode only] + The value given to a gap opening in an alignment. + required: false + example: 6 + - name: --ge + type: integer + description: | + [selective-alignment mode only] + The value given to a gap extension in an alignment. + required: false + example: 2 + - name: --bandwidth + type: integer + description: | + [selective-alignment mode only] + The value used for the bandwidth passed to ksw2. A smaller bandwidth can make the alignment verification run more quickly, but could possibly miss valid alignments. + required: false + example: 15 + - name: --allow_dovetail + type: boolean_true + description: | + [selective-alignment mode only] + Allow dovetailing mappings. + - name: --recover_orphans + type: boolean_true + description: | + [selective-alignment mode only] + Attempt to recover the mates of orphaned reads. This uses edlib for orphan recovery, and so introduces some computational overhead, but it can improve sensitivity. + - name: --mimicBT2 + type: boolean_true + description: | + [selective-alignment mode only] + Set flags to mimic parameters similar to Bowtie2 with --no-discordant and --no-mixed flags. This increases disallows dovetailing reads, and discards orphans. Note, this does not impose the very strict parameters assumed by RSEM+Bowtie2, like gapless alignments. For that behavior, use the --mimic_strictBT2 flag below. + - name: --mimic_strictBT2 + type: boolean_true + description: | + [selective-alignment mode only] + Set flags to mimic the very strict parameters used by RSEM+Bowtie2. This increases --min_score_fraction to 0.8, disallows dovetailing reads, discards orphans, and disallows gaps in alignments. + - name: --softclip + type: boolean_true + description: | + [selective-alignment mode only] + Allos soft-clipping of reads during selective-alignment. If this option is provided, then regions at the beginning or end of the read can be withheld from alignment without any effect on the resulting score (i.e. neither adding nor removing from the score). This will drastically reduce the penalty if there are mismatches at the beginning or end of the read due to e.g. low-quality bases or adapters. NOTE: Even with soft-clipping enabled, the read must still achieve a score of at least min_score_fraction * maximum achievable score, where the maximum achievable score is computed based on the full (un-clipped) read length. + - name: --softclip_overhangs + type: boolean_true + description: | + [selective-alignment mode only] + Allow soft-clipping of reads that overhang the beginning or ends of the transcript. In this case, the overhaning section of the read will simply be unaligned, and will not contribute or detract from the alignment score. The default policy is to force an end-to-end alignment of the entire read, so that overhanings will result in some deletion of nucleotides from the read. + - name: --full_length_alignment + type: boolean_true + description: | + [selective-alignment mode only] + Perform selective alignment over the full length of the read, beginning from the (approximate) initial mapping location and using extension alignment. This is in contrast with the default behavior which is to only perform alignment between the MEMs in the optimal chain (and before the first and after the last MEM if applicable). The default strategy forces the MEMs to belong to the alignment, but has the benefit that it can discover indels prior to the first hit shared between the read and reference. Except in very rare circumstances, the default mode should be more accurate. + - name: --hard_filter + type: boolean_true + description: | + [selective-alignment mode only] + Instead of weighting mappings by their alignment score, this flag will discard any mappings with sub-optimal alignment score. The default option of soft-filtering (i.e. weighting mappings by their alignment score) usually yields slightly more accurate abundance estimates but this flag may be desirable if you want more accurate 'naive' equivalence classes, rather than range factorized equivalence classes. + - name: --min_aln_prob + type: double + description: | + The minimum number of fragments that must be assigned to the transcriptome for quantification to proceed. + example: 0.00001 + - name: --write_mappings + alternatives: ["-z"] + type: boolean_true + description: | + If this option is provided, then the selective-alignment results will be written out in SAM-compatible format. By default, output will be directed to stdout, but an alternative file name can be provided instead. + - name: --mapping_sam + type: file + description: Path to file that should output the selective-alignment results in SAM-compatible format. THis option must be provided while using --write_mappings + required: false + direction: output + example: mappings.sam + - name: --write_qualities + type: boolean_true + description: | + This flag only has meaning if mappings are being written (with --write_mappings/-z). If this flag is provided, then the output SAM file will contain quality strings as well as read sequences. Note that this can greatly increase the size of the output file. + - name: --hit_filter_policy + type: string + description: | + [selective-alignment mode only] + Determines the policy by which hits are filtered in selective alignment. Filtering hits after chaining (the default) is more sensitive, but more computationally intensive, because it performs the chaining dynamic program for all hits. Filtering before chaining is faster, but some true hits may be missed. The options are BEFORE, AFTER, BOTH and NONE. + required: false + choices: [BEFORE, AFTER, BOTH, NONE] + example: AFTER + + - name: Advance options + arguments: + - name: --alternative_init_mode + type: boolean_true + description: | + Use an alternative strategy (rather than simple interpolation between) the online and uniform abundance estimates to initialize the EM / VBEM algorithm. + - name: --aux_dir + type: file + direction: output + description: | + The sub-directory of the quantification directory where auxiliary information e.g. bootstraps, bias parameters, etc. will be written. + required: false + example: aux_info + - name: --skip_quant + type: boolean_true + description: | + Skip performing the actual transcript quantification (including any Gibbs sampling or bootstrapping). + - name: --dump_eq + type: boolean_true + description: | + Dump the simple equivalence class counts that were computed during mapping or alignment. + - name: --dump_eq_weights + alternatives: ["-d"] + type: boolean_true + description: | + Dump conditional probabilities associated with transcripts when equivalence class information is being dumped to file. Note, this will dump the factorization that is actually used by salmon's offline phase for inference. If you are using range-factorized equivalence classes (the default) then the same transcript set may appear multiple times with different associated conditional probabilities. + - name: --min_assigned_frags + type: integer + description: | + The minimum number of fragments that must be assigned to the transcriptome for quantification to proceed. + required: false + example: 10 + - name: --reduce_GC_memory + type: boolean_true + description: | + If this option is selected, a more memory efficient (but slightly slower) representation is used to compute fragment GC content. Enabling this will reduce memory usage, but can also reduce speed. However, the results themselves will remain the same. + - name: --bias_speed_samp + type: integer + description: | + The value at which the fragment length PMF is down-sampled when evaluating sequence-specific & GC fragment bias. Larger values speed up effective length correction, but may decrease the fidelity of bias modeling results. + required: false + example: 5 + - name: --fld_max + type: integer + description: | + The maximum fragment length to consider when building the empirical distribution + required: false + example: 1000 + - name: --fld_mean + type: integer + description: | + The mean used in the fragment length distribution prior + required: false + example: 250 + - name: --fld_SD + type: integer + description: | + The standard deviation used in the fragment length distribution prior + required: false + example: 25 + - name: --forgetting_factor + alternatives: ["-f"] + type: double + description: | + The forgetting factor used in the online learning schedule. A smallervalue results in quicker learning, but higher variance and may be unstable. A larger value results in slower learning but may be more stable. Value should be in the interval (0.5, 1.0]. + required: false + min: 0.500000001 + max: 1 + example: 0.65 + - name: --init_uniform + type: boolean_true + description: | + Initialize the offline inference with uniform parameters, rather than seeding with online parameters. + - name: --max_occs_per_hit + type: integer + description: | + When collecting "hits" (MEMs), hits having more than max_occs_per_hit occurrences won't be considered. + required: false + example: 1000 + - name: --max_read_occ + type: integer + description: | + Reads "mapping" to more than this many places won't be considered. + required: false + example: 200 + - name: --no_length_correction + type: boolean_true + description: | + Entirely disables length correction when estimating the abundance of transcripts. This option can be used with protocols where one expects that fragments derive from their underlying targets without regard to that target's length (e.g. QuantSeq) + - name: --no_effective_length_correction + type: boolean_true + description: | + Disables effective length correction when computing the probability that a fragment was generated from a transcript. If this flag is passed in,the fragment length distribution is not taken into account when computing this probability. + - name: --no_single_frag_prob + type: boolean_true + description: | + Disables the estimation of an associated fragment length probability for single-end reads or for orphaned mappings in paired-end libraries. The default behavior is to consider the probability of all possible fragment lengths associated with the retained mapping. Enabling this flag (i.e. turning this default behavior off) will simply not attempt to estimate a fragment length probability in such cases. + - name: --no_frag_length_dist + type: boolean_true + description: | + Don't consider concordance with the learned fragment length distribution when trying to determine the probability that a fragment has originated from a specified location. Normally, Fragments with unlikely lengths will be assigned a smaller relative probability than those with more likely lengths. When this flag is passed in, the observed fragment length has no effect on that fragment's a priori probability. + - name: --no_bias_length_threshold + type: boolean_true + description: | + If this option is enabled, then no (lower) threshold will be set on how short bias correction can make effective lengths. This can increase the precision of bias correction, but harm robustness. The default correction applies a threshold. + - name: --num_bias_samples + type: integer + description: | + Number of fragment mappings to use when learning the sequence-specific bias model. + required: false + example: 2000000 + - name: --num_aux_model_samples + type: integer + description: | + The first are used to train the auxiliary model parameters (e.g. fragment length distribution, bias, etc.). After ther first observations the auxiliary model parameters will be assumed to have converged and will be fixed. + required: false + example: 5000000 + - name: --num_pre_aux_model_samples + type: integer + description: | + The first will have their assignment likelihoods and contributions to the transcript abundances computed without applying any auxiliary models. The purpose of ignoring the auxiliary models for the first observations is to avoid applying these models before their parameters have been learned sufficiently well. + required: false + example: 5000 + - name: --useEM + type: boolean_true + description: | + Use the traditional EM algorithm for optimization in the batch passes. + - name: --useVBOpt + type: boolean_true + description: | + Use the Variational Bayesian EM [default] + - name: --range_factorization_bins + type: integer + description: | + Factorizes the likelihood used in quantification by adopting a new notion of equivalence classes based on the conditional probabilities with which fragments are generated from different transcripts. This is a more fine-grained factorization than the normal rich equivalence classes. The default value (4) corresponds to the default used in Zakeri et al. 2017 (doi: 10.1093/bioinformatics/btx262), and larger values imply a more fine-grained factorization. If range factorization is enabled, a common value to select for this parameter is 4. A value of 0 signifies the use of basic rich equivalence classes. + required: false + example: 4 + - name: --num_Gibbs_samples + type: integer + description: | + Number of Gibbs sampling rounds to perform. + required: false + example: 0 + - name: --no_Gamma_draw + type: boolean_true + description: | + This switch will disable drawing transcript fractions from a Gamma distribution during Gibbs sampling. In this case the sampler does not account for shot-noise, but only assignment ambiguity + - name: --num_bootstraps + type: integer + description: | + Number of bootstrap samples to generate. Note: This is mutually exclusive with Gibbs sampling. + required: false + example: 0 + - name: --bootstrap_reproject + type: boolean_true + description: | + This switch will learn the parameter distribution from the bootstrapped counts for each sample, but will reproject those parameters onto the original equivalence class counts. + - name: --thinning_factor + type: integer + description: | + Number of steps to discard for every sample kept from the Gibbs chain. The larger this number, the less chance that subsequent samples are auto-correlated, but the slower sampling becomes. + required: false + example: 16 + - name: --quiet + alternatives: ["-q"] + type: boolean_true + description: | + Be quiet while doing quantification (don't write informative output to the console unless something goes wrong). + - name: --per_transcript_prior + type: boolean_true + description: | + The prior (either the default or the argument provided via --vb_prior) will be interpreted as a transcript-level prior (i.e. each transcript will be given a prior read count of this value) + - name: --per_nucleotide_prior + type: boolean_true + description: | + The prior (either the default or the argument provided via --vb_prior) will be interpreted as a nucleotide-level prior (i.e. each nucleotide will be given a prior read count of this value) + - name: --sig_digits + type: integer + description: | + The number of significant digits to write when outputting the EffectiveLength and NumReads columns + required: false + example: 3 + - name: --vb_prior + type: double + description: | + The prior that will be used in the VBEM algorithm. This is interpreted as a per-transcript prior, unless the --per_nucleotide_prior flag is also given. If the --per_nucleotide_prior flag is given, this is used as a nucleotide-level prior. If the default is used, it will be divided by 1000 before being used as a nucleotide-level prior, i.e. the default per-nucleotide prior will be 1e-5. + required: false + example: 0.01 + - name: --write_orphan_links + type: boolean_true + description: | + Write the transcripts that are linked by orphaned reads. + - name: --write_unmapped_names + type: boolean_true + description: | + Write the names of un-mapped reads to the file unmapped_names.txt in the auxiliary directory. + + - name: Alignment-specific options + arguments: + - name: --no_error_model + type: boolean_true + description: | + Turn off the alignment error model, which takes into account the the observed frequency of different types of mismatches / indels when computing the likelihood of a given alignment. Turning this off can speed up alignment-based salmon, but can harm quantification accuracy. + - name: --num_error_bins + type: integer + description: | + The number of bins into which to divide each read when learning and applying the error model. For example, a value of 10 would mean that effectively, a separate error model is leared and applied to each 10th of the read, while a value of 3 would mean that a separate error model is applied to the read beginning (first third), middle (second third) and end (final third). + required: false + example: 6 + - name: --sample_out + alternatives: ["-s"] + type: boolean_true + description: | + Write a "postSample.bam" file in the output directory that will sample the input alignments according to the estimated transcript abundances. If you're going to perform downstream analysis of the alignments with tools which don't, themselves, take fragment assignment ambiguity into account, you should use this output. + - name: --sample_unaligned + alternatives: ["-u"] + type: boolean_true + description: | + In addition to sampling the aligned reads, also write the un-aligned reads to "postSample.bam". + - name: --gencode + type: boolean_true + description: | + This flag will expect the input transcript fasta to be in GENCODE format, and will split the transcript name at the first '|' character. These reduced names will be used in the output and when looking for these transcripts in a gene to transcript GTF. + - name: --mapping_cache_memory_limit + type: integer + description: | + If the file contained fewer than this many mapped reads, then just keep the data in memory for subsequent rounds of inference. Obviously, this value should not be too large if you wish to keep a low memory usage, but setting it large enough to accommodate all of the mapped read can substantially speed up inference on "small" files that contain only a few million reads. + required: false + example: 2000000 + +resources: + - type: bash_script + path: script.sh + +test_resources: + - type: bash_script + path: test.sh + +engines: + - type: docker + image: quay.io/biocontainers/salmon:1.10.2--hecfa306_0 + setup: + - type: docker + run: | + salmon index -v 2>&1 | sed 's/salmon \([0-9.]*\)/salmon: \1/' > /var/software_versions.txt +runners: + - type: executable + - type: nextflow \ No newline at end of file diff --git a/src/salmon/salmon_quant/help.txt b/src/salmon/salmon_quant/help.txt new file mode 100644 index 00000000..bcd92656 --- /dev/null +++ b/src/salmon/salmon_quant/help.txt @@ -0,0 +1,976 @@ +```bash +salmon quant -h +``` +salmon v1.10.2 +=============== + +salmon quant has two modes --- one quantifies expression using raw reads +and the other makes use of already-aligned reads (in BAM/SAM format). +Which algorithm is used depends on the arguments passed to salmon quant. +If you provide salmon with alignments '-a [ --alignments ]' then the +alignment-based algorithm will be used, otherwise the algorithm for +quantifying from raw reads will be used. + +to view the help for salmon's selective-alignment-based mode, use the command + +salmon quant --help-reads + +To view the help for salmon's alignment-based mode, use the command + +salmon quant --help-alignment + + +```bash +salmon quant --help-reads +``` +Quant +========== +Perform dual-phase, selective-alignment-based estimation of +transcript abundance from RNA-seq reads + +salmon quant options: + + +mapping input options: + -l [ --libType ] arg Format string describing the library + type + -i [ --index ] arg salmon index + -r [ --unmatedReads ] arg List of files containing unmated reads + of (e.g. single-end reads) + -1 [ --mates1 ] arg File containing the #1 mates + -2 [ --mates2 ] arg File containing the #2 mates + + +basic options: + -v [ --version ] print version string + -h [ --help ] produce help message + -o [ --output ] arg Output quantification directory. + --seqBias Perform sequence-specific bias + correction. + --gcBias [beta for single-end reads] Perform + fragment GC bias correction. + --posBias Perform positional bias correction. + -p [ --threads ] arg (=16) The number of threads to use + concurrently. + --incompatPrior arg (=0) This option sets the prior probability + that an alignment that disagrees with + the specified library type (--libType) + results from the true fragment origin. + Setting this to 0 specifies that + alignments that disagree with the + library type should be "impossible", + while setting it to 1 says that + alignments that disagree with the + library type are no less likely than + those that do + -g [ --geneMap ] arg File containing a mapping of + transcripts to genes. If this file is + provided salmon will output both + quant.sf and quant.genes.sf files, + where the latter contains aggregated + gene-level abundance estimates. The + transcript to gene mapping should be + provided as either a GTF file, or a in + a simple tab-delimited format where + each line contains the name of a + transcript and the gene to which it + belongs separated by a tab. The + extension of the file is used to + determine how the file should be + parsed. Files ending in '.gtf', '.gff' + or '.gff3' are assumed to be in GTF + format; files with any other extension + are assumed to be in the simple format. + In GTF / GFF format, the + "transcript_id" is assumed to contain + the transcript identifier and the + "gene_id" is assumed to contain the + corresponding gene identifier. + --auxTargetFile arg A file containing a list of "auxiliary" + targets. These are valid targets + (i.e., not decoys) to which fragments + are allowed to map and be assigned, and + which will be quantified, but for which + auxiliary models like sequence-specific + and fragment-GC bias correction should + not be applied. + --meta If you're using Salmon on a metagenomic + dataset, consider setting this flag to + disable parts of the abundance + estimation model that make less sense + for metagenomic data. + + +options specific to mapping mode: + --discardOrphansQuasi [selective-alignment mode only] : + Discard orphan mappings in + selective-alignment mode. If this flag + is passed then only paired mappings + will be considered toward + quantification estimates. The default + behavior is to consider orphan mappings + if no valid paired mappings exist. + This flag is independent of the option + to write the orphaned mappings to file + (--writeOrphanLinks). + --validateMappings [*deprecated* (no effect; + selective-alignment is the default)] + --consensusSlack arg (=0.349999994) [selective-alignment mode only] : The + amount of slack allowed in the + selective-alignment filtering + mechanism. If this is set to a + fraction, X, greater than 0 (and in + [0,1)), then uniMEM chains with scores + below (100 * X)% of the best chain + score for a read, and read pairs with a + sum of chain scores below (100 * X)% of + the best chain score for a read pair + will be discounted as a mapping + candidates. The default value of this + option is 0.35. + --preMergeChainSubThresh arg (=0.75) [selective-alignment mode only] : The + threshold of sub-optimal chains, + compared to the best chain on a given + target, that will be retained and + passed to the next phase of mapping. + Specifically, if the best chain for a + read (or read-end in paired-end mode) + to target t has score X_t, then all + chains for this read with score >= X_t + * preMergeChainSubThresh will be + retained and passed to subsequent + mapping phases. This value must be in + the range [0, 1]. + --postMergeChainSubThresh arg (=0.90000000000000002) + [selective-alignment mode only] : The + threshold of sub-optimal chain pairs, + compared to the best chain pair on a + given target, that will be retained and + passed to the next phase of mapping. + This is different than + preMergeChainSubThresh, because this is + applied to pairs of chains (from the + ends of paired-end reads) after merging + (i.e. after checking concordancy + constraints etc.). Specifically, if + the best chain pair to target t has + score X_t, then all chain pairs for + this read pair with score >= X_t * + postMergeChainSubThresh will be + retained and passed to subsequent + mapping phases. This value must be in + the range [0, 1]. Note: This option is + only meaningful for paired-end + libraries, and is ignored for + single-end libraries. + --orphanChainSubThresh arg (=0.94999999999999996) + [selective-alignment mode only] : This + threshold sets a global sub-optimality + threshold for chains corresponding to + orphan mappings. That is, if the + merging procedure results in no + concordant mappings then only orphan + mappings with a chain score >= + orphanChainSubThresh * bestChainScore + will be retained and passed to + subsequent mapping phases. This value + must be in the range [0, 1]. Note: This + option is only meaningful for + paired-end libraries, and is ignored + for single-end libraries. + --scoreExp arg (=1) [selective-alignment mode only] : The + factor by which sub-optimal alignment + scores are downweighted to produce a + probability. If the best alignment + score for the current read is S, and + the score for a particular alignment is + w, then the probability will be + computed porportional to exp( - + scoreExp * (S-w) ). + --minScoreFraction arg [selective-alignment mode only] : The + fraction of the optimal possible + alignment score that a mapping must + achieve in order to be considered + "valid" --- should be in (0,1]. + Salmon Default 0.65 and Alevin Default + 0.87 + --mismatchSeedSkip arg (=3) [selective-alignment mode only] : After + a k-mer hit is extended to a uni-MEM, + the uni-MEM extension can terminate for + one of 3 reasons; the end of the read, + the end of the unitig, or a mismatch. + If the extension ends because of a + mismatch, this is likely the result of + a sequencing error. To avoid looking + up many k-mers that will likely fail to + be located in the index, the search + procedure skips by a factor of + mismatchSeedSkip until it either (1) + finds another match or (2) is k-bases + past the mismatch position. This value + controls that skip length. A smaller + value can increase sensitivity, while a + larger value can speed up seeding. + --disableChainingHeuristic [selective-alignment mode only] : By + default, the heuristic of (Li 2018) is + implemented, which terminates the + chaining DP once a given number of + valid backpointers are found. This + speeds up the seed (MEM) chaining step, + but may result in sub-optimal chains in + complex situations (e.g. sequences with + many repeats and overlapping repeats). + Passing this flag will disable the + chaining heuristic, and perform the + full chaining dynamic program, + guaranteeing the optimal chain is found + in this step. + --decoyThreshold arg (=1) [selective-alignment mode only] : For + an alignemnt to an annotated transcript + to be considered invalid, it must have + an alignment score < (decoyThreshold * + bestDecoyScore). A value of 1.0 means + that any alignment strictly worse than + the best decoy alignment will be + discarded. A smaller value will allow + reads to be allocated to transcripts + even if they strictly align better to + the decoy sequence. + --ma arg (=2) [selective-alignment mode only] : The + value given to a match between read and + reference nucleotides in an alignment. + --mp arg (=-4) [selective-alignment mode only] : The + value given to a mis-match between read + and reference nucleotides in an + alignment. + --go arg (=6) [selective-alignment mode only] : The + value given to a gap opening in an + alignment. + --ge arg (=2) [selective-alignment mode only] : The + value given to a gap extension in an + alignment. + --bandwidth arg (=15) [selective-alignment mode only] : The + value used for the bandwidth passed to + ksw2. A smaller bandwidth can make the + alignment verification run more + quickly, but could possibly miss valid + alignments. + --allowDovetail [selective-alignment mode only] : allow + dovetailing mappings. + --recoverOrphans [selective-alignment mode only] : + Attempt to recover the mates of + orphaned reads. This uses edlib for + orphan recovery, and so introduces some + computational overhead, but it can + improve sensitivity. + --mimicBT2 [selective-alignment mode only] : Set + flags to mimic parameters similar to + Bowtie2 with --no-discordant and + --no-mixed flags. This increases + disallows dovetailing reads, and + discards orphans. Note, this does not + impose the very strict parameters + assumed by RSEM+Bowtie2, like gapless + alignments. For that behavior, use the + --mimiStrictBT2 flag below. + --mimicStrictBT2 [selective-alignment mode only] : Set + flags to mimic the very strict + parameters used by RSEM+Bowtie2. This + increases --minScoreFraction to 0.8, + disallows dovetailing reads, discards + orphans, and disallows gaps in + alignments. + --softclip [selective-alignment mode only + (experimental)] : Allos soft-clipping + of reads during selective-alignment. If + this option is provided, then regions + at the beginning or end of the read can + be withheld from alignment without any + effect on the resulting score (i.e. + neither adding nor removing from the + score). This will drastically reduce + the penalty if there are mismatches at + the beginning or end of the read due to + e.g. low-quality bases or adapters. + NOTE: Even with soft-clipping enabled, + the read must still achieve a score of + at least minScoreFraction * maximum + achievable score, where the maximum + achievable score is computed based on + the full (un-clipped) read length. + --softclipOverhangs [selective-alignment mode only] : Allow + soft-clipping of reads that overhang + the beginning or ends of the + transcript. In this case, the + overhaning section of the read will + simply be unaligned, and will not + contribute or detract from the + alignment score. The default policy is + to force an end-to-end alignment of the + entire read, so that overhanings will + result in some deletion of nucleotides + from the read. + --fullLengthAlignment [selective-alignment mode only] : + Perform selective alignment over the + full length of the read, beginning from + the (approximate) initial mapping + location and using extension alignment. + This is in contrast with the default + behavior which is to only perform + alignment between the MEMs in the + optimal chain (and before the first and + after the last MEM if applicable). The + default strategy forces the MEMs to + belong to the alignment, but has the + benefit that it can discover indels + prior to the first hit shared between + the read and reference. Except in very + rare circumstances, the default mode + should be more accurate. + --hardFilter [selective-alignemnt mode only] : + Instead of weighting mappings by their + alignment score, this flag will discard + any mappings with sub-optimal alignment + score. The default option of + soft-filtering (i.e. weighting mappings + by their alignment score) usually + yields slightly more accurate abundance + estimates but this flag may be + desirable if you want more accurate + 'naive' equivalence classes, rather + than range factorized equivalence + classes. + --minAlnProb arg (=1.0000000000000001e-05) + [selective-alignment mode only] : Any + mapping whose alignment probability (as + computed by P(aln) = exp(-scoreExp * + difference from best mapping score) is + less than minAlnProb will not be + considered as a valid alignment for + this read. The goal of this flag is to + remove very low probability alignments + that are unlikely to have any + non-trivial effect on the final + quantifications. Filtering such + alignments reduces the number of + variables that need to be considered + and can result in slightly faster + inference and 'cleaner' equivalence + classes. + -z [ --writeMappings ] [=arg(=-)] If this option is provided, then the + selective-alignment results will be + written out in SAM-compatible format. + By default, output will be directed to + stdout, but an alternative file name + can be provided instead. + --writeQualities This flag only has meaning if mappings + are being written (with + --writeMappings/-z). If this flag is + provided, then the output SAM file will + contain quality strings as well as read + sequences. Note that this can greatly + increase the size of the output file. + --hitFilterPolicy arg (=AFTER) [selective-alignment mode only] : + Determines the policy by which hits are + filtered in selective alignment. + Filtering hits after chaining (the + default) is more sensitive, but more + computationally intensive, because it + performs the chaining dynamic program + for all hits. Filtering before + chaining is faster, but some true hits + may be missed. The options are BEFORE, + AFTER, BOTH and NONE. + + +advanced options: + --alternativeInitMode [Experimental]: Use an alternative + strategy (rather than simple + interpolation between) the online and + uniform abundance estimates to + initialize the EM / VBEM algorithm. + --auxDir arg (=aux_info) The sub-directory of the quantification + directory where auxiliary information + e.g. bootstraps, bias parameters, etc. + will be written. + --skipQuant Skip performing the actual transcript + quantification (including any Gibbs + sampling or bootstrapping). + --dumpEq Dump the simple equivalence class + counts that were computed during + mapping or alignment. + -d [ --dumpEqWeights ] Dump conditional probabilities + associated with transcripts when + equivalence class information is being + dumped to file. Note, this will dump + the factorization that is actually used + by salmon's offline phase for + inference. If you are using + range-factorized equivalence classes + (the default) then the same transcript + set may appear multiple times with + different associated conditional + probabilities. + --minAssignedFrags arg (=10) The minimum number of fragments that + must be assigned to the transcriptome + for quantification to proceed. + --reduceGCMemory If this option is selected, a more + memory efficient (but slightly slower) + representation is used to compute + fragment GC content. Enabling this will + reduce memory usage, but can also + reduce speed. However, the results + themselves will remain the same. + --biasSpeedSamp arg (=5) The value at which the fragment length + PMF is down-sampled when evaluating + sequence-specific & GC fragment bias. + Larger values speed up effective length + correction, but may decrease the + fidelity of bias modeling results. + --fldMax arg (=1000) The maximum fragment length to consider + when building the empirical + distribution + --fldMean arg (=250) The mean used in the fragment length + distribution prior + --fldSD arg (=25) The standard deviation used in the + fragment length distribution prior + -f [ --forgettingFactor ] arg (=0.65000000000000002) + The forgetting factor used in the + online learning schedule. A smaller + value results in quicker learning, but + higher variance and may be unstable. A + larger value results in slower learning + but may be more stable. Value should + be in the interval (0.5, 1.0]. + --initUniform initialize the offline inference with + uniform parameters, rather than seeding + with online parameters. + --maxOccsPerHit arg (=1000) When collecting "hits" (MEMs), hits + having more than maxOccsPerHit + occurrences won't be considered. + -w [ --maxReadOcc ] arg (=200) Reads "mapping" to more than this many + places won't be considered. + --maxRecoverReadOcc arg (=2500) Relevant for alevin with '--sketch' + mode only: if a read has valid seed + matches, but no read has matches + leading to fewer than "maxReadOcc" + mappings, then try to recover mappings + for this read as long as there are + fewer than "maxRecoverReadOcc" + mappings. + --noLengthCorrection [experimental] : Entirely disables + length correction when estimating the + abundance of transcripts. This option + can be used with protocols where one + expects that fragments derive from + their underlying targets without regard + to that target's length (e.g. QuantSeq) + --noEffectiveLengthCorrection Disables effective length correction + when computing the probability that a + fragment was generated from a + transcript. If this flag is passed in, + the fragment length distribution is not + taken into account when computing this + probability. + --noSingleFragProb Disables the estimation of an + associated fragment length probability + for single-end reads or for orphaned + mappings in paired-end libraries. The + default behavior is to consider the + probability of all possible fragment + lengths associated with the retained + mapping. Enabling this flag (i.e. + turning this default behavior off) will + simply not attempt to estimate a + fragment length probability in such + cases. + --noFragLengthDist [experimental] : Don't consider + concordance with the learned fragment + length distribution when trying to + determine the probability that a + fragment has originated from a + specified location. Normally, + Fragments with unlikely lengths will be + assigned a smaller relative probability + than those with more likely lengths. + When this flag is passed in, the + observed fragment length has no effect + on that fragment's a priori + probability. + --noBiasLengthThreshold [experimental] : If this option is + enabled, then no (lower) threshold will + be set on how short bias correction can + make effective lengths. This can + increase the precision of bias + correction, but harm robustness. The + default correction applies a threshold. + --numBiasSamples arg (=2000000) Number of fragment mappings to use when + learning the sequence-specific bias + model. + --numAuxModelSamples arg (=5000000) The first are used + to train the auxiliary model parameters + (e.g. fragment length distribution, + bias, etc.). After ther first + observations the + auxiliary model parameters will be + assumed to have converged and will be + fixed. + --numPreAuxModelSamples arg (=5000) The first will + have their assignment likelihoods and + contributions to the transcript + abundances computed without applying + any auxiliary models. The purpose of + ignoring the auxiliary models for the + first + observations is to avoid applying these + models before their parameters have + been learned sufficiently well. + --useEM Use the traditional EM algorithm for + optimization in the batch passes. + --useVBOpt Use the Variational Bayesian EM + [default] + --rangeFactorizationBins arg (=4) Factorizes the likelihood used in + quantification by adopting a new notion + of equivalence classes based on the + conditional probabilities with which + fragments are generated from different + transcripts. This is a more + fine-grained factorization than the + normal rich equivalence classes. The + default value (4) corresponds to the + default used in Zakeri et al. 2017 + (doi: 10.1093/bioinformatics/btx262), + and larger values imply a more + fine-grained factorization. If range + factorization is enabled, a common + value to select for this parameter is + 4. A value of 0 signifies the use of + basic rich equivalence classes. + --numGibbsSamples arg (=0) Number of Gibbs sampling rounds to + perform. + --noGammaDraw This switch will disable drawing + transcript fractions from a Gamma + distribution during Gibbs sampling. In + this case the sampler does not account + for shot-noise, but only assignment + ambiguity + --numBootstraps arg (=0) Number of bootstrap samples to + generate. Note: This is mutually + exclusive with Gibbs sampling. + --bootstrapReproject This switch will learn the parameter + distribution from the bootstrapped + counts for each sample, but will + reproject those parameters onto the + original equivalence class counts. + --thinningFactor arg (=16) Number of steps to discard for every + sample kept from the Gibbs chain. The + larger this number, the less chance + that subsequent samples are + auto-correlated, but the slower + sampling becomes. + -q [ --quiet ] Be quiet while doing quantification + (don't write informative output to the + console unless something goes wrong). + --perTranscriptPrior The prior (either the default or the + argument provided via --vbPrior) will + be interpreted as a transcript-level + prior (i.e. each transcript will be + given a prior read count of this value) + --perNucleotidePrior The prior (either the default or the + argument provided via --vbPrior) will + be interpreted as a nucleotide-level + prior (i.e. each nucleotide will be + given a prior read count of this value) + --sigDigits arg (=3) The number of significant digits to + write when outputting the + EffectiveLength and NumReads columns + --vbPrior arg (=0.01) The prior that will be used in the VBEM + algorithm. This is interpreted as a + per-transcript prior, unless the + --perNucleotidePrior flag is also + given. If the --perNucleotidePrior + flag is given, this is used as a + nucleotide-level prior. If the default + is used, it will be divided by 1000 + before being used as a nucleotide-level + prior, i.e. the default per-nucleotide + prior will be 1e-5. + --writeOrphanLinks Write the transcripts that are linked + by orphaned reads. + --writeUnmappedNames Write the names of un-mapped reads to + the file unmapped_names.txt in the + auxiliary directory. + + +```bash +salmon quant --help-alignment +``` +Quant +========== +Perform dual-phase, alignment-based estimation of +transcript abundance from RNA-seq reads + +salmon quant options: + + +alignment input options: + --discardOrphans [alignment-based mode only] : Discard + orphan alignments in the input . If + this flag is passed, then only paired + alignments will be considered toward + quantification estimates. The default + behavior is to consider orphan + alignments if no valid paired mappings + exist. + -l [ --libType ] arg Format string describing the library + type + -a [ --alignments ] arg input alignment (BAM) file(s). + -e [ --eqclasses ] arg input salmon weighted equivalence class + file. + -t [ --targets ] arg FASTA format file containing target + transcripts. + --ont use alignment model for Oxford Nanopore + long reads + + +basic options: + -v [ --version ] print version string + -h [ --help ] produce help message + -o [ --output ] arg Output quantification directory. + --seqBias Perform sequence-specific bias + correction. + --gcBias [beta for single-end reads] Perform + fragment GC bias correction. + --posBias Perform positional bias correction. + -p [ --threads ] arg (=8) The number of threads to use + concurrently. + --incompatPrior arg (=0) This option sets the prior probability + that an alignment that disagrees with + the specified library type (--libType) + results from the true fragment origin. + Setting this to 0 specifies that + alignments that disagree with the + library type should be "impossible", + while setting it to 1 says that + alignments that disagree with the + library type are no less likely than + those that do + -g [ --geneMap ] arg File containing a mapping of + transcripts to genes. If this file is + provided salmon will output both + quant.sf and quant.genes.sf files, + where the latter contains aggregated + gene-level abundance estimates. The + transcript to gene mapping should be + provided as either a GTF file, or a in + a simple tab-delimited format where + each line contains the name of a + transcript and the gene to which it + belongs separated by a tab. The + extension of the file is used to + determine how the file should be + parsed. Files ending in '.gtf', '.gff' + or '.gff3' are assumed to be in GTF + format; files with any other extension + are assumed to be in the simple format. + In GTF / GFF format, the + "transcript_id" is assumed to contain + the transcript identifier and the + "gene_id" is assumed to contain the + corresponding gene identifier. + --auxTargetFile arg A file containing a list of "auxiliary" + targets. These are valid targets + (i.e., not decoys) to which fragments + are allowed to map and be assigned, and + which will be quantified, but for which + auxiliary models like sequence-specific + and fragment-GC bias correction should + not be applied. + --meta If you're using Salmon on a metagenomic + dataset, consider setting this flag to + disable parts of the abundance + estimation model that make less sense + for metagenomic data. + + +alignment-specific options: + --noErrorModel Turn off the alignment error model, + which takes into account the the + observed frequency of different types + of mismatches / indels when computing + the likelihood of a given alignment. + Turning this off can speed up + alignment-based salmon, but can harm + quantification accuracy. + --numErrorBins arg (=6) The number of bins into which to divide + each read when learning and applying + the error model. For example, a value + of 10 would mean that effectively, a + separate error model is leared and + applied to each 10th of the read, while + a value of 3 would mean that a separate + error model is applied to the read + beginning (first third), middle (second + third) and end (final third). + -s [ --sampleOut ] Write a "postSample.bam" file in the + output directory that will sample the + input alignments according to the + estimated transcript abundances. If + you're going to perform downstream + analysis of the alignments with tools + which don't, themselves, take fragment + assignment ambiguity into account, you + should use this output. + -u [ --sampleUnaligned ] In addition to sampling the aligned + reads, also write the un-aligned reads + to "postSample.bam". + --gencode This flag will expect the input + transcript fasta to be in GENCODE + format, and will split the transcript + name at the first '|' character. These + reduced names will be used in the + output and when looking for these + transcripts in a gene to transcript + GTF. + --scoreExp arg (=1) The factor by which sub-optimal + alignment scores are downweighted to + produce a probability. If the best + alignment score for the current read is + S, and the score for a particular + alignment is w, then the probability + will be computed porportional to exp( - + scoreExp * (S-w) ). NOTE: This flag + only has an effect if you are parsing + alignments produced by salmon itself + (i.e. pufferfish or RapMap in + selective-alignment mode). + --mappingCacheMemoryLimit arg (=2000000) + If the file contained fewer than this + many mapped reads, then just keep the + data in memory for subsequent rounds of + inference. Obviously, this value should + not be too large if you wish to keep a + low memory usage, but setting it large + enough to accommodate all of the mapped + read can substantially speed up + inference on "small" files that contain + only a few million reads. + + +advanced options: + --alternativeInitMode [Experimental]: Use an alternative + strategy (rather than simple + interpolation between) the online and + uniform abundance estimates to + initialize the EM / VBEM algorithm. + --auxDir arg (=aux_info) The sub-directory of the quantification + directory where auxiliary information + e.g. bootstraps, bias parameters, etc. + will be written. + --skipQuant Skip performing the actual transcript + quantification (including any Gibbs + sampling or bootstrapping). + --dumpEq Dump the simple equivalence class + counts that were computed during + mapping or alignment. + -d [ --dumpEqWeights ] Dump conditional probabilities + associated with transcripts when + equivalence class information is being + dumped to file. Note, this will dump + the factorization that is actually used + by salmon's offline phase for + inference. If you are using + range-factorized equivalence classes + (the default) then the same transcript + set may appear multiple times with + different associated conditional + probabilities. + --minAssignedFrags arg (=10) The minimum number of fragments that + must be assigned to the transcriptome + for quantification to proceed. + --reduceGCMemory If this option is selected, a more + memory efficient (but slightly slower) + representation is used to compute + fragment GC content. Enabling this will + reduce memory usage, but can also + reduce speed. However, the results + themselves will remain the same. + --biasSpeedSamp arg (=5) The value at which the fragment length + PMF is down-sampled when evaluating + sequence-specific & GC fragment bias. + Larger values speed up effective length + correction, but may decrease the + fidelity of bias modeling results. + --fldMax arg (=1000) The maximum fragment length to consider + when building the empirical + distribution + --fldMean arg (=250) The mean used in the fragment length + distribution prior + --fldSD arg (=25) The standard deviation used in the + fragment length distribution prior + -f [ --forgettingFactor ] arg (=0.65000000000000002) + The forgetting factor used in the + online learning schedule. A smaller + value results in quicker learning, but + higher variance and may be unstable. A + larger value results in slower learning + but may be more stable. Value should + be in the interval (0.5, 1.0]. + --initUniform initialize the offline inference with + uniform parameters, rather than seeding + with online parameters. + --maxOccsPerHit arg (=1000) When collecting "hits" (MEMs), hits + having more than maxOccsPerHit + occurrences won't be considered. + -w [ --maxReadOcc ] arg (=200) Reads "mapping" to more than this many + places won't be considered. + --maxRecoverReadOcc arg (=2500) Relevant for alevin with '--sketch' + mode only: if a read has valid seed + matches, but no read has matches + leading to fewer than "maxReadOcc" + mappings, then try to recover mappings + for this read as long as there are + fewer than "maxRecoverReadOcc" + mappings. + --noLengthCorrection [experimental] : Entirely disables + length correction when estimating the + abundance of transcripts. This option + can be used with protocols where one + expects that fragments derive from + their underlying targets without regard + to that target's length (e.g. QuantSeq) + --noEffectiveLengthCorrection Disables effective length correction + when computing the probability that a + fragment was generated from a + transcript. If this flag is passed in, + the fragment length distribution is not + taken into account when computing this + probability. + --noSingleFragProb Disables the estimation of an + associated fragment length probability + for single-end reads or for orphaned + mappings in paired-end libraries. The + default behavior is to consider the + probability of all possible fragment + lengths associated with the retained + mapping. Enabling this flag (i.e. + turning this default behavior off) will + simply not attempt to estimate a + fragment length probability in such + cases. + --noFragLengthDist [experimental] : Don't consider + concordance with the learned fragment + length distribution when trying to + determine the probability that a + fragment has originated from a + specified location. Normally, + Fragments with unlikely lengths will be + assigned a smaller relative probability + than those with more likely lengths. + When this flag is passed in, the + observed fragment length has no effect + on that fragment's a priori + probability. + --noBiasLengthThreshold [experimental] : If this option is + enabled, then no (lower) threshold will + be set on how short bias correction can + make effective lengths. This can + increase the precision of bias + correction, but harm robustness. The + default correction applies a threshold. + --numBiasSamples arg (=2000000) Number of fragment mappings to use when + learning the sequence-specific bias + model. + --numAuxModelSamples arg (=5000000) The first are used + to train the auxiliary model parameters + (e.g. fragment length distribution, + bias, etc.). After ther first + observations the + auxiliary model parameters will be + assumed to have converged and will be + fixed. + --numPreAuxModelSamples arg (=5000) The first will + have their assignment likelihoods and + contributions to the transcript + abundances computed without applying + any auxiliary models. The purpose of + ignoring the auxiliary models for the + first + observations is to avoid applying these + models before their parameters have + been learned sufficiently well. + --useEM Use the traditional EM algorithm for + optimization in the batch passes. + --useVBOpt Use the Variational Bayesian EM + [default] + --rangeFactorizationBins arg (=4) Factorizes the likelihood used in + quantification by adopting a new notion + of equivalence classes based on the + conditional probabilities with which + fragments are generated from different + transcripts. This is a more + fine-grained factorization than the + normal rich equivalence classes. The + default value (4) corresponds to the + default used in Zakeri et al. 2017 + (doi: 10.1093/bioinformatics/btx262), + and larger values imply a more + fine-grained factorization. If range + factorization is enabled, a common + value to select for this parameter is + 4. A value of 0 signifies the use of + basic rich equivalence classes. + --numGibbsSamples arg (=0) Number of Gibbs sampling rounds to + perform. + --noGammaDraw This switch will disable drawing + transcript fractions from a Gamma + distribution during Gibbs sampling. In + this case the sampler does not account + for shot-noise, but only assignment + ambiguity + --numBootstraps arg (=0) Number of bootstrap samples to + generate. Note: This is mutually + exclusive with Gibbs sampling. + --bootstrapReproject This switch will learn the parameter + distribution from the bootstrapped + counts for each sample, but will + reproject those parameters onto the + original equivalence class counts. + --thinningFactor arg (=16) Number of steps to discard for every + sample kept from the Gibbs chain. The + larger this number, the less chance + that subsequent samples are + auto-correlated, but the slower + sampling becomes. + -q [ --quiet ] Be quiet while doing quantification + (don't write informative output to the + console unless something goes wrong). + --perTranscriptPrior The prior (either the default or the + argument provided via --vbPrior) will + be interpreted as a transcript-level + prior (i.e. each transcript will be + given a prior read count of this value) + --perNucleotidePrior The prior (either the default or the + argument provided via --vbPrior) will + be interpreted as a nucleotide-level + prior (i.e. each nucleotide will be + given a prior read count of this value) + --sigDigits arg (=3) The number of significant digits to + write when outputting the + EffectiveLength and NumReads columns + --vbPrior arg (=0.01) The prior that will be used in the VBEM + algorithm. This is interpreted as a + per-transcript prior, unless the + --perNucleotidePrior flag is also + given. If the --perNucleotidePrior + flag is given, this is used as a + nucleotide-level prior. If the default + is used, it will be divided by 1000 + before being used as a nucleotide-level + prior, i.e. the default per-nucleotide + prior will be 1e-5. + --writeOrphanLinks Write the transcripts that are linked + by orphaned reads. + --writeUnmappedNames Write the names of un-mapped reads to + the file unmapped_names.txt in the + auxiliary directory. \ No newline at end of file diff --git a/src/salmon/salmon_quant/script.sh b/src/salmon/salmon_quant/script.sh new file mode 100644 index 00000000..4c9f69d5 --- /dev/null +++ b/src/salmon/salmon_quant/script.sh @@ -0,0 +1,152 @@ +#!/bin/bash + +set -e + +## VIASH START +## VIASH END + +[[ "$par_discard_orphans" == "false" ]] && unset par_discard_orphans +[[ "$par_ont" == "false" ]] && unset par_ont +[[ "$par_seq_bias" == "false" ]] && unset par_seq_bias +[[ "$par_gc_bias" == "false" ]] && unset par_gc_bias +[[ "$par_pos_bias" == "false" ]] && unset par_pos_bias +[[ "$par_meta" == "false" ]] && unset par_meta +[[ "$par_discard_orphans_quasi" == "false" ]] && unset par_discard_orphans_quasi +[[ "$par_disable_chaining_heuristic" == "false" ]] && unset par_disable_chaining_heuristic +[[ "$par_allow_dovetail" == "false" ]] && unset par_allow_dovetail +[[ "$par_recover_orphans" == "false" ]] && unset par_recover_orphans +[[ "$par_mimicBT2" == "false" ]] && unset par_mimicBT2 +[[ "$par_mimic_strictBT2" == "false" ]] && unset par_mimic_strictBT2 +[[ "$par_softclip" == "false" ]] && unset par_softclip +[[ "$par_softclip_overhangs" == "false" ]] && unset par_softclip_overhangs +[[ "$par_full_length_alignment" == "false" ]] && unset par_full_length_alignment +[[ "$par_hard_filter" == "false" ]] && unset par_hard_filter +[[ "$par_write_mappings" == "false" ]] && unset par_write_mappings +[[ "$par_write_qualities" == "false" ]] && unset par_write_qualities +[[ "$par_alternative_init_mode" == "false" ]] && unset par_alternative_init_mode +[[ "$par_skip_quant" == "false" ]] && unset par_skip_quant +[[ "$par_dump_eq" == "false" ]] && unset par_dump_eq +[[ "$par_dump_eq_weights" == "false" ]] && unset par_dump_eq_weights +[[ "$par_reduce_GC_memory" == "false" ]] && unset par_reduce_GC_memory +[[ "$par_init_uniform" == "false" ]] && unset par_init_uniform +[[ "$par_no_length_correction" == "false" ]] && unset par_no_length_correction +[[ "$par_no_effective_length_correction" == "false" ]] && unset par_no_effective_length_correction +[[ "$par_no_single_frag_prob" == "false" ]] && unset par_no_single_frag_prob +[[ "$par_no_frag_length_dist" == "false" ]] && unset par_no_frag_length_dist +[[ "$par_no_bias_length_threshold" == "false" ]] && unset par_no_bias_length_threshold +[[ "$par_useEM" == "false" ]] && unset par_useEM +[[ "$par_useVBOpt" == "false" ]] && unset par_useVBOpt +[[ "$par_no_Gamma_draw" == "false" ]] && unset par_no_Gamma_draw +[[ "$par_bootstrap_reproject" == "false" ]] && unset par_bootstrap_reproject +[[ "$par_quiet" == "false" ]] && unset par_quiet +[[ "$par_per_transcript_prior" == "false" ]] && unset par_per_transcript_prior +[[ "$par_per_nucleotide_prior" == "false" ]] && unset par_per_nucleotide_prior +[[ "$par_write_orphan_links" == "false" ]] && unset par_write_orphan_links +[[ "$par_write_unmapped_names" == "false" ]] && unset par_write_unmapped_names +[[ "$par_no_error_model" == "false" ]] && unset par_no_error_model +[[ "$par_sample_out" == "false" ]] && unset par_sample_out +[[ "$par_sample_unaligned" == "false" ]] && unset par_sample_unaligned +[[ "$par_gencode" == "false" ]] && unset par_gencode + +IFS=";" read -ra unmated_reads <<< $par_unmated_reads +IFS=";" read -ra mates1 <<< $par_mates1 +IFS=";" read -ra mates2 <<< $par_mates2 +IFS=";" read -ra alignment <<< $par_alignments + +salmon quant \ + ${par_lib_type:+-l "${par_lib_type}"} \ + ${par_index:+-i "${par_index}"} \ + ${par_unmated_reads:+-r ${unmated_reads[*]}} \ + ${par_mates1:+-1 ${mates1[*]}} \ + ${par_mates2:+-2 ${mates2[*]}} \ + ${par_alignments:+-a ${alignment[*]}} \ + ${par_discard_orphans:+--discardOrphans} \ + ${par_eqclasses:+-e "${par_eqclasses}"} \ + ${par_targets:+-t "${par_targets}"} \ + ${par_ont:+--ont} \ + ${par_output:+-o "${par_output}"} \ + ${par_seq_bias:+--seqBias} \ + ${par_gc_bias:+--gcBias} \ + ${par_pos_bias:+--posBias} \ + ${meta_cpus:+-p "${meta_cpus}"} \ + ${par_incompat_prior:+--incompatPrior "${par_incompat_prior}"} \ + ${par_gene_map:+-g "${par_gene_map}"} \ + ${par_aux_target_file:+--auxTargetFile "${par_aux_target_file}"} \ + ${par_meta:+--meta} \ + ${par_score_exp:+--scoreExp "${par_score_exp}"} \ + ${par_discard_orphans_quasi:+--discardOrphansQuasi} \ + ${par_consensus_slack:+--consensusSlack "${par_consensus_slack}"} \ + ${par_pre_merge_chain_sub_thresh:+--preMergeChainSubThresh "${par_pre_merge_chain_sub_thresh}"} \ + ${par_post_merge_chain_sub_thresh:+--postMergeChainSubThresh "${par_post_merge_chain_sub_thresh}"} \ + ${par_orphan_chain_sub_thresh:+--orphanChainSubThresh "${par_orphan_chain_sub_thresh}"} \ + ${par_min_score_fraction:+--minScoreFraction "${par_min_score_fraction}"} \ + ${par_mismatch_seed_skip:+--mismatchSeedSkip "${par_mismatch_seed_skip}"} \ + ${par_disable_chaining_heuristic:+--disableChainingHeuristic} \ + ${par_decoy_threshold:+--decoyThreshold "${par_decoy_threshold}"} \ + ${par_ma:+--ma "${par_ma}"} \ + ${par_mp:+--mp "${par_mp}"} \ + ${par_go:+--go "${par_go}"} \ + ${par_ge:+--ge "${par_ge}"} \ + ${par_bandwidth:+--bandwidth "${par_bandwidth}"} \ + ${par_allow_dovetail:+--allowDovetail} \ + ${par_recover_orphans:+--recoverOrphans} \ + ${par_mimicBT2:+--mimicBT2} \ + ${par_mimic_strictBT2:+--mimicStrictBT2} \ + ${par_softclip:+--softclip} \ + ${par_softclip_overhangs:+--softclipOverhangs} \ + ${par_full_length_alignment:+--fullLengthAlignment} \ + ${par_hard_filter:+--hardFilter} \ + ${par_min_aln_prob:+--minAlnProb "${par_min_aln_prob}"} \ + ${par_write_mappings:+--write_mappings="${par_mappings_sam}"} \ + ${par_write_qualities:+--writeQualities} \ + ${par_hit_filter_policy:+--hitFilterPolicy "${par_hit_filter_policy}"} \ + ${par_alternative_init_mode:+--alternativeInitMode} \ + ${par_aux_dir:+--auxDir "${par_aux_dir}"} \ + ${par_skip_quant:+--skipQuant} \ + ${par_dump_eq:+--dumpEq} \ + ${par_dump_eq_weights:+-d "${par_dump_eq_weights}"} \ + ${par_min_assigned_frags:+--minAssignedFrags "${par_min_assigned_frags}"} \ + ${par_reduce_GC_memory:+--reduceGCMemory} \ + ${par_bias_speed_samp:+--biasSpeedSamp "${par_bias_speed_samp}"} \ + ${par_fld_max:+--fldMax "${par_fld_max}"} \ + ${par_fld_mean:+--fldMean "${par_fld_mean}"} \ + ${par_fld_SD:+--fldSD "${par_fld_SD}"} \ + ${par_forgetting_factor:+-f "${par_forgetting_factor}"} \ + ${par_init_uniform:+--initUniform} \ + ${par_max_occs_per_hit:+--maxOccsPerHit "${par_max_occs_per_hit}"} \ + ${par_max_read_occ:+-w "${par_max_read_occ}"} \ + ${par_no_length_correction:+--noLengthCorrection} \ + ${par_no_effective_length_correction:+--noEffectiveLengthCorrection} \ + ${par_no_single_frag_prob:+--noSingleFragProb} \ + ${par_no_frag_length_dist:+--noFragLengthDist} \ + ${par_no_bias_length_threshold:+--noBiasLengthThreshold} \ + ${par_num_bias_samples:+--numBiasSamples "${par_num_bias_samples}"} \ + ${par_num_aux_model_samples:+--numAuxModelSamples "${par_num_aux_model_samples}"} \ + ${par_num_pre_aux_model_samples:+--numPreAuxModelSamples "${par_num_pre_aux_model_samples}"} \ + ${par_useEM:+--useEM} \ + ${par_useVBOpt:+--useVBOpt} \ + ${par_range_factorization_bins:+--rangeFactorizationBins "${par_range_factorization_bins}"} \ + ${par_num_Gibbs_samples:+--numGibbsSamples "${par_num_Gibbs_samples}"} \ + ${par_no_Gamma_draw:+--noGammaDraw} \ + ${par_num_bootstraps:+--numBootstraps "${par_num_bootstraps}"} \ + ${par_bootstrap_reproject:+--bootstrapReproject} \ + ${par_thinning_factor:+--thinningFactor "${par_thinning_factor}"} \ + ${par_quiet:+--quiet} \ + ${par_per_transcript_prior:+--perTranscriptPrior} \ + ${par_per_nucleotide_prior:+--perNucleotidePrior} \ + ${par_sig_digits:+--sigDigits "${par_sig_digits}"} \ + ${par_vb_prior:+--vbPrior "${par_vb_prior}"} \ + ${par_write_orphan_links:+--writeOrphanLinks} \ + ${par_write_unmapped_names:+--writeUnmappedNames} \ + ${par_no_error_model:+--noErrorModel} \ + ${par_num_error_bins:+--numErrorBins "${par_num_error_bins}"} \ + ${par_sample_out:+--sampleOut} \ + ${par_sample_unaligned:+--sampleUnaligned} \ + ${par_gencode:+--gencode} \ + ${par_mapping_cache_memory_limit:+--mappingCacheMemoryLimit "${par_mapping_cache_memory_limit}"} + +if [ -f "$par_output/quant.sf" ]; then + mv $par_output/quant.sf $par_quant_results +else + echo "Quantification file not generated!" +fi \ No newline at end of file diff --git a/src/salmon/salmon_quant/test.sh b/src/salmon/salmon_quant/test.sh new file mode 100644 index 00000000..54953a87 --- /dev/null +++ b/src/salmon/salmon_quant/test.sh @@ -0,0 +1,156 @@ +#!/bin/bash + +set -e + +echo "===============================================================================" +echo "> Prepare test data" + +dir_in="test_data" +mkdir -p "$dir_in" + +cat > "$dir_in/transcriptome.fasta" <<'EOF' +>contig1 +AGCTCCAGATTCGCTCAGGCCCTTGATCATCAGTCGTCGTCGTCTTCGATTTGCCAGAGG +AGTTTAGATGAAGAATGTCAAGGATGTTCCTCCCTGCCCTCCCATCTAGCCAAGAACATT +TCCAAGAAGATAAAACTGTCACTGAGACAGGTCTGGATGCGCCCTAGGGGCAAATAGAGA +>contig2 +AGGCCTTTACCACATTGCTGCTGGCTATAGGAAGTCCCAGGTACTAGCCTGAAACAGCTG +ATATTTGGGGCTGTCACAGACAATATGGCCACCCCTTGGTCTTTATGCATGAAGATTATG +TAAAGGTTTTTATTAAAAAATATATATATATATATAAATGATCTAGATTATTTTCCTCTT +TCTGAAGTACTTTCTTAAAAAAATAAAATTAAATGTTTATAGTATTCCCGGT +EOF + +cat > "$dir_in/a_1.fq" <<'EOF' +@SEQ_ID1 +AGAATGTCAAGGATGTTCCTCC ++ +IIIIIIIIIIIIIIIIIIIIII +@SEQ_ID2 +ACCCGCAAGATTAGGCTCCGTA ++ +!!!!!!!!!!!!!!!!!!!!!! +@SEQ_ID3 +CTCAGGCCCTTGATCATCAGTC ++ +IIIIIIIIIIIIIIIIIIIIII +EOF + +cat > "$dir_in/a_2.fq" <<'EOF' +@SEQ_ID1 +GGAGGAACATCCTTGACATTCT ++ +IIIIIIIIIIIIIIIIIIIIII +@SEQ_ID2 +GTGTACGGAGCCTAATCTTGCA ++ +!!!!!!!!!!!!!!!!!!!!!! +@SEQ_ID3 +GACTGATGATCAAGGGCCTGAG ++ +IIIIIIIIIIIIIIIIIIIIII +EOF + +cat > "$dir_in/b_1.fq" <<'EOF' +@SEQ_ID1 +CTTTACCACATTGCTGCTGGCT ++ +IIIIIIIIIIIIIIIIIIIIII +@SEQ_ID2 +ATTAGGCTCCGTAACCCGCAAG ++ +!!!!!!!!!!!!!!!!!!!!!! +@SEQ_ID3 +GCCACCCCTTGGTCTTTATGCA ++ +IIIIIIIIIIIIIIIIIIIIII +EOF + +cat > "$dir_in/b_2.fq" <<'EOF' +@SEQ_ID1 +AGCCAGCAGCAATGTGGTAAAG ++ +IIIIIIIIIIIIIIIIIIIIII +@SEQ_ID2 +CTTGCGGGTTACGGAGCCTAAT ++ +!!!!!!!!!!!!!!!!!!!!!! +@SEQ_ID3 +TGCATAAAGACCAAGGGGTGGC ++ +IIIIIIIIIIIIIIIIIIIIII +EOF + +echo "===============================================================================" +echo "> Run salmon index" + +salmon index \ + --transcripts "$dir_in/transcriptome.fasta" \ + --index "$dir_in/index" \ + --kmerLen 11 + +echo "===============================================================================" +echo "> Run salmon quant for single-end reads" +"$meta_executable" \ + --lib_type "A" \ + --index "$dir_in/index" \ + --unmated_reads "$dir_in/a_1.fq" \ + --output "quant_se_results" \ + --quant_results "quant_se.sf" \ + --min_assigned_frags 1 + +echo ">> Checking output" +[ ! -d "quant_se_results" ] && echo "Output directory quant_se_results does not exist" && exit 1 +[ ! -f "quant_se.sf" ] && echo "Output file quant_se.sf does not exist!" && exit 1 +[ ! -s "quant_se.sf" ] && echo "Output file quant_se.sf is empty!" && exit 1 +grep -q "Name Length EffectiveLength TPM NumReads" "quant_se.sf" || (echo "Output file quant_se.sf does not have the right format!" && exit 1) +[ $(grep "contig1" "quant_se.sf" | cut -f 5) != '2.000' ] && echo "Number of reads mapping to contig1 does not match the expected value!" && exit 1 +[ $(grep "contig2" "quant_se.sf" | cut -f 5) != '0.000' ] && echo "Number of reads mapping to contig2 does not match the expected value!" && exit 1 +[ $(grep '"percent_mapped":' quant_se_results/aux_info/meta_info.json | cut -d':' -f 2) != '66.66666666666666,' ] && echo "Mapping rate does not match the expected value!" && exit 1 + +echo "===============================================================================" +echo "> Run salmon quant for paired-end reads" +"$meta_executable" \ + --lib_type "A" \ + --index "$dir_in/index" \ + --mates1 "$dir_in/a_1.fq" \ + --mates2 "$dir_in/a_2.fq" \ + --output "quant_pe_results" \ + --quant_results "quant_pe.sf" \ + --min_assigned_frags 1 + +echo ">> Checking output" +[ ! -d "quant_pe_results" ] && echo "Output directory quant_pe_results does not exist" && exit +[ ! -f "quant_pe.sf" ] && echo "Output file quant_pe.sf does not exist!" && exit 1 +[ ! -s "quant_pe.sf" ] && echo "Output file quant_pe.sf is empty!" && exit 1 +grep -q "Name Length EffectiveLength TPM NumReads" "quant_pe.sf" || (echo "Output file quant_pe.sf does not have the right format!" && exit 1) +[ $(grep "contig1" "quant_pe.sf" | cut -f 5) != '2.000' ] && echo "Number of reads mapping to contig1 does not match the expected value!" && exit 1 +[ $(grep "contig2" "quant_pe.sf" | cut -f 5) != '0.000' ] && echo "Number of reads mapping to contig2 does not match the expected value!" && exit 1 +[ $(grep '"percent_mapped":' quant_pe_results/aux_info/meta_info.json | cut -d':' -f 2) != '66.66666666666666,' ] && echo "Mapping rate does not match the expected value!" && exit 1 + +echo "===============================================================================" +echo "> Run salmon quant for paired-end reads with technical replicates" +"$meta_executable" \ + --lib_type "A" \ + --index "$dir_in/index" \ + --mates1 "$dir_in/a_1.fq;$dir_in/b_1.fq" \ + --mates2 "$dir_in/a_2.fq;$dir_in/b_2.fq" \ + --output "quant_pe_rep_results" \ + --quant_results "quant_pe_rep.sf" \ + --min_assigned_frags 1 + +echo ">> Checking output" +[ ! -d "quant_pe_rep_results" ] && echo "Output directory quant_pe_rep_results does not exist" && exit +[ ! -f "quant_pe_rep.sf" ] && echo "Output file quant_pe_rep.sf does not exist!" && exit 1 +[ ! -s "quant_pe_rep.sf" ] && echo "Output file quant_pe_rep.sf is empty!" && exit 1 +grep -q "Name Length EffectiveLength TPM NumReads" "quant_pe_rep.sf" || (echo "Output file quant_pe_rep.sf does not have the right format!" && exit 1) +[ $(grep "contig1" "quant_pe_rep.sf" | cut -f 5) != '2.000' ] && echo "Number of reads mapping to contig1 does not match the expected value!" && exit 1 +[ $(grep "contig2" "quant_pe_rep.sf" | cut -f 5) != '2.000' ] && echo "Number of reads mapping to contig2 does not match the expected value!" && exit 1 +[ $(grep '"percent_mapped":' quant_pe_rep_results/aux_info/meta_info.json | cut -d':' -f 2) != '66.66666666666666,' ] && echo "Mapping rate does not match the expected value!" && exit 1 + + +# TODO: check counts and mapping rates +# contig1 should have 2 reads, contig2 should have 2 reads +# mapping rate should be 66.6% + +echo "===============================================================================" +echo "> Test successful" \ No newline at end of file diff --git a/src/samtools/samtools_collate/config.vsh.yaml b/src/samtools/samtools_collate/config.vsh.yaml new file mode 100644 index 00000000..669f4cdf --- /dev/null +++ b/src/samtools/samtools_collate/config.vsh.yaml @@ -0,0 +1,94 @@ +name: samtools_collate +namespace: samtools +description: Shuffles and groups reads in SAM/BAM/CRAM files together by their names. +keywords: [collate, counts, bam, sam, cram] +links: + homepage: https://www.htslib.org/ + documentation: https://www.htslib.org/doc/samtools-icollate.html + repository: https://github.com/samtools/samtools +references: + doi: [10.1093/bioinformatics/btp352, 10.1093/gigascience/giab008] +license: MIT/Expat + +argument_groups: + - name: Inputs + arguments: + - name: --input + type: file + description: The input BAM file. + required: true + - name: --reference + type: file + description: Reference sequence FASTA FILE. + + - name: Outputs + arguments: + - name: --output + alternatives: -o + type: file + description: The output filename. + required: true + direction: output + + - name: Options + arguments: + - name: --uncompressed + alternatives: -u + type: boolean_true + description: Output uncompressed BAM. + - name: --fast + alternatives: -f + type: boolean_true + description: Fast mode, only primary alignments. + - name: --working_reads + alternatives: -r + type: integer + description: Working reads stored (for use with -f). + default: 10000 + - name: --compression + alternatives: -l + type: integer + description: Compression level. + default: 1 + - name: --nb_tmp_files + alternatives: -n + type: integer + description: Number of temporary files. + default: 64 + - name: --tmp_prefix + alternatives: -T + type: string + description: Write temporary files to PREFIX.nnnn.bam. + - name: --no_pg + type: boolean_true + description: Do not add a PG line. + - name: --input_fmt_option + type: string + description: Specify a single input file format option in the form of OPTION or OPTION=VALUE. + - name: --output_fmt + type: string + description: Specify output format (SAM, BAM, CRAM). + - name: --output_fmt_option + type: string + description: Specify a single output file format option in the form of OPTION or OPTION=VALUE. + + +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 + setup: + - type: docker + run: | + samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ + sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt +runners: +- type: executable +- type: nextflow diff --git a/src/samtools/samtools_collate/help.txt b/src/samtools/samtools_collate/help.txt new file mode 100644 index 00000000..16190f4b --- /dev/null +++ b/src/samtools/samtools_collate/help.txt @@ -0,0 +1,31 @@ +``` +samtools collate +``` +Usage: samtools collate [options...] [] + +Options: + -O Output to stdout + -o Output file name (use prefix if not set) + -u Uncompressed BAM output + -f Fast (only primary alignments) + -r Working reads stored (with -f) [10000] + -l INT Compression level [1] + -n INT Number of temporary files [64] + -T PREFIX + Write temporary files to PREFIX.nnnn.bam + --no-PG do not add a PG line + --input-fmt-option OPT[=VAL] + Specify a single input file format option in the form + of OPTION or OPTION=VALUE + --output-fmt FORMAT[,OPT[=VAL]]... + Specify output format (SAM, BAM, CRAM) + --output-fmt-option OPT[=VAL] + Specify a single output file format option in the form + of OPTION or OPTION=VALUE + --reference FILE + Reference sequence FASTA FILE [null] + -@, --threads INT + Number of additional threads to use [0] + --verbosity INT + Set level of verbosity + is required unless the -o or -O options are used. \ No newline at end of file diff --git a/src/samtools/samtools_collate/script.sh b/src/samtools/samtools_collate/script.sh new file mode 100644 index 00000000..25847a52 --- /dev/null +++ b/src/samtools/samtools_collate/script.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +set -e + +[[ "$par_uncompressed" == "false" ]] && unset par_uncompressed +[[ "$par_fast" == "false" ]] && unset par_fast +[[ "$par_no_pg" == "false" ]] && unset par_no_pg + +samtools collate \ + "$par_input" \ + ${par_output:+-o "$par_output"} \ + ${par_reference:+-T "$par_reference"} \ + ${par_uncompressed:+-u} \ + ${par_fast:+-f} \ + ${par_working_reads:+-r "$par_working_reads"} \ + ${par_compression:+-l "$par_compression"} \ + ${par_nb_tmp_files:+-n "$par_nb_tmp_files"} \ + ${par_tmp_prefix:+-T "$par_tmp_prefix"} \ + ${par_no_pg:+-P} \ + ${par_input_fmt_option:+-O "$par_input_fmt_option"} \ + ${par_output_fmt:+-O "$par_output_fmt"} \ + ${par_output_fmt_option:+-O "$par_output_fmt_option"} + +exit 0 diff --git a/src/samtools/samtools_collate/test.sh b/src/samtools/samtools_collate/test.sh new file mode 100644 index 00000000..c5a2e6e6 --- /dev/null +++ b/src/samtools/samtools_collate/test.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +test_dir="${meta_resources_dir}/test_data" +out_dir="${meta_resources_dir}/out" + +############################################################################################ + +echo ">>> Test 1: $meta_functionality_name" +"$meta_executable" \ + --input "$test_dir/test.paired_end.sorted.bam" \ + --output "$out_dir/collated.bam" + +echo ">>> Checking whether output exists" +[ ! -f "$out_dir/collated.bam" ] && echo "File 'collated.bam' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$out_dir/collated.bam" ] && echo "File 'collated.bam' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff <(samtools view "$out_dir/collated.bam") \ + <(samtools view "$test_dir/collated.bam") || \ + (echo "Output file collated.bam does not match expected output" && exit 1) + +############################################################################################ + +echo ">>> Test 2: $meta_functionality_name with --fast option" +"$meta_executable" \ + --fast \ + --input "$test_dir/test.paired_end.sorted.bam" \ + --output "$out_dir/fast_collated.bam" + +echo ">>> Checking whether output exists" +[ ! -f "$test_dir/fast_collated.bam" ] && echo "File 'fast_collated.bam' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/fast_collated.bam" ] && echo "File 'fast_collated.bam' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff <(samtools view "$test_dir/fast_collated.bam") \ + <(samtools view "$test_dir/fast_collated.bam") || \ + (echo "Output file fast_collated.bam does not match expected output" && exit 1) + + +############################################################################################ + +echo ">>> Test 3: $meta_functionality_name with compression" +"$meta_executable" \ + --compression 8 \ + --input "$test_dir/test.paired_end.sorted.bam" \ + --output "$out_dir/comp_collated.bam" + +echo ">>> Checking whether output exists" +[ ! -f "$out_dir/comp_collated.bam" ] && echo "File 'comp_collated.bam' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$out_dir/comp_collated.bam" ] && echo "File 'comp_collated.bam' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff <(samtools view "$out_dir/comp_collated.bam") \ + <(samtools view "$test_dir/comp_collated.bam") || \ + (echo "Output file comp_collated.bam does not match expected output" && exit 1) + +############################################################################################ + +echo ">>> All tests passed successfully." + +exit 0 diff --git a/src/samtools/samtools_collate/test_data/collated.bam b/src/samtools/samtools_collate/test_data/collated.bam new file mode 100644 index 00000000..f6d5eab9 Binary files /dev/null and b/src/samtools/samtools_collate/test_data/collated.bam differ diff --git a/src/samtools/samtools_collate/test_data/comp_collated.bam b/src/samtools/samtools_collate/test_data/comp_collated.bam new file mode 100644 index 00000000..1f26cee4 Binary files /dev/null and b/src/samtools/samtools_collate/test_data/comp_collated.bam differ diff --git a/src/samtools/samtools_collate/test_data/fast_collated.bam b/src/samtools/samtools_collate/test_data/fast_collated.bam new file mode 100644 index 00000000..bb78fe5a Binary files /dev/null and b/src/samtools/samtools_collate/test_data/fast_collated.bam differ diff --git a/src/samtools/samtools_collate/test_data/script.sh b/src/samtools/samtools_collate/test_data/script.sh new file mode 100755 index 00000000..f97a7efe --- /dev/null +++ b/src/samtools/samtools_collate/test_data/script.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +# dowload test data from nf-core module +wget https://github.com/nf-core/test-datasets/raw/modules/data/genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam \ No newline at end of file diff --git a/src/samtools/samtools_collate/test_data/test.paired_end.sorted.bam b/src/samtools/samtools_collate/test_data/test.paired_end.sorted.bam new file mode 100644 index 00000000..85cccf14 Binary files /dev/null and b/src/samtools/samtools_collate/test_data/test.paired_end.sorted.bam differ diff --git a/src/samtools/samtools_faidx/config.vsh.yaml b/src/samtools/samtools_faidx/config.vsh.yaml new file mode 100644 index 00000000..c1c9325d --- /dev/null +++ b/src/samtools/samtools_faidx/config.vsh.yaml @@ -0,0 +1,95 @@ +name: samtools_faidx +namespace: samtools +description: Indexes FASTA files to enable random access to fasta and fastq files. +keywords: [ idex, fasta, faidx ] +links: + homepage: https://www.htslib.org/ + documentation: https://www.htslib.org/doc/samtools-faidx.html + repository: https://github.com/samtools/samtools +references: + doi: [10.1093/bioinformatics/btp352, 10.1093/gigascience/giab008] +license: MIT/Expat + +argument_groups: + - name: Inputs + arguments: + - name: --input + type: file + description: | + FASTA input file. + - name: --length + alternatives: -n + type: integer + description: | + Length for FASTA sequence line wrapping. If zero, this means do not + line wrap. Defaults to the line length in the input file. + default: 60 + - name: --region_file + alternatives: -r + type: file + description: | + File of regions. Format is chr:from-to. One per line. + Must be used with --output to avoid sending output to stdout. + - name: Options + arguments: + - name: --continue + type: boolean_true + description: | + Continue working if a non-existent region is requested. + - name: --reverse_complement + alternatives: -i + type: boolean_true + description: | + Reverse complement sequences. + - name: Outputs + arguments: + - name: --output + alternatives: -o + type: file + description: | + Write output to file. + direction: output + required: true + example: output.fasta + - name: --mark_strand + type: string + description: | + Add strand indicator to sequence name. Options are: + [ rc, no, sign, custom,, ] + default: rc + - name: --fai_idx + type: file + description: | + Read/Write to specified index file (default file.fa.fai). + direction: output + example: file.fa.fai + - name: --gzi_idx + type: file + description: | + Read/Write to specified compressed file index (used with .gz files, default file.fa.gz.gzi). + direction: output + example: file.fa.gz.gzi + - name: --fastq + type: boolean_true + description: | + Read FASTQ files and output extracted sequences in FASTQ format. Same as using samtools fqidx. + +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 + setup: + - type: docker + run: | + samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ + sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt +runners: +- type: executable +- type: nextflow diff --git a/src/samtools/samtools_faidx/help.txt b/src/samtools/samtools_faidx/help.txt new file mode 100644 index 00000000..89320c6f --- /dev/null +++ b/src/samtools/samtools_faidx/help.txt @@ -0,0 +1,19 @@ +```sh +samtools faidx -h +``` +Usage: samtools faidx [ [...]] +Option: + -o, --output FILE Write FASTA to file. + -n, --length INT Length of FASTA sequence line. [60] + -c, --continue Continue after trying to retrieve missing region. + -r, --region-file FILE File of regions. Format is chr:from-to. One per line. + -i, --reverse-complement Reverse complement sequences. + --mark-strand TYPE Add strand indicator to sequence name + TYPE = rc for /rc on negative strand (default) + no for no strand indicator + sign for (+) / (-) + custom,, for custom indicator + --fai-idx FILE name of the index file (default file.fa.fai). + --gzi-idx FILE name of compressed file index (default file.fa.gz.gzi). + -f, --fastq File and index in FASTQ format. + -h, --help This message. \ No newline at end of file diff --git a/src/samtools/samtools_faidx/script.sh b/src/samtools/samtools_faidx/script.sh new file mode 100644 index 00000000..61502d5f --- /dev/null +++ b/src/samtools/samtools_faidx/script.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +set -e + +[[ "$par_continue" == "false" ]] && unset par_continue +[[ "$par_reverse_complement" == "false" ]] && unset par_reverse_complement +[[ "$par_fastq" == "false" ]] && unset par_fastq + +samtools faidx \ + "$par_input" \ + ${par_output:+-o "$par_output"} \ + ${par_length:+-n "$par_length"} \ + ${par_continue:+-c} \ + ${par_region_file:+-r "$par_region_file"} \ + ${par_reverse_complement:+-r} \ + ${par_mark_strand:+--mark-strand "$par_mark_strand"} \ + ${par_fai_idx:+--fai-idx "$par_fai_idx"} \ + ${par_gzi_idx:+--gzi-idx "$par_gzi_idx"} \ + ${par_fastq:+-f} + +exit 0 \ No newline at end of file diff --git a/src/samtools/samtools_faidx/test.sh b/src/samtools/samtools_faidx/test.sh new file mode 100644 index 00000000..10627c16 --- /dev/null +++ b/src/samtools/samtools_faidx/test.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +test_dir="${meta_resources_dir}/test_data" +echo ">>> Testing $meta_functionality_name" + +"$meta_executable" \ + --input "$test_dir/test.fasta" \ + --output "$test_dir/test.fasta.fai" + +echo "$meta_executable" +echo "$test_dir/test.fasta" + +echo ">>> Checking whether output exists" +[ ! -f "$test_dir/test.fasta.fai" ] && echo "File 'test.fasta.fai' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/test.fasta.fai" ] && echo "File 'test.fasta.fai' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff "$test_dir/test.fasta.fai" "$test_dir/output/test.fasta.fai" || \ + (echo "Output file test.fasta.fai does not match expected output" && exit 1) + +rm "$test_dir/test.fasta.fai" + +#################################################################################################### + +echo ">>> Test 2: ${meta_functionality_name} with bgzipped input" + +"$meta_executable" \ + --input "$test_dir/test.fasta.gz" \ + --output "$test_dir/test.fasta.gz.fai" + +echo ">>> Checking whether output exists"1 +[ ! -f "$test_dir/test.fasta.gz.fai" ] && echo "File 'test.fasta.gz.fai' does not exist!" && exit 1 +[ ! -f "$test_dir/test.fasta.gz.gzi" ] && echo "File 'test.fasta.gz.gzi' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/test.fasta.gz.fai" ] && echo "File 'test.fasta.gz.fai' is empty!" && exit 1 +[ ! -s "$test_dir/test.fasta.gz.gzi" ] && echo "File 'test.fasta.gz.gzi' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff "$test_dir/test.fasta.gz.fai" "$test_dir/output/test.fasta.gz.fai" || \ + (echo "Output file test_zip.fasta.gz.fai does not match expected output" && exit 1) +diff "$test_dir/test.fasta.gz.gzi" "$test_dir/output/test.fasta.gz.gzi" || \ + (echo "Output file test2.fasta.gz.gzi does not match expected output" && exit 1) + +rm "$test_dir/test.fasta.gz.fai" +rm "$test_dir/test.fasta.gz.gzi" + +#################################################################################################### + +echo ">>> Test 3: ${meta_functionality_name} with fastq input" + +"$meta_executable" \ + --input "$test_dir/test.fastq" \ + --output "$test_dir/test.fastq.fai" + +echo ">>> Checking whether output exists" +[ ! -f "$test_dir/test.fastq.fai" ] && echo "File 'test.fastq.fai' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/test.fastq.fai" ] && echo "File 'test.fastq.fai' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff "$test_dir/test.fastq.fai" "$test_dir/output/test.fastq.fai" || \ + (echo "Output file test.fastq.fai does not match expected output" && exit 1) + +rm "$test_dir/test.fastq.fai" + +#################################################################################################### + +echo ">>> Test 4: ${meta_functionality_name} with region file containing non-existent regions and + specific fasta line wrap length" + +"$meta_executable" \ + --input "$test_dir/test.fasta" \ + --output "$test_dir/regions.fasta" \ + --length 10 \ + --continue \ + --region_file "$test_dir/test.regions" \ + --fai_idx "$test_dir/regions.fasta.fai" + +echo ">>> Checking whether output exists" +[ ! -f "$test_dir/regions.fasta" ] && echo "File 'regions.fasta' does not exist!" && exit 1 +[ ! -f "$test_dir/regions.fasta.fai" ] && echo "File 'regions.fasta.fai' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/regions.fasta" ] && echo "File 'regions.fasta' is empty!" && exit 1 +[ ! -s "$test_dir/regions.fasta.fai" ] && echo "File 'regions.fasta.fai' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff "$test_dir/regions.fasta" "$test_dir/output/regions.fasta" || \ + (echo "Output file regions.fasta does not match expected output" && exit 1) +diff "$test_dir/regions.fasta.fai" "$test_dir/output/regions.fasta.fai" || \ + (echo "Output file regions.fasta.fai does not match expected output" && exit 1) + +rm "$test_dir/regions.fasta" +rm "$test_dir/regions.fasta.fai" + +#################################################################################################### + +echo "All tests succeeded!" +exit 0 + diff --git a/src/samtools/samtools_faidx/test_data/output/regions.fasta b/src/samtools/samtools_faidx/test_data/output/regions.fasta new file mode 100644 index 00000000..6953e46d --- /dev/null +++ b/src/samtools/samtools_faidx/test_data/output/regions.fasta @@ -0,0 +1,14 @@ +>YAL069W:300-315 +CCCAAATATT +GTATAA +>YAL068C:200-230 +CTGAAGCCGT +TTTCAACTAC +GGTGACTTCA +C +>YAL067W-A:115-145 +GCTTATTGTC +TAAGCCTGAA +TTCAGTCTGC +T +>chr1:1-100 diff --git a/src/samtools/samtools_faidx/test_data/output/regions.fasta.fai b/src/samtools/samtools_faidx/test_data/output/regions.fasta.fai new file mode 100644 index 00000000..475dde4d --- /dev/null +++ b/src/samtools/samtools_faidx/test_data/output/regions.fasta.fai @@ -0,0 +1,4 @@ +YAL069W 315 19 70 71 +YAL068W-A 255 360 70 71 +YAL068C 363 638 70 71 +YAL067W-A 228 1028 70 71 diff --git a/src/samtools/samtools_faidx/test_data/output/test.fasta.fai b/src/samtools/samtools_faidx/test_data/output/test.fasta.fai new file mode 100644 index 00000000..475dde4d --- /dev/null +++ b/src/samtools/samtools_faidx/test_data/output/test.fasta.fai @@ -0,0 +1,4 @@ +YAL069W 315 19 70 71 +YAL068W-A 255 360 70 71 +YAL068C 363 638 70 71 +YAL067W-A 228 1028 70 71 diff --git a/src/samtools/samtools_faidx/test_data/output/test.fasta.gz.fai b/src/samtools/samtools_faidx/test_data/output/test.fasta.gz.fai new file mode 100644 index 00000000..475dde4d --- /dev/null +++ b/src/samtools/samtools_faidx/test_data/output/test.fasta.gz.fai @@ -0,0 +1,4 @@ +YAL069W 315 19 70 71 +YAL068W-A 255 360 70 71 +YAL068C 363 638 70 71 +YAL067W-A 228 1028 70 71 diff --git a/src/samtools/samtools_faidx/test_data/output/test.fasta.gz.gzi b/src/samtools/samtools_faidx/test_data/output/test.fasta.gz.gzi new file mode 100644 index 00000000..1b1cb4d4 Binary files /dev/null and b/src/samtools/samtools_faidx/test_data/output/test.fasta.gz.gzi differ diff --git a/src/samtools/samtools_faidx/test_data/output/test.fastq.fai b/src/samtools/samtools_faidx/test_data/output/test.fastq.fai new file mode 100644 index 00000000..d489386a --- /dev/null +++ b/src/samtools/samtools_faidx/test_data/output/test.fastq.fai @@ -0,0 +1,2 @@ +fastq1 66 8 30 31 79 +fastq2 28 156 14 15 188 diff --git a/src/samtools/samtools_faidx/test_data/script.sh b/src/samtools/samtools_faidx/test_data/script.sh new file mode 100644 index 00000000..ffd5b789 --- /dev/null +++ b/src/samtools/samtools_faidx/test_data/script.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +wget https://raw.githubusercontent.com/nf-core/test-datasets/rnaseq3/reference/transcriptome.fasta + +head -n 23 transcriptome.fasta > test.fasta # kepp only 4 first entries of the file for testing. + +rm transcriptome.fasta \ No newline at end of file diff --git a/src/samtools/samtools_faidx/test_data/test.fasta b/src/samtools/samtools_faidx/test_data/test.fasta new file mode 100644 index 00000000..eee04dde --- /dev/null +++ b/src/samtools/samtools_faidx/test_data/test.fasta @@ -0,0 +1,23 @@ +>YAL069W CDS=1-315 +ATGATCGTAAATAACACACACGTGCTTACCCTACCACTTTATACCACCACCACATGCCATACTCACCCTC +ACTTGTATACTGATTTTACGTACGCACACGGATGCTACAGTATATACCATCTCAAACTTACCCTACTCTC +AGATTCCACTTCACTCCATGGCCCATCTCTCACTGAATCAGTACCAAATGCACTCACATCATTATGCACG +GCACTTGCCTCAGCGGTCTATACCCTGTGCCATTTACCCATAACGCCCATCATTATCCACATTTTGATAT +CTATATCTCATTCGGCGGTCCCAAATATTGTATAA +>YAL068W-A CDS=1-255 +ATGCACGGCACTTGCCTCAGCGGTCTATACCCTGTGCCATTTACCCATAACGCCCATCATTATCCACATT +TTGATATCTATATCTCATTCGGCGGTCCCAAATATTGTATAACTGCCCTTAATACATACGTTATACCACT +TTTGCACCATATACTTACCACTCCATTTATATACACTTATGTCAATATTACAGAAAAATCCCCACAAAAA +TCACCTAAACATAAAAATATTCTACTTTTCAACAATAATACATAA +>YAL068C CDS=1-363 +ATGGTCAAATTAACTTCAATCGCCGCTGGTGTCGCTGCCATCGCTGCTACTGCTTCTGCAACCACCACTC +TAGCTCAATCTGACGAAAGAGTCAACTTGGTGGAATTGGGTGTCTACGTCTCTGATATCAGAGCTCACTT +AGCCCAATACTACATGTTCCAAGCCGCCCACCCAACTGAAACCTACCCAGTCGAAGTTGCTGAAGCCGTT +TTCAACTACGGTGACTTCACCACCATGTTGACCGGTATTGCTCCAGACCAAGTGACCAGAATGATCACCG +GTGTTCCATGGTACTCCAGCAGATTAAAGCCAGCCATCTCCAGTGCTCTATCCAAGGACGGTATCTACAC +TATCGCAAACTAG +>YAL067W-A CDS=1-228 +ATGCCAATTATAGGGGTGCCGAGGTGCCTTATAAAACCCTTTTCTGTGCCTGTGACATTTCCTTTTTCGG +TCAAAAAGAATATCCGAATTTTAGATTTGGACCCTCGTACAGAAGCTTATTGTCTAAGCCTGAATTCAGT +CTGCTTTAAACGGCTTCCGCGGAGGAAATATTTCCATCTCTTGAATTCGTACAACATTAAACGTGTGTTG +GGAGTCGTATACTGTTAG diff --git a/src/samtools/samtools_faidx/test_data/test.fasta.gz b/src/samtools/samtools_faidx/test_data/test.fasta.gz new file mode 100644 index 00000000..d21edc57 Binary files /dev/null and b/src/samtools/samtools_faidx/test_data/test.fasta.gz differ diff --git a/src/samtools/samtools_faidx/test_data/test.fastq b/src/samtools/samtools_faidx/test_data/test.fastq new file mode 100644 index 00000000..b8f8c917 --- /dev/null +++ b/src/samtools/samtools_faidx/test_data/test.fastq @@ -0,0 +1,14 @@ +@fastq1 +ATGCATGCATGCATGCATGCATGCATGCAT +GCATGCATGCATGCATGCATGCATGCATGC +ATGCAT ++ +FFFA@@FFFFFFFFFFHHB:::@BFFFFGG +HIHIIIIIIIIIIIIIIIIIIIIIIIFFFF +8011<< +@fastq2 +ATGCATGCATGCAT +GCATGCATGCATGC ++ +IIA94445EEII== +=>IIIIIIIIICCC \ No newline at end of file diff --git a/src/samtools/samtools_faidx/test_data/test.regions b/src/samtools/samtools_faidx/test_data/test.regions new file mode 100644 index 00000000..2034aaf0 --- /dev/null +++ b/src/samtools/samtools_faidx/test_data/test.regions @@ -0,0 +1,4 @@ +YAL069W:300-315 +YAL068C:200-230 +YAL067W-A:115-145 +chr1:1-100 diff --git a/src/samtools/samtools_fastq/config.vsh.yaml b/src/samtools/samtools_fastq/config.vsh.yaml new file mode 100644 index 00000000..39e926f0 --- /dev/null +++ b/src/samtools/samtools_fastq/config.vsh.yaml @@ -0,0 +1,189 @@ +name: samtools_fastq +namespace: samtools +description: Converts a SAM, BAM or CRAM to FASTQ format. +keywords: [fastq, bam, sam, cram] +links: + homepage: https://www.htslib.org/ + documentation: https://www.htslib.org/doc/samtools-fastq.html + repository: https://github.com/samtools/samtools +references: + doi: [10.1093/bioinformatics/btp352, 10.1093/gigascience/giab008] +license: MIT/Expat + +argument_groups: + - name: Inputs + arguments: + - name: --input + type: file + description: input SAM/BAM/CRAM file + required: true + - name: Outputs + arguments: + - name: --output + type: file + description: output FASTQ file + required: true + direction: output + - name: Options + arguments: + - name: --no_suffix + alternatives: -n + type: boolean_true + description: | + By default, either '/1' or '/2' is added to the end of read names where the corresponding + READ1 or READ2 FLAG bit is set. Using -n causes read names to be left as they are. + - name: --suffix + alternatives: -N + type: boolean_true + description: | + Always add either '/1' or '/2' to the end of read names even when put into different files. + - name: --use_oq + alternatives: -O + type: boolean_true + description: | + Use quality values from OQ tags in preference to standard quality string if available. + - name: --singleton + alternatives: -s + type: file + description: write singleton reads to FILE. + - name: --copy_tags + alternatives: -t + type: boolean_true + description: | + Copy RG, BC and QT tags to the FASTQ header line, if they exist. + - name: --copy_tags_list + alternatives: -T + type: string + description: | + Specify a comma-separated list of tags to copy to the FASTQ header line, if they exist. + TAGLIST can be blank or * to indicate all tags should be copied to the output. If using *, + be careful to quote it to avoid unwanted shell expansion. + - name: --read1 + alternatives: -1 + type: file + description: | + Write reads with the READ1 FLAG set (and READ2 not set) to FILE instead of outputting them. + If the -s option is used, only paired reads will be written to this file. + direction: output + - name: --read2 + alternatives: -2 + type: file + description: | + Write reads with the READ2 FLAG set (and READ1 not set) to FILE instead of outputting them. + If the -s option is used, only paired reads will be written to this file. + direction: output + - name: --output_reads + alternatives: -o + type: file + description: | + Write reads with either READ1 FLAG or READ2 flag set to FILE instead of outputting them to stdout. + This is equivalent to -1 FILE -2 FILE. + direction: output + - name: --output_reads_both + alternatives: -0 + type: file + description: | + Write reads where the READ1 and READ2 FLAG bits set are either both set or both unset to FILE + instead of outputting them. + direction: output + - name: --filter_flags + alternatives: -f + type: integer + description: | + Only output alignments with all bits set in INT present in the FLAG field. INT can be specified + in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/) or in octal by beginning with `0' + (i.e. /^0[0-7]+/). + default: 0 + - name: --excl_flags + alternatives: -F + type: string + description: | + Do not output alignments with any bits set in INT present in the FLAG field. INT can be specified + in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/) or in octal by beginning with `0' + (i.e. /^0[0-7]+/). This defaults to 0x900 representing filtering of secondary and + supplementary alignments. + default: 0x900 + - name: --incl_flags + alternatives: --rf + type: string + description: | + Only output alignments with any bits set in INT present in the FLAG field. INT can be specified + in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0' + (i.e. /^0[0-7]+/), as a decimal number not beginning with '0' or as a comma-separated list of + flag names. + default: 0 + - name: --excl_flags_all + alternatives: -G + type: integer + description: | + Only EXCLUDE reads with all of the bits set in INT present in the FLAG field. INT can be specified + in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/) or in octal by beginning with `0' + (i.e. /^0[0-7]+/). + default: 0 + - name: --aux_tag + alternatives: -d + type: string + description: | + Only output alignments containing an auxiliary tag matching both TAG and VAL. If VAL is omitted + then any value is accepted. The tag types supported are i, f, Z, A and H. "B" arrays are not + supported. This is comparable to the method used in samtools view --tag. The option may be specified + multiple times and is equivalent to using the --aux_tag_file option. + - name: --aux_tag_file + alternatives: -D + type: string + description: | + Only output alignments containing an auxiliary tag matching TAG and having a value listed in FILE. + The format of the file is one line per value. This is equivalent to specifying --aux_tag multiple times. + - name: --casava + alternatives: -i + type: boolean_true + description: add Illumina Casava 1.8 format entry to header (eg 1:N:0:ATCACG) + - name: --compression + alternatives: -c + type: integer + description: set compression level when writing gz or bgzf fastq files. + default: 0 + - name: --index1 + alternatives: --i1 + type: file + description: write first index reads to FILE. + - name: --index2 + alternatives: --i2 + type: file + description: write second index reads to FILE. + - name: --barcode_tag + type: string + description: Auxiliary tag to find index reads in. + default: BC + - name: --quality_tag + type: string + description: Auxiliary tag to find index quality in. + default: QT + - name: --index_format + type: string + description: | + string to describe how to parse the barcode and quality tags. For example: + [i14i8]: the first 14 characters are index 1, the next 8 characters are index 2. + [n8i14]: ignore the first 8 characters, and use the next 14 characters for index 1. + If the tag contains a separator, then the numeric part can be replaced with '*' to mean + 'read until the separator or end of tag', for example: [n*i*]. + +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 + setup: + - type: docker + run: | + samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ + sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt +runners: +- type: executable +- type: nextflow diff --git a/src/samtools/samtools_fastq/help.txt b/src/samtools/samtools_fastq/help.txt new file mode 100644 index 00000000..39ed0d00 --- /dev/null +++ b/src/samtools/samtools_fastq/help.txt @@ -0,0 +1,80 @@ +``` +samtools fastq +``` + +Usage: samtools fastq [options...] + +Description: +Converts a SAM, BAM or CRAM to FASTQ format. + +Options: + -0 FILE write reads designated READ_OTHER to FILE + -1 FILE write reads designated READ1 to FILE + -2 FILE write reads designated READ2 to FILE + -o FILE write reads designated READ1 or READ2 to FILE + note: if a singleton file is specified with -s, only + paired reads will be written to the -1 and -2 files. + -d, --tag TAG[:VAL] + only include reads containing TAG, optionally with value VAL + -f, --require-flags INT + only include reads with all of the FLAGs in INT present [0] + -F, --excl[ude]-flags INT + only include reads with none of the FLAGs in INT present [0x900] + --rf, --incl[ude]-flags INT + only include reads with any of the FLAGs in INT present [0] + -G INT only EXCLUDE reads with all of the FLAGs in INT present [0] + -n don't append /1 and /2 to the read name + -N always append /1 and /2 to the read name + -O output quality in the OQ tag if present + -s FILE write singleton reads designated READ1 or READ2 to FILE + -t copy RG, BC and QT tags to the FASTQ header line + -T TAGLIST copy arbitrary tags to the FASTQ header line, '*' for all + -v INT default quality score if not given in file [1] + -i add Illumina Casava 1.8 format entry to header (eg 1:N:0:ATCACG) + -c INT compression level [0..9] to use when writing bgzf files [1] + --i1 FILE write first index reads to FILE + --i2 FILE write second index reads to FILE + --barcode-tag TAG + Barcode tag [BC] + --quality-tag TAG + Quality tag [QT] + --index-format STR + How to parse barcode and quality tags + + --input-fmt-option OPT[=VAL] + Specify a single input file format option in the form + of OPTION or OPTION=VALUE + --reference FILE + Reference sequence FASTA FILE [null] + -@, --threads INT + Number of additional threads to use [0] + --verbosity INT + Set level of verbosity + +The files will be automatically compressed if the file names have a .gz +or .bgzf extension. The input to this program must be collated by name. +Run 'samtools collate' or 'samtools sort -n' to achieve this. + +Reads are designated READ1 if FLAG READ1 is set and READ2 is not set. +Reads are designated READ2 if FLAG READ1 is not set and READ2 is set. +Otherwise reads are designated READ_OTHER (both flags set or both flags unset). +Run 'samtools flags' for more information on flag codes and meanings. + +The index-format string describes how to parse the barcode and quality tags. +It is made up of 'i' or 'n' followed by a length or '*'. For example: + i14i8 The first 14 characters are index 1, the next 8 are index 2 + n8i14 Ignore the first 8 characters, and use the next 14 for index 1 + +If the tag contains a separator, then the numeric part can be replaced with +'*' to mean 'read until the separator or end of tag', for example: + i*i* Break the tag at the separator into index 1 and index 2 + n*i* Ignore the left part of the tag until the separator, + then use the second part of the tag as index 1 + +Examples: +To get just the paired reads in separate files, use: + samtools fastq -1 pair1.fq -2 pair2.fq -0 /dev/null -s /dev/null -n in.bam + +To get all non-supplementary/secondary reads in a single file, redirect +the output: + samtools fastq in.bam > all_reads.fq \ No newline at end of file diff --git a/src/samtools/samtools_fastq/script.sh b/src/samtools/samtools_fastq/script.sh new file mode 100644 index 00000000..367432f9 --- /dev/null +++ b/src/samtools/samtools_fastq/script.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +set -e + +[[ "$par_no_suffix" == "false" ]] && unset par_no_suffix +[[ "$par_suffix" == "false" ]] && unset par_suffix +[[ "$par_use_oq" == "false" ]] && unset par_use_oq +[[ "$par_copy_tags" == "false" ]] && unset par_copy_tags +[[ "$par_casava" == "false" ]] && unset par_casava + +samtools fastq \ + ${par_no_suffix:+-n} \ + ${par_suffix:+-N} \ + ${par_use_oq:+-O} \ + ${par_singleton:+-s "$par_singleton"} \ + ${par_copy_tags:+-t} \ + ${par_copy_tags_list:+-T "$par_copy_tags_list"} \ + ${par_read1:+-1 "$par_read1"} \ + ${par_read2:+-2 "$par_read2"} \ + ${par_output_reads:+-o "$par_output_reads"} \ + ${par_output_reads_both:+-0 "$par_output_reads_both"} \ + ${par_filter_flags:+-f "$par_filter_flags"} \ + ${par_excl_flags:+-F "$par_excl_flags"} \ + ${par_incl_flags:+--rf "$par_incl_flags"} \ + ${par_excl_flags_all:+-G "$par_excl_flags_all"} \ + ${par_aux_tag:+-d "$par_aux_tag"} \ + ${par_aux_tag_file:+-D "$par_aux_tag_file"} \ + ${par_casava:+-i} \ + ${par_compression:+-c "$par_compression"} \ + ${par_index1:+--i1 "$par_index1"} \ + ${par_index2:+--i2 "$par_index2"} \ + ${par_barcode_tag:+--barcode-tag "$par_barcode_tag"} \ + ${par_quality_tag:+--quality-tag "$par_quality_tag"} \ + ${par_index_format:+--index-format "$par_index_format"} \ + "$par_input" \ + > "$par_output" + diff --git a/src/samtools/samtools_fastq/test.sh b/src/samtools/samtools_fastq/test.sh new file mode 100644 index 00000000..32ee3f5e --- /dev/null +++ b/src/samtools/samtools_fastq/test.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +test_dir="${meta_resources_dir}/test_data" +out_dir="${meta_resources_dir}/out_data" + +############################################################################################ + +echo ">>> Test 1: Convert all reads from a bam file to fastq format" +"$meta_executable" \ + --input "$test_dir/a.bam" \ + --output "$out_dir/a.fq" + +echo ">>> Check if output file exists" +[ ! -f "$out_dir/a.fq" ] && echo "Output file a.fq does not exist" && exit 1 + +echo ">>> Check if output is empty" +[ ! -s "$out_dir/a.fq" ] && echo "Output file a.fq is empty" && exit 1 + +echo ">>> Check if output matches expected output" +diff "$out_dir/a.fq" "$test_dir/a.fq" || + (echo "Output file a.fq does not match expected output" && exit 1) + +rm "$out_dir/a.fq" + +############################################################################################ + +echo ">>> Test 2: Convert all reads from a sam file to fastq format" +"$meta_executable" \ + --input "$test_dir/a.sam" \ + --output "$out_dir/a.fq" + +echo ">>> Check if output file exists" +[ ! -f "$out_dir/a.fq" ] && echo "Output file a.fq does not exist" && exit 1 + +echo ">>> Check if output is empty" +[ ! -s "$out_dir/a.fq" ] && echo "Output file a.fq is empty" && exit 1 + +echo ">>> Check if output matches expected output" +diff "$out_dir/a.fq" "$test_dir/a.fq" || + (echo "Output file a.fq does not match expected output" && exit 1) + +rm "$out_dir/a.fq" + +############################################################################################ + +echo ">>> Test 3: Output reads from bam file to separate files" + +"$meta_executable" \ + --input "$test_dir/a.bam" \ + --read1 "$out_dir/a.1.fq" \ + --read2 "$out_dir/a.2.fq" \ + --output "$out_dir/a.fq" + +echo ">>> Check if output files exist" +[ ! -f "$out_dir/a.1.fq" ] && echo "Output file a.1.fq does not exist" && exit 1 +[ ! -f "$out_dir/a.2.fq" ] && echo "Output file a.2.fq does not exist" && exit 1 +[ ! -f "$out_dir/a.fq" ] && echo "Output file a.fq does not exist" && exit 1 + +echo ">>> Check if output files are empty" +[ ! -s "$out_dir/a.1.fq" ] && echo "Output file a.1.fq is empty" && exit 1 +[ ! -s "$out_dir/a.2.fq" ] && echo "Output file a.2.fq is empty" && exit 1 +# output should be empty since input has no singleton reads + +echo ">>> Check if output files match expected output" +diff "$out_dir/a.1.fq" "$test_dir/a.1.fq" || + (echo "Output file a.1.fq does not match expected output" && exit 1) +diff "$out_dir/a.2.fq" "$test_dir/a.2.fq" || + (echo "Output file a.2.fq does not match expected output" && exit 1) + +rm "$out_dir/a.1.fq" "$out_dir/a.2.fq" "$out_dir/a.fq" + +############################################################################################ + +echo ">>> Test 4: Output only forward reads from bam file to fastq format" + +"$meta_executable" \ + --input "$test_dir/a.sam" \ + --excl_flags "0x80" \ + --output "$out_dir/half.fq" + +echo ">>> Check if output file exists" +[ ! -f "$out_dir/half.fq" ] && echo "Output file half.fq does not exist" && exit 1 + +echo ">>> Check if output is empty" +[ ! -s "$out_dir/half.fq" ] && echo "Output file half.fq is empty" && exit 1 + +echo ">>> Check if output matches expected output" +diff "$out_dir/half.fq" "$test_dir/half.fq" || + (echo "Output file half.fq does not match expected output" && exit 1) + +rm "$out_dir/half.fq" + +############################################################################################ + +echo "All tests succeeded!" +exit 0 \ No newline at end of file diff --git a/src/samtools/samtools_fastq/test_data/a.1.fq b/src/samtools/samtools_fastq/test_data/a.1.fq new file mode 100644 index 00000000..03eaa725 --- /dev/null +++ b/src/samtools/samtools_fastq/test_data/a.1.fq @@ -0,0 +1,12 @@ +@a1 +AAAAAAAAAA ++ +********** +@b1 +AAAAAAAAAA ++ +********** +@c1 +AAAAAAAAAA ++ +********** diff --git a/src/samtools/samtools_fastq/test_data/a.2.fq b/src/samtools/samtools_fastq/test_data/a.2.fq new file mode 100644 index 00000000..03eaa725 --- /dev/null +++ b/src/samtools/samtools_fastq/test_data/a.2.fq @@ -0,0 +1,12 @@ +@a1 +AAAAAAAAAA ++ +********** +@b1 +AAAAAAAAAA ++ +********** +@c1 +AAAAAAAAAA ++ +********** diff --git a/src/samtools/samtools_fastq/test_data/a.bam b/src/samtools/samtools_fastq/test_data/a.bam new file mode 100644 index 00000000..dba1268a Binary files /dev/null and b/src/samtools/samtools_fastq/test_data/a.bam differ diff --git a/src/samtools/samtools_fastq/test_data/a.fq b/src/samtools/samtools_fastq/test_data/a.fq new file mode 100644 index 00000000..d12c62ca --- /dev/null +++ b/src/samtools/samtools_fastq/test_data/a.fq @@ -0,0 +1,24 @@ +@a1/1 +AAAAAAAAAA ++ +********** +@b1/1 +AAAAAAAAAA ++ +********** +@c1/1 +AAAAAAAAAA ++ +********** +@a1/2 +AAAAAAAAAA ++ +********** +@b1/2 +AAAAAAAAAA ++ +********** +@c1/2 +AAAAAAAAAA ++ +********** diff --git a/src/samtools/samtools_fastq/test_data/a.sam b/src/samtools/samtools_fastq/test_data/a.sam new file mode 100644 index 00000000..aa8c77b3 --- /dev/null +++ b/src/samtools/samtools_fastq/test_data/a.sam @@ -0,0 +1,7 @@ +@SQ SN:xx LN:20 +a1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +b1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +c1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +a1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** +b1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** +c1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** diff --git a/src/samtools/samtools_fastq/test_data/half.fq b/src/samtools/samtools_fastq/test_data/half.fq new file mode 100644 index 00000000..85a2b1c4 --- /dev/null +++ b/src/samtools/samtools_fastq/test_data/half.fq @@ -0,0 +1,12 @@ +@a1/1 +AAAAAAAAAA ++ +********** +@b1/1 +AAAAAAAAAA ++ +********** +@c1/1 +AAAAAAAAAA ++ +********** diff --git a/src/samtools/samtools_fastq/test_data/script.sh b/src/samtools/samtools_fastq/test_data/script.sh new file mode 100755 index 00000000..b59bc1bd --- /dev/null +++ b/src/samtools/samtools_fastq/test_data/script.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# dowload test data from snakemake wrapper +if [ ! -d /tmp/fastq_source ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers.git /tmp/fastq_source +fi + +cp -r /tmp/fastq_source/bio/samtools/fastx/test/*.sam src/samtools/samtools_fastq/test_data/ +cp -r /tmp/fastq_source/bio/samtools/fastq/interleaved/test/mapped/*.bam src/samtools/samtools_fastq/test_data/ +cp -r /tmp/fastq_source/bio/samtools/fastq/interleaved/test/reads/*.fq src/samtools/samtools_fastq/test_data/ +cp -r /tmp/fastq_source/bio/samtools/fastq/separate/test/reads/*.fq src/samtools/samtools_fastq/test_data/ \ No newline at end of file diff --git a/src/samtools/samtools_flagstat/config.vsh.yaml b/src/samtools/samtools_flagstat/config.vsh.yaml new file mode 100644 index 00000000..9b4dfbe1 --- /dev/null +++ b/src/samtools/samtools_flagstat/config.vsh.yaml @@ -0,0 +1,52 @@ +name: samtools_flagstat +namespace: samtools +description: Counts the number of alignments in SAM/BAM/CRAM files for each FLAG type. +keywords: [ stats, mapping, counts, bam, sam, cram ] +links: + homepage: https://www.htslib.org/ + documentation: https://www.htslib.org/doc/samtools-flagstat.html + repository: https://github.com/samtools/samtools +references: + doi: [10.1093/bioinformatics/btp352, 10.1093/gigascience/giab008] +license: MIT/Expat + +argument_groups: + - name: Inputs + arguments: + - name: --bam + type: file + description: | + BAM input files. + - name: --bai + type: file + description: | + BAM index file. + - name: Outputs + arguments: + - name: --output + type: file + description: | + File containing samtools stats output. + direction: output + required: true + example: output.flagstat + +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 + setup: + - type: docker + run: | + samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ + sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt +runners: +- type: executable +- type: nextflow \ No newline at end of file diff --git a/src/samtools/samtools_flagstat/help.txt b/src/samtools/samtools_flagstat/help.txt new file mode 100644 index 00000000..fe54d20c --- /dev/null +++ b/src/samtools/samtools_flagstat/help.txt @@ -0,0 +1,13 @@ +```sh +samtools flagstat --help +``` +Usage: samtools flagstat [options] + --input-fmt-option OPT[=VAL] + Specify a single input file format option in the form + of OPTION or OPTION=VALUE + -@, --threads INT + Number of additional threads to use [0] + --verbosity INT + Set level of verbosity + -O, --output-fmt FORMAT[,OPT[=VAL]]... + Specify output format (json, tsv) \ No newline at end of file diff --git a/src/samtools/samtools_flagstat/script.sh b/src/samtools/samtools_flagstat/script.sh new file mode 100644 index 00000000..beac3703 --- /dev/null +++ b/src/samtools/samtools_flagstat/script.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +set -e + +samtools flagstat \ + "$par_bam" \ + > "$par_output" + \ No newline at end of file diff --git a/src/samtools/samtools_flagstat/test.sh b/src/samtools/samtools_flagstat/test.sh new file mode 100644 index 00000000..647c64af --- /dev/null +++ b/src/samtools/samtools_flagstat/test.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +test_dir="${meta_resources_dir}/test_data" +echo ">>> Testing $meta_functionality_name" + +"$meta_executable" \ + --bam "$test_dir/a.bam" \ + --bai "$test_dir/a.bam.bai" \ + --output "$test_dir/a.flagstat" + +echo ">>> Checking whether output exists" +[ ! -f "$test_dir/a.flagstat" ] && echo "File 'a.flagstat' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/a.flagstat" ] && echo "File 'a.flagstat' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff "$test_dir/a.flagstat" "$test_dir/a_ref.flagstat" || \ + (echo "Output file a.flagstat does not match expected output" && exit 1) + +rm "$test_dir/a.flagstat" + +############################################################################################ + +echo ">>> Testing $meta_functionality_name with singletons in the input" + +"$meta_executable" \ + --bam "$test_dir/test.paired_end.sorted.bam" \ + --bai "$test_dir/test.paired_end.sorted.bam.bai" \ + --output "$test_dir/test.paired_end.sorted.flagstat" + +echo ">>> Checking whether output exists" +[ ! -f "$test_dir/test.paired_end.sorted.flagstat" ] && echo "File 'test.paired_end.sorted.flagstat' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/test.paired_end.sorted.flagstat" ] && echo "File 'test.paired_end.sorted.flagstat' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff "$test_dir/test.paired_end.sorted.flagstat" "$test_dir/test_ref.paired_end.sorted.flagstat" || \ + (echo "Output file test.paired_end.sorted.flagstat does not match expected output" && exit 1) + +rm "$test_dir/test.paired_end.sorted.flagstat" + + + +echo "All tests succeeded!" +exit 0 \ No newline at end of file diff --git a/src/samtools/samtools_flagstat/test_data/a.bam b/src/samtools/samtools_flagstat/test_data/a.bam new file mode 100644 index 00000000..dba1268a Binary files /dev/null and b/src/samtools/samtools_flagstat/test_data/a.bam differ diff --git a/src/samtools/samtools_flagstat/test_data/a.bam.bai b/src/samtools/samtools_flagstat/test_data/a.bam.bai new file mode 100644 index 00000000..12f5f510 Binary files /dev/null and b/src/samtools/samtools_flagstat/test_data/a.bam.bai differ diff --git a/src/samtools/samtools_flagstat/test_data/a_ref.flagstat b/src/samtools/samtools_flagstat/test_data/a_ref.flagstat new file mode 100644 index 00000000..5d8b9a73 --- /dev/null +++ b/src/samtools/samtools_flagstat/test_data/a_ref.flagstat @@ -0,0 +1,16 @@ +6 + 0 in total (QC-passed reads + QC-failed reads) +6 + 0 primary +0 + 0 secondary +0 + 0 supplementary +0 + 0 duplicates +0 + 0 primary duplicates +6 + 0 mapped (100.00% : N/A) +6 + 0 primary mapped (100.00% : N/A) +6 + 0 paired in sequencing +3 + 0 read1 +3 + 0 read2 +6 + 0 properly paired (100.00% : N/A) +6 + 0 with itself and mate mapped +0 + 0 singletons (0.00% : N/A) +0 + 0 with mate mapped to a different chr +0 + 0 with mate mapped to a different chr (mapQ>=5) diff --git a/src/samtools/samtools_flagstat/test_data/script.sh b/src/samtools/samtools_flagstat/test_data/script.sh new file mode 100755 index 00000000..fc32b48e --- /dev/null +++ b/src/samtools/samtools_flagstat/test_data/script.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Download test data from snakemake wrapper + +wget https://raw.githubusercontent.com/snakemake/snakemake-wrappers/3a4f7004281efc176fd9af732ad88d00c47d432d/bio/samtools/flagstat/test/mapped/a.bam +samtools index a.bam +# samtools flagstat a.bam > a_ref.flagstat + + +# Download test data from nf-core module + +wget https://github.com/nf-core/test-datasets/raw/modules/data/genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam +wget https://github.com/nf-core/test-datasets/raw/modules/data/genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai +# samtools flagstat test.paired_end.sorted.bam > test_ref.paired_end.sorted.flagstat \ No newline at end of file diff --git a/src/samtools/samtools_flagstat/test_data/test.paired_end.sorted.bam b/src/samtools/samtools_flagstat/test_data/test.paired_end.sorted.bam new file mode 100644 index 00000000..85cccf14 Binary files /dev/null and b/src/samtools/samtools_flagstat/test_data/test.paired_end.sorted.bam differ diff --git a/src/samtools/samtools_flagstat/test_data/test.paired_end.sorted.bam.bai b/src/samtools/samtools_flagstat/test_data/test.paired_end.sorted.bam.bai new file mode 100644 index 00000000..0c6d5a96 Binary files /dev/null and b/src/samtools/samtools_flagstat/test_data/test.paired_end.sorted.bam.bai differ diff --git a/src/samtools/samtools_flagstat/test_data/test_ref.paired_end.sorted.flagstat b/src/samtools/samtools_flagstat/test_data/test_ref.paired_end.sorted.flagstat new file mode 100644 index 00000000..4028563d --- /dev/null +++ b/src/samtools/samtools_flagstat/test_data/test_ref.paired_end.sorted.flagstat @@ -0,0 +1,16 @@ +200 + 0 in total (QC-passed reads + QC-failed reads) +200 + 0 primary +0 + 0 secondary +0 + 0 supplementary +0 + 0 duplicates +0 + 0 primary duplicates +197 + 0 mapped (98.50% : N/A) +197 + 0 primary mapped (98.50% : N/A) +200 + 0 paired in sequencing +100 + 0 read1 +100 + 0 read2 +192 + 0 properly paired (96.00% : N/A) +194 + 0 with itself and mate mapped +3 + 0 singletons (1.50% : N/A) +0 + 0 with mate mapped to a different chr +0 + 0 with mate mapped to a different chr (mapQ>=5) diff --git a/src/samtools/samtools_idxstats/config.vsh.yaml b/src/samtools/samtools_idxstats/config.vsh.yaml new file mode 100644 index 00000000..30f21348 --- /dev/null +++ b/src/samtools/samtools_idxstats/config.vsh.yaml @@ -0,0 +1,53 @@ +name: samtools_idxstats +namespace: samtools +description: Reports alignment summary statistics for a BAM file. +keywords: [stats, mapping, counts, chromosome, bam, sam, cram] +links: + homepage: https://www.htslib.org/ + documentation: https://www.htslib.org/doc/samtools-idxstats.html + repository: https://github.com/samtools/samtools +references: + doi: [10.1093/bioinformatics/btp352, 10.1093/gigascience/giab008] +license: MIT/Expat + +argument_groups: + - name: Inputs + arguments: + - name: "--bam" + type: file + description: BAM input file. + - name: "--bai" + type: file + description: BAM index file. + - name: "--fasta" + type: file + description: Reference file the CRAM was created with (optional). + - name: Outputs + arguments: + - name: "--output" + type: file + description: | + File containing samtools stats output in tab-delimited format. + required: true + direction: output + example: output.idxstats + +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 + setup: + - type: docker + run: | + samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ + sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt +runners: +- type: executable +- type: nextflow \ No newline at end of file diff --git a/src/samtools/samtools_idxstats/help.txt b/src/samtools/samtools_idxstats/help.txt new file mode 100644 index 00000000..7b63ab17 --- /dev/null +++ b/src/samtools/samtools_idxstats/help.txt @@ -0,0 +1,12 @@ +``` +samtools idxstats +``` + +Usage: samtools idxstats [options] + --input-fmt-option OPT[=VAL] + Specify a single input file format option in the form + of OPTION or OPTION=VALUE + -@, --threads INT + Number of additional threads to use [0] + --verbosity INT + Set level of verbosity \ No newline at end of file diff --git a/src/samtools/samtools_idxstats/script.sh b/src/samtools/samtools_idxstats/script.sh new file mode 100644 index 00000000..9f8d4af4 --- /dev/null +++ b/src/samtools/samtools_idxstats/script.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +set -e + +samtools idxstats "$par_bam" > "$par_output" \ No newline at end of file diff --git a/src/samtools/samtools_idxstats/test.sh b/src/samtools/samtools_idxstats/test.sh new file mode 100644 index 00000000..1459ec08 --- /dev/null +++ b/src/samtools/samtools_idxstats/test.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +test_dir="${meta_resources_dir}/test_data" +echo ">>> Testing $meta_functionality_name" + +"$meta_executable" \ + --bam "$test_dir/a.sorted.bam" \ + --bai "$test_dir/a.sorted.bam.bai" \ + --output "$test_dir/a.sorted.idxstats" + +echo ">>> Checking whether output exists" +[ ! -f "$test_dir/a.sorted.idxstats" ] && echo "File 'a.sorted.idxstats' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/a.sorted.idxstats" ] && echo "File 'a.sorted.idxstats' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff "$test_dir/a.sorted.idxstats" "$test_dir/a_ref.sorted.idxstats" || \ + (echo "Output file a.sorted.idxstats does not match expected output" && exit 1) + +rm "$test_dir/a.sorted.idxstats" + +############################################################################################ + +echo ">>> Testing $meta_functionality_name with singletons in the input" + +"$meta_executable" \ + --bam "$test_dir/test.paired_end.sorted.bam" \ + --bai "$test_dir/test.paired_end.sorted.bam.bai" \ + --output "$test_dir/test.paired_end.sorted.idxstats" + +echo ">>> Checking whether output exists" +[ ! -f "$test_dir/test.paired_end.sorted.idxstats" ] && \ + echo "File 'test.paired_end.sorted.idxstats' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/test.paired_end.sorted.idxstats" ] && \ + echo "File 'test.paired_end.sorted.idxstats' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff "$test_dir/test.paired_end.sorted.idxstats" "$test_dir/test_ref.paired_end.sorted.idxstats" || \ + (echo "Output file test.paired_end.sorted.idxstats does not match expected output" && exit 1) + +rm "$test_dir/test.paired_end.sorted.idxstats" + +############################################################################################ + +echo "All tests succeeded!" +exit 0 \ No newline at end of file diff --git a/src/samtools/samtools_idxstats/test_data/a.sorted.bam b/src/samtools/samtools_idxstats/test_data/a.sorted.bam new file mode 100644 index 00000000..1c81d7cd Binary files /dev/null and b/src/samtools/samtools_idxstats/test_data/a.sorted.bam differ diff --git a/src/samtools/samtools_idxstats/test_data/a.sorted.bam.bai b/src/samtools/samtools_idxstats/test_data/a.sorted.bam.bai new file mode 100644 index 00000000..4f08f5d5 Binary files /dev/null and b/src/samtools/samtools_idxstats/test_data/a.sorted.bam.bai differ diff --git a/src/samtools/samtools_idxstats/test_data/a_ref.sorted.idxstats b/src/samtools/samtools_idxstats/test_data/a_ref.sorted.idxstats new file mode 100644 index 00000000..27862419 --- /dev/null +++ b/src/samtools/samtools_idxstats/test_data/a_ref.sorted.idxstats @@ -0,0 +1,2 @@ +xx 20 6 0 +* 0 0 0 diff --git a/src/samtools/samtools_idxstats/test_data/script.sh b/src/samtools/samtools_idxstats/test_data/script.sh new file mode 100755 index 00000000..0e28a4c6 --- /dev/null +++ b/src/samtools/samtools_idxstats/test_data/script.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# dowload test data from snakemake wrapper +if [ ! -d /tmp/idxstats_source ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers.git /tmp/idxstats_source +fi + +cp -r /tmp/idxstats_source/bio/samtools/idxstats/test/mapped/* src/samtools/idxstats/test_data +# samtools idxstats a.sorted.bam > a.sorted.idxstats + +# dowload test data from nf-core module +wget https://github.com/nf-core/test-datasets/raw/modules/data/genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam +wget https://github.com/nf-core/test-datasets/raw/modules/data/genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai +# samtools idxstats test.paired_end.sorted.bam > test_ref.paired_end.sorted.idxstats \ No newline at end of file diff --git a/src/samtools/samtools_idxstats/test_data/test.paired_end.sorted.bam b/src/samtools/samtools_idxstats/test_data/test.paired_end.sorted.bam new file mode 100644 index 00000000..85cccf14 Binary files /dev/null and b/src/samtools/samtools_idxstats/test_data/test.paired_end.sorted.bam differ diff --git a/src/samtools/samtools_idxstats/test_data/test.paired_end.sorted.bam.bai b/src/samtools/samtools_idxstats/test_data/test.paired_end.sorted.bam.bai new file mode 100644 index 00000000..0c6d5a96 Binary files /dev/null and b/src/samtools/samtools_idxstats/test_data/test.paired_end.sorted.bam.bai differ diff --git a/src/samtools/samtools_idxstats/test_data/test_ref.paired_end.sorted.idxstats b/src/samtools/samtools_idxstats/test_data/test_ref.paired_end.sorted.idxstats new file mode 100644 index 00000000..77bc11b8 --- /dev/null +++ b/src/samtools/samtools_idxstats/test_data/test_ref.paired_end.sorted.idxstats @@ -0,0 +1,2 @@ +MT192765.1 29829 197 3 +* 0 0 0 diff --git a/src/samtools/samtools_index/config.vsh.yaml b/src/samtools/samtools_index/config.vsh.yaml new file mode 100644 index 00000000..8c59a20e --- /dev/null +++ b/src/samtools/samtools_index/config.vsh.yaml @@ -0,0 +1,67 @@ +name: samtools_index +namespace: samtools +description: Index SAM/BAM/CRAM files. +keywords: [index, bam, sam, cram] +links: + homepage: https://www.htslib.org/ + documentation: https://www.htslib.org/doc/samtools-index.html + repository: https://github.com/samtools/samtools +references: + doi: [10.1093/bioinformatics/btp352, 10.1093/gigascience/giab008] +license: MIT/Expat + +argument_groups: + - name: Inputs + arguments: + - name: --input + type: file + description: Input file name + required: true + must_exist: true + - name: Outputs + arguments: + - name: --output + alternatives: -o + type: file + description: Output file name + required: true + direction: output + example: out.bam.bai + - name: Options + arguments: + - name: --bai + alternatives: -b + type: boolean_true + description: Generate BAM index + - name: --csi + alternatives: -c + type: boolean_true + description: | + Create a CSI index for BAM files instead of the traditional BAI + index. This will be required for genomes with larger chromosome + sizes. + - name: --min_shift + alternatives: -m + type: integer + description: | + Create a CSI index, with a minimum interval size of 2^INT. + +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 + setup: + - type: docker + run: | + samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ + sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt +runners: +- type: executable +- type: nextflow \ No newline at end of file diff --git a/src/samtools/samtools_index/help.txt b/src/samtools/samtools_index/help.txt new file mode 100644 index 00000000..fdf0d12d --- /dev/null +++ b/src/samtools/samtools_index/help.txt @@ -0,0 +1,13 @@ +``` +samtools index +``` + +Usage: samtools index -M [-bc] [-m INT] ... + or: samtools index [-bc] [-m INT] [out.index] +Options: + -b, --bai Generate BAI-format index for BAM files [default] + -c, --csi Generate CSI-format index for BAM files + -m, --min-shift INT Set minimum interval size for CSI indices to 2^INT [14] + -M Interpret all filename arguments as files to be indexed + -o, --output FILE Write index to FILE [alternative to in args] + -@, --threads INT Sets the number of threads [none] \ No newline at end of file diff --git a/src/samtools/samtools_index/script.sh b/src/samtools/samtools_index/script.sh new file mode 100644 index 00000000..9db47fa4 --- /dev/null +++ b/src/samtools/samtools_index/script.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +set -e +[[ "$par_multiple" == "false" ]] && unset par_multiple +[[ "$par_bai" == "false" ]] && unset par_bai +[[ "$par_csi" == "false" ]] && unset par_csi +[[ "$par_multiple" == "false" ]] && unset par_multiple + +samtools index \ + "$par_input" \ + ${par_csi:+-c} \ + ${par_bai:+-b} \ + ${par_min_shift:+-m "par_min_shift"} \ + ${par_multiple:+-M} \ + -o "$par_output" \ No newline at end of file diff --git a/src/samtools/samtools_index/test.sh b/src/samtools/samtools_index/test.sh new file mode 100644 index 00000000..f4b1d3b6 --- /dev/null +++ b/src/samtools/samtools_index/test.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +test_dir="${meta_resources_dir}/test_data" + +echo ">>> Testing $meta_functionality_name" + +echo ">>> Generating BAM index" +"$meta_executable" \ + --input "$test_dir/a.sorted.bam" \ + --bai \ + --output "$test_dir/a.sorted.bam.bai" + +echo ">>> Check whether output exists" +[ ! -f "$test_dir/a.sorted.bam.bai" ] && echo "File 'a.sorted.bam.bai' does not exist!" && exit 1 + +echo ">>> Check whether output is empty" +[ ! -s "$test_dir/a.sorted.bam.bai" ] && echo "File 'a.sorted.bam.bai' is empty!" && exit 1 + +echo ">>> Check whether output is correct" +diff "$test_dir/a.sorted.bam.bai" "$test_dir/a_ref.sorted.bam.bai" || \ + (echo "File 'a.sorted.bam.bai' does not match expected output." && exit 1) + +rm "$test_dir/a.sorted.bam.bai" + +################################################################################################# + +echo ">>> Generating CSI index" +"$meta_executable" \ + --input "$test_dir/a.sorted.bam" \ + --csi \ + --output "$test_dir/a.sorted.bam.csi" + +echo ">>> Check whether output exists" +[ ! -f "$test_dir/a.sorted.bam.csi" ] && echo "File 'a.sorted.bam.csi' does not exist!" && exit 1 + +echo ">>> Check whether output is empty" +[ ! -s "$test_dir/a.sorted.bam.csi" ] && echo "File 'a.sorted.bam.csi' is empty!" && exit 1 + +echo ">>> Check whether output is correct" +diff "$test_dir/a.sorted.bam.csi" "$test_dir/a_ref.sorted.bam.csi" || \ + (echo "File 'a.sorted.bam.csi' does not match expected output." && exit 1) + +rm "$test_dir/a.sorted.bam.csi" + +################################################################################################# + +echo ">>> Generating bam index with -M option" +"$meta_executable" \ + --input "$test_dir/a.sorted.bam" \ + --bai \ + --output "$test_dir/a_multiple.sorted.bam.bai" \ + --multiple + +echo ">>> Check whether output exists" +[ ! -f "$test_dir/a_multiple.sorted.bam.bai" ] && echo "File 'a_multiple.sorted.bam.bai' does not exist!" && exit 1 + +echo ">>> Check whether output is empty" +[ ! -s "$test_dir/a_multiple.sorted.bam.bai" ] && echo "File 'a_multiple.sorted.bam.bai' is empty!" && exit 1 + +echo ">>> Check whether output is correct" +diff "$test_dir/a_multiple.sorted.bam.bai" "$test_dir/a_multiple_ref.sorted.bam.bai" || \ + (echo "File 'a_multiple.sorted.bam.bai' does not match expected output." && exit 1) + + +################################################################################################# + +echo ">>> Generating BAM index with -m option" + +"$meta_executable" \ + --input "$test_dir/a.sorted.bam" \ + --min_shift 4 \ + --bai \ + --output "$test_dir/a_4.sorted.bam.bai" + +echo ">>> Check whether output exists" +[ ! -f "$test_dir/a_4.sorted.bam.bai" ] && echo "File 'a_4.sorted.bam.bai' does not exist!" && exit 1 + +echo ">>> Check whether output is empty" +[ ! -s "$test_dir/a_4.sorted.bam.bai" ] && echo "File 'a_4.sorted.bam.bai' is empty!" && exit 1 + +echo ">>> Check whether output is correct" +diff "$test_dir/a_4.sorted.bam.bai" "$test_dir/a_4_ref.sorted.bam.bai" || \ + (echo "File 'a_4.sorted.bam.bai' does not match expected output." && exit 1) + +rm "$test_dir/a_4.sorted.bam.bai" + +################################################################################################# + + +echo "All tests succeeded!" +exit 0 \ No newline at end of file diff --git a/src/samtools/samtools_index/test_data/a.sorted.bam b/src/samtools/samtools_index/test_data/a.sorted.bam new file mode 100644 index 00000000..1c81d7cd Binary files /dev/null and b/src/samtools/samtools_index/test_data/a.sorted.bam differ diff --git a/src/samtools/samtools_index/test_data/a_4_ref.sorted.bam.bai b/src/samtools/samtools_index/test_data/a_4_ref.sorted.bam.bai new file mode 100644 index 00000000..4f08f5d5 Binary files /dev/null and b/src/samtools/samtools_index/test_data/a_4_ref.sorted.bam.bai differ diff --git a/src/samtools/samtools_index/test_data/a_multiple_ref.sorted.bam.bai b/src/samtools/samtools_index/test_data/a_multiple_ref.sorted.bam.bai new file mode 100644 index 00000000..4f08f5d5 Binary files /dev/null and b/src/samtools/samtools_index/test_data/a_multiple_ref.sorted.bam.bai differ diff --git a/src/samtools/samtools_index/test_data/a_ref.sorted.bam.bai b/src/samtools/samtools_index/test_data/a_ref.sorted.bam.bai new file mode 100644 index 00000000..4f08f5d5 Binary files /dev/null and b/src/samtools/samtools_index/test_data/a_ref.sorted.bam.bai differ diff --git a/src/samtools/samtools_index/test_data/a_ref.sorted.bam.csi b/src/samtools/samtools_index/test_data/a_ref.sorted.bam.csi new file mode 100644 index 00000000..e337be19 Binary files /dev/null and b/src/samtools/samtools_index/test_data/a_ref.sorted.bam.csi differ diff --git a/src/samtools/samtools_index/test_data/script.sh b/src/samtools/samtools_index/test_data/script.sh new file mode 100755 index 00000000..ee86e514 --- /dev/null +++ b/src/samtools/samtools_index/test_data/script.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# dowload test data from snakemake wrapper +if [ ! -d /tmp/idxstats_source ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers.git /tmp/idxstats_source +fi + +cp -r /tmp/idxstats_source/bio/samtools/idxstats/test/mapped/* src/samtools/idxstats/test_data +# samtools index a_ref.sorted.bam -o a_ref.sorted.bam.bai +# samtools index a_ref.sorted.bam -c a_ref.sorted.bam.csi + + diff --git a/src/samtools/samtools_sort/config.vsh.yaml b/src/samtools/samtools_sort/config.vsh.yaml new file mode 100644 index 00000000..a78800da --- /dev/null +++ b/src/samtools/samtools_sort/config.vsh.yaml @@ -0,0 +1,149 @@ +name: samtools_sort +namespace: samtools +description: Sort SAM/BAM/CRAM file. +keywords: [sort, bam, sam, cram] +links: + homepage: https://www.htslib.org/ + documentation: https://www.htslib.org/doc/samtools-sort.html + repository: https://github.com/samtools/samtools +references: + doi: [10.1093/bioinformatics/btp352, 10.1093/gigascience/giab008] +license: MIT/Expat + +argument_groups: + - name: Inputs + arguments: + - name: --input + type: file + description: SAM/BAM/CRAM input file. + required: true + must_exist: true + - name: Outputs + arguments: + - name: --output + type: file + description: | + Write final output to file. + required: true + direction: output + example: out.bam + - name: --output_fmt + alternatives: -O + type: string + description: | + Specify output format (SAM, BAM, CRAM). + example: BAM + - name: --output_fmt_option + type: string + description: | + Specify a single output file format option in the form + of OPTION or OPTION=VALUE. + - name: --reference + type: file + description: | + Reference sequence FASTA FILE. + example: ref.fa + - name: --write_index + type: boolean_true + description: | + Automatically index the output files. + - name: --prefix + alternatives: -T + type: string + description: | + Write temporary files to PREFIX.nnnn.bam. + - name: --no_PG + type: boolean_true + description: | + Do not add a PG line. + - name: --template_coordinate + type: boolean_true + description: | + Sort by template-coordinate. + - name: --input_fmt_option + type: string + description: | + Specify a single input file format option in the form + of OPTION or OPTION=VALUE. + - name: Options + arguments: + - name: --compression + alternatives: -l + type: integer + description: | + Set compression level, from 0 (uncompressed) to 9 (best). + default: 0 + - name: --uncompressed + alternatives: -u + type: boolean_true + description: | + Output uncompressed data (equivalent to --compression 0). + - name: --minimiser + alternatives: -M + type: boolean_true + description: | + Use minimiser for clustering unaligned/unplaced reads. + - name: --not_reverse + alternatives: -R + type: boolean_true + description: | + Do not use reverse strand (only compatible with --minimiser) + - name: --kmer_size + alternatives: -K + type: integer + description: | + Kmer size to use for minimiser. + example: 20 + - name: --order + alternatives: -I + type: file + description: | + Order minimisers by their position in FILE FASTA. + example: ref.fa + - name: --window + alternatives: -w + type: integer + description: | + Window size for minimiser INDEXING VIA --order REF.FA. + example: 100 + - name: --homopolymers + alternatives: -H + type: boolean_true + description: | + Squash homopolymers when computing minimiser. + - name: --natural_sort + alternatives: -n + type: boolean_true + description: | + Sort by read name (natural): cannot be used with samtools index. + - name: --ascii_sort + alternatives: -N + type: boolean_true + description: | + Sort by read name (ASCII): cannot be used with samtools index. + - name: --tag + alternatives: -t + type: string + description: | + Sort by value of TAG. Uses position as secondary index + (or read name if --natural_sort is set). + +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 + setup: + - type: docker + run: | + samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ + sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt +runners: +- type: executable +- type: nextflow \ No newline at end of file diff --git a/src/samtools/samtools_sort/help.txt b/src/samtools/samtools_sort/help.txt new file mode 100644 index 00000000..27cd86a0 --- /dev/null +++ b/src/samtools/samtools_sort/help.txt @@ -0,0 +1,40 @@ +``` +samtools sort +``` + +Usage: samtools sort [options...] [in.bam] +Options: + -l INT Set compression level, from 0 (uncompressed) to 9 (best) + -u Output uncompressed data (equivalent to -l 0) + -m INT Set maximum memory per thread; suffix K/M/G recognized [768M] + -M Use minimiser for clustering unaligned/unplaced reads + -R Do not use reverse strand (only compatible with -M) + -K INT Kmer size to use for minimiser [20] + -I FILE Order minimisers by their position in FILE FASTA + -w INT Window size for minimiser indexing via -I ref.fa [100] + -H Squash homopolymers when computing minimiser + -n Sort by read name (natural): cannot be used with samtools index + -N Sort by read name (ASCII): cannot be used with samtools index + -t TAG Sort by value of TAG. Uses position as secondary index (or read name if -n is set) + -o FILE Write final output to FILE rather than standard output + -T PREFIX Write temporary files to PREFIX.nnnn.bam + --no-PG + Do not add a PG line + --template-coordinate + Sort by template-coordinate + --input-fmt-option OPT[=VAL] + Specify a single input file format option in the form + of OPTION or OPTION=VALUE + -O, --output-fmt FORMAT[,OPT[=VAL]]... + Specify output format (SAM, BAM, CRAM) + --output-fmt-option OPT[=VAL] + Specify a single output file format option in the form + of OPTION or OPTION=VALUE + --reference FILE + Reference sequence FASTA FILE [null] + -@, --threads INT + Number of additional threads to use [0] + --write-index + Automatically index the output files [off] + --verbosity INT + Set level of verbosity \ No newline at end of file diff --git a/src/samtools/samtools_sort/script.sh b/src/samtools/samtools_sort/script.sh new file mode 100644 index 00000000..94836c18 --- /dev/null +++ b/src/samtools/samtools_sort/script.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +set -e + +[[ "$par_uncompressed" == "false" ]] && unset par_uncompressed +[[ "$par_minimiser" == "false" ]] && unset par_minimiser +[[ "$par_not_reverse" == "false" ]] && unset par_not_reverse +[[ "$par_homopolymers" == "false" ]] && unset par_homopolymers +[[ "$par_natural_sort" == "false" ]] && unset par_natural_sort +[[ "$par_ascii_sort" == "false" ]] && unset par_ascii_sort +[[ "$par_template_coordinate" == "false" ]] && unset par_template_coordinate +[[ "$par_write_index" == "false" ]] && unset par_write_index +[[ "$par_no_PG" == "false" ]] && unset par_no_PG + + +samtools sort \ + ${par_compression:+-l "$par_compression"} \ + ${par_uncompressed:+-u} \ + ${par_minimiser:+-M} \ + ${par_not_reverse:+-R} \ + ${par_kmer_size:+-K "$par_kmer_size"} \ + ${par_order:+-I "$par_order"} \ + ${par_window:+-w "$par_window"} \ + ${par_homopolymers:+-H} \ + ${par_natural_sort:+-n} \ + ${par_ascii_sort:+-N} \ + ${par_tag:+-t "$par_tag"} \ + ${par_input_fmt_option:+--input-fmt-option "$par_input_fmt_option"} \ + ${par_template_coordinate:+--template-coordinate} \ + ${par_write_index:+--write-index} \ + ${par_prefix:+-T "$par_prefix"} \ + ${par_no_PG:+--no-PG} \ + ${par_output_fmt:+-O "$par_output_fmt"} \ + ${par_output_fmt_option:+--output-fmt-option "$par_output_fmt_option"} \ + ${par_reference:+--reference "$par_reference"} \ + -o "$par_output" \ + "$par_input" + +# save text files containing the output of samtools view for later comparison +samtools view "$par_output" -o "$par_output".txt \ No newline at end of file diff --git a/src/samtools/samtools_sort/test.sh b/src/samtools/samtools_sort/test.sh new file mode 100644 index 00000000..d8425dc9 --- /dev/null +++ b/src/samtools/samtools_sort/test.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +test_dir="${meta_resources_dir}/test_data" +out_dir="${meta_resources_dir}/test_data/text" + +# Files are compared using the "samtools view" output. +############################################################################################ + +echo ">>> Test 1: Sorting a BAM file" + +"$meta_executable" \ + --input "$test_dir/a.bam" \ + --output "$test_dir/a.sorted.bam" + +echo ">>> Check if output file exists" +[ ! -f "$test_dir/a.sorted.bam" ] \ + && echo "Output file a.sorted.bam does not exist" && exit 1 + +echo ">>> Check if output is empty" +[ ! -s "$test_dir/a.sorted.bam" ] \ + && echo "Output file a.sorted.bam is empty" && exit 1 + +echo ">>> Check if output matches expected output" +diff -a "$test_dir/a.sorted.bam.txt" "$out_dir/a_ref.sorted.txt" \ + || (echo "Output file a.sorted.bam does not match expected output" && exit 1) + +rm "$test_dir/a.sorted.bam" "$test_dir/a.sorted.bam.txt" + +############################################################################################ + +echo ">>> Test 2: Sorting a BAM file according to ascii order" + +"$meta_executable" \ + --input "$test_dir/a.bam" \ + --ascii_sort \ + --output "$test_dir/ascii.sorted.bam" + +echo ">>> Check if output file exists" +[ ! -f "$test_dir/ascii.sorted.bam" ] \ + && echo "Output file ascii.sorted.bam does not exist" && exit 1 + +echo ">>> Check if output is empty" +[ ! -s "$test_dir/ascii.sorted.bam" ] \ + && echo "Output file ascii.sorted.bam is empty" && exit 1 + +echo ">>> Check if output matches expected output" +diff -a "$test_dir/ascii.sorted.bam.txt" "$out_dir/ascii_ref.sorted.txt" \ + || (echo "Output file ascii.sorted.bam does not match expected output" && exit 1) + +rm "$test_dir/ascii.sorted.bam" "$test_dir/ascii.sorted.bam.txt" + +############################################################################################ + +echo ">>> Test 3: Sorting a BAM file with compression" + +"$meta_executable" \ + --input "$test_dir/a.bam" \ + --compression 5 \ + --output "$test_dir/compressed.sorted.bam" + +echo ">>> Check if output file exists" +[ ! -f "$test_dir/compressed.sorted.bam" ] \ + && echo "Output file compressed.sorted.bam does not exist" && exit 1 + +echo ">>> Check if output is empty" +[ ! -s "$test_dir/compressed.sorted.bam" ] \ + && echo "Output file compressed.sorted.bam is empty" && exit 1 + +echo ">>> Check if output matches expected output" # +diff "$test_dir/compressed.sorted.bam.txt" "$out_dir/compressed_ref.sorted.txt" \ + || (echo "Output file compressed.sorted.bam does not match expected output" && exit 1) + +rm "$test_dir/compressed.sorted.bam" "$test_dir/compressed.sorted.bam.txt" + +############################################################################################ + + +echo "All tests succeeded!" +exit 0 \ No newline at end of file diff --git a/src/samtools/samtools_sort/test_data/a.bam b/src/samtools/samtools_sort/test_data/a.bam new file mode 100644 index 00000000..dba1268a Binary files /dev/null and b/src/samtools/samtools_sort/test_data/a.bam differ diff --git a/src/samtools/samtools_sort/test_data/output/a_ref.sorted.bam b/src/samtools/samtools_sort/test_data/output/a_ref.sorted.bam new file mode 100644 index 00000000..da4edc86 Binary files /dev/null and b/src/samtools/samtools_sort/test_data/output/a_ref.sorted.bam differ diff --git a/src/samtools/samtools_sort/test_data/output/ascii_ref.sorted.bam b/src/samtools/samtools_sort/test_data/output/ascii_ref.sorted.bam new file mode 100644 index 00000000..58e4f57e Binary files /dev/null and b/src/samtools/samtools_sort/test_data/output/ascii_ref.sorted.bam differ diff --git a/src/samtools/samtools_sort/test_data/output/compressed_ref.sorted.bam b/src/samtools/samtools_sort/test_data/output/compressed_ref.sorted.bam new file mode 100644 index 00000000..d10c2c00 Binary files /dev/null and b/src/samtools/samtools_sort/test_data/output/compressed_ref.sorted.bam differ diff --git a/src/samtools/samtools_sort/test_data/script.sh b/src/samtools/samtools_sort/test_data/script.sh new file mode 100755 index 00000000..a7a5b13c --- /dev/null +++ b/src/samtools/samtools_sort/test_data/script.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# dowload test data from snakemake wrapper +if [ ! -d /tmp/idxstats_source ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers.git /tmp/sort_source +fi + +cp -r /tmp/sort_source/bio/samtools/sort/test/mapped/* src/samtools/samtools_sort/test_data diff --git a/src/samtools/samtools_sort/test_data/text/a_ref.sorted.txt b/src/samtools/samtools_sort/test_data/text/a_ref.sorted.txt new file mode 100644 index 00000000..ce8d0527 --- /dev/null +++ b/src/samtools/samtools_sort/test_data/text/a_ref.sorted.txt @@ -0,0 +1,6 @@ +a1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +b1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +c1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +a1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** +b1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** +c1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** diff --git a/src/samtools/samtools_sort/test_data/text/ascii_ref.sorted.txt b/src/samtools/samtools_sort/test_data/text/ascii_ref.sorted.txt new file mode 100644 index 00000000..00cdbc69 --- /dev/null +++ b/src/samtools/samtools_sort/test_data/text/ascii_ref.sorted.txt @@ -0,0 +1,6 @@ +a1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +a1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** +b1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +b1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** +c1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +c1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** diff --git a/src/samtools/samtools_sort/test_data/text/compressed_ref.sorted.txt b/src/samtools/samtools_sort/test_data/text/compressed_ref.sorted.txt new file mode 100644 index 00000000..ce8d0527 --- /dev/null +++ b/src/samtools/samtools_sort/test_data/text/compressed_ref.sorted.txt @@ -0,0 +1,6 @@ +a1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +b1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +c1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +a1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** +b1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** +c1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** diff --git a/src/samtools/samtools_stats/config.vsh.yaml b/src/samtools/samtools_stats/config.vsh.yaml new file mode 100644 index 00000000..0d8f57a4 --- /dev/null +++ b/src/samtools/samtools_stats/config.vsh.yaml @@ -0,0 +1,166 @@ +name: samtools_stats +namespace: samtools +description: Reports alignment summary statistics for a BAM file. +keywords: [statistics, counts, bam, sam, cram] +links: + homepage: https://www.htslib.org/ + documentation: https://www.htslib.org/doc/samtools-stats.html + repository: https://github.com/samtools/samtools +references: + doi: [10.1093/bioinformatics/btp352, 10.1093/gigascience/giab008] +license: MIT/Expat + +argument_groups: + - name: Inputs + arguments: + - name: --input + type: file + description: | + Input file. + required: true + must_exist: true + - name: --bai + type: file + description: | + Index file. + - name: --fasta + type: file + description: | + Reference file the CRAM was created with. + - name: --coverage + alternatives: -c + type: integer + description: | + Coverage distribution min,max,step [1,1000,1]. + multiple: true + multiple_sep: ',' + - name: --remove_dups + alternatives: -d + type: boolean_true + description: | + Exclude from statistics reads marked as duplicates. + - name: --customized_index_file + alternatives: -X + type: boolean_true + description: | + Use a customized index file. + - name: --required_flag + alternatives: -f + type: string + description: | + Required flag, 0 for unset. See also `samtools flags`. + default: "0" + - name: --filtering_flag + alternatives: -F + type: string + description: | + Filtering flag, 0 for unset. See also `samtools flags`. + default: "0" + - name: --GC_depth + type: double + description: | + The size of GC-depth bins (decreasing bin size increases memory requirement). + default: 20000.0 + - name: --insert_size + alternatives: -i + type: integer + description: | + Maximum insert size. + default: 8000 + - name: --id + alternatives: -I + type: string + description: | + Include only listed read group or sample name. + - name: --read_length + alternatives: -l + type: integer + description: | + Include in the statistics only reads with the given read length. + default: -1 + - name: --most_inserts + alternatives: -m + type: double + description: | + Report only the main part of inserts. + default: 0.99 + - name: --split_prefix + alternatives: -P + type: string + description: | + Path or string prefix for filepaths output by --split (default is input filename). + - name: --trim_quality + alternatives: -q + type: integer + description: | + The BWA trimming parameter. + default: 0 + - name: --ref_seq + alternatives: -r + type: file + description: | + Reference sequence (required for GC-depth and mismatches-per-cycle calculation). + - name: --split + alternatives: -S + type: string + description: | + Also write statistics to separate files split by tagged field. + - name: --target_regions + alternatives: -t + type: file + description: | + Do stats in these regions only. Tab-delimited file chr,from,to, 1-based, inclusive. + - name: --sparse + alternatives: -x + type: boolean_true + description: | + Suppress outputting IS rows where there are no insertions. + - name: --remove_overlaps + alternatives: -p + type: boolean_true + description: | + Remove overlaps of paired-end reads from coverage and base count computations. + - name: --cov_threshold + alternatives: -g + type: integer + description: | + Only bases with coverage above this value will be included in the target percentage computation. + default: 0 + - name: --input_fmt_option + type: string + description: | + Specify a single input file format option in the form of OPTION or OPTION=VALUE. + - name: --reference + type: file + description: | + Reference sequence FASTA FILE. + - name: Outputs + arguments: + - name: --output + alternatives: -o + type: file + description: | + Output file. + default: "out.txt" + required: true + direction: output + +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 + setup: + - type: docker + run: | + samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ + sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt +runners: +- type: executable +- type: nextflow diff --git a/src/samtools/samtools_stats/help.txt b/src/samtools/samtools_stats/help.txt new file mode 100644 index 00000000..2298a362 --- /dev/null +++ b/src/samtools/samtools_stats/help.txt @@ -0,0 +1,36 @@ +``` +samtools stats -h +``` + +Usage: samtools stats [OPTIONS] file.bam + samtools stats [OPTIONS] file.bam chr:from-to +Options: + -c, --coverage ,, Coverage distribution min,max,step [1,1000,1] + -d, --remove-dups Exclude from statistics reads marked as duplicates + -X, --customized-index-file Use a customized index file + -f, --required-flag Required flag, 0 for unset. See also `samtools flags` [0] + -F, --filtering-flag Filtering flag, 0 for unset. See also `samtools flags` [0] + --GC-depth the size of GC-depth bins (decreasing bin size increases memory requirement) [2e4] + -h, --help This help message + -i, --insert-size Maximum insert size [8000] + -I, --id Include only listed read group or sample name + -l, --read-length Include in the statistics only reads with the given read length [-1] + -m, --most-inserts Report only the main part of inserts [0.99] + -P, --split-prefix Path or string prefix for filepaths output by -S (default is input filename) + -q, --trim-quality The BWA trimming parameter [0] + -r, --ref-seq Reference sequence (required for GC-depth and mismatches-per-cycle calculation). + -s, --sam Ignored (input format is auto-detected). + -S, --split Also write statistics to separate files split by tagged field. + -t, --target-regions Do stats in these regions only. Tab-delimited file chr,from,to, 1-based, inclusive. + -x, --sparse Suppress outputting IS rows where there are no insertions. + -p, --remove-overlaps Remove overlaps of paired-end reads from coverage and base count computations. + -g, --cov-threshold Only bases with coverage above this value will be included in the target percentage computation [0] + --input-fmt-option OPT[=VAL] + Specify a single input file format option in the form + of OPTION or OPTION=VALUE + --reference FILE + Reference sequence FASTA FILE [null] + -@, --threads INT + Number of additional threads to use [0] + --verbosity INT + Set level of verbosity diff --git a/src/samtools/samtools_stats/script.sh b/src/samtools/samtools_stats/script.sh new file mode 100644 index 00000000..6e32e9a5 --- /dev/null +++ b/src/samtools/samtools_stats/script.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +set -e + +[[ "$par_remove_dups" == "false" ]] && unset par_remove_dups +[[ "$par_customized_index_file" == "false" ]] && unset par_customized_index_file +[[ "$par_sparse" == "false" ]] && unset par_sparse +[[ "$par_remove_overlaps" == "false" ]] && unset par_remove_overlaps + +samtools stats \ + ${par_coverage:+-c "$par_coverage"} \ + ${par_remove_dups:+-d} \ + ${par_required_flag:+-f "$par_required_flag"} \ + ${par_filtering_flag:+-F "$par_filtering_flag"} \ + ${par_GC_depth:+--GC-depth "$par_GC_depth"} \ + ${par_insert_size:+-i "$par_insert_size"} \ + ${par_id:+-I "$par_id"} \ + ${par_read_length:+-l "$par_read_length"} \ + ${par_most_inserts:+-m "$par_most_inserts"} \ + ${par_split_prefix:+-P "$par_split_prefix"} \ + ${par_trim_quality:+-q "$par_trim_quality"} \ + ${par_ref_seq:+-r "$par_ref_seq"} \ + ${par_split:+-S "$par_split"} \ + ${par_target_regions:+-t "$par_target_regions"} \ + ${par_sparse:+-x} \ + ${par_remove_overlaps:+-p} \ + ${par_cov_threshold:+-g "$par_cov_threshold"} \ + ${par_input_fmt_option:+-O "$par_input_fmt_option"} \ + ${par_reference:+-R "$par_reference"} \ + "$par_input" \ + > "$par_output" + +exit 0 \ No newline at end of file diff --git a/src/samtools/samtools_stats/test.sh b/src/samtools/samtools_stats/test.sh new file mode 100644 index 00000000..05d70d30 --- /dev/null +++ b/src/samtools/samtools_stats/test.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +test_dir="${meta_resources_dir}/test_data" + +############################################################################################ + +echo ">>> Test 1: $meta_functionality_name" +"$meta_executable" \ + --input "$test_dir/test.paired_end.sorted.bam" \ + --bai "$test_dir/test.paired_end.sorted.bam.bai" \ + --output "$test_dir/test.paired_end.sorted.txt" + +echo ">>> Checking whether output exists" +[ ! -f "$test_dir/test.paired_end.sorted.txt" ] && echo "File 'test.paired_end.sorted.txt' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/test.paired_end.sorted.txt" ] && echo "File 'test.paired_end.sorted.txt' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +# compare using diff, ignoring the line stating the command that was passed. +diff <(grep -v "^# The command" "$test_dir/test.paired_end.sorted.txt") \ + <(grep -v "^# The command" "$test_dir/ref.paired_end.sorted.txt") || \ + (echo "Output file ref.paired_end.sorted.txt does not match expected output" && exit 1) + +rm "$test_dir/test.paired_end.sorted.txt" + +############################################################################################ + +echo ">>> Test 2: $meta_functionality_name with --remove_dups" +"$meta_executable" \ + --remove_dups \ + --input "$test_dir/test.paired_end.sorted.bam" \ + --bai "$test_dir/test.paired_end.sorted.bam.bai" \ + --output "$test_dir/test.d.paired_end.sorted.txt" + +echo ">>> Checking whether output exists" +[ ! -f "$test_dir/ref.d.paired_end.sorted.txt" ] && echo "File 'ref.d.paired_end.sorted.txt' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/ref.d.paired_end.sorted.txt" ] && echo "File 'ref.d.paired_end.sorted.txt' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +# compare using diff, ignoring the line stating the command that was passed. +diff <(grep -v "^# The command" "$test_dir/test.d.paired_end.sorted.txt") \ + <(grep -v "^# The command" "$test_dir/ref.d.paired_end.sorted.txt") || \ + (echo "Output file ref.d.paired_end.sorted.txt does not match expected output" && exit 1) + +rm "$test_dir/test.d.paired_end.sorted.txt" + +############################################################################################ + +echo ">>> Test 3: $meta_functionality_name with --remove_overlaps" +"$meta_executable" \ + --remove_overlaps \ + --input "$test_dir/test.paired_end.sorted.bam" \ + --bai "$test_dir/test.paired_end.sorted.bam.bai" \ + --output "$test_dir/test.p.paired_end.sorted.txt" + +echo ">>> Checking whether output exists" +[ ! -f "$test_dir/ref.p.paired_end.sorted.txt" ] && echo "File 'ref.p.paired_end.sorted.txt' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$test_dir/ref.p.paired_end.sorted.txt" ] && echo "File 'ref.p.paired_end.sorted.txt' is empty!" && exit 1 + + +echo ">>> Checking whether output is correct" +# compare using diff, ignoring the line stating the command that was passed. +diff <(grep -v "^# The command" "$test_dir/test.p.paired_end.sorted.txt") \ + <(grep -v "^# The command" "$test_dir/ref.p.paired_end.sorted.txt") || \ + (echo "Output file ref.p.paired_end.sorted.txt does not match expected output" && exit 1) + +rm "$test_dir/test.p.paired_end.sorted.txt" + +############################################################################################ + +echo ">>> All tests passed successfully." + +exit 0 diff --git a/src/samtools/samtools_stats/test_data/ref.d.paired_end.sorted.txt b/src/samtools/samtools_stats/test_data/ref.d.paired_end.sorted.txt new file mode 100644 index 00000000..315c597d --- /dev/null +++ b/src/samtools/samtools_stats/test_data/ref.d.paired_end.sorted.txt @@ -0,0 +1,1539 @@ +# This file was produced by samtools stats (1.19.2+htslib-1.19.1) and can be plotted using plot-bamstats +# This file contains statistics for all reads. +# The command line was: stats -d test_data/test.paired_end.sorted.bam +# CHK, Checksum [2]Read Names [3]Sequences [4]Qualities +# CHK, CRC32 of reads which passed filtering followed by addition (32bit overflow) +CHK 696e2242 1799722a a8072f55 +# Summary Numbers. Use `grep ^SN | cut -f 2-` to extract this part. +SN raw total sequences: 200 # excluding supplementary and secondary reads +SN filtered sequences: 0 +SN sequences: 200 +SN is sorted: 1 +SN 1st fragments: 100 +SN last fragments: 100 +SN reads mapped: 197 +SN reads mapped and paired: 194 # paired-end technology bit set + both mates mapped +SN reads unmapped: 3 +SN reads properly paired: 192 # proper-pair bit set +SN reads paired: 200 # paired-end technology bit set +SN reads duplicated: 0 # PCR or optical duplicate bit set +SN reads MQ0: 0 # mapped and MQ=0 +SN reads QC failed: 0 +SN non-primary alignments: 0 +SN supplementary alignments: 0 +SN total length: 27645 # ignores clipping +SN total first fragment length: 13897 # ignores clipping +SN total last fragment length: 13748 # ignores clipping +SN bases mapped: 27423 # ignores clipping +SN bases mapped (cigar): 27401 # more accurate +SN bases trimmed: 0 +SN bases duplicated: 0 +SN mismatches: 140 # from NM fields +SN error rate: 5.109303e-03 # mismatches / bases mapped (cigar) +SN average length: 138 +SN average first fragment length: 139 +SN average last fragment length: 137 +SN maximum length: 151 +SN maximum first fragment length: 151 +SN maximum last fragment length: 151 +SN average quality: 33.3 +SN insert size average: 207.7 +SN insert size standard deviation: 66.4 +SN inward oriented pairs: 88 +SN outward oriented pairs: 9 +SN pairs with other orientation: 0 +SN pairs on different chromosomes: 0 +SN percentage of properly paired reads (%): 96.0 +# First Fragment Qualities. Use `grep ^FFQ | cut -f 2-` to extract this part. +# Columns correspond to qualities and rows to cycles. First column is the cycle number. +FFQ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100 0 0 0 0 0 +FFQ 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 96 0 0 0 0 0 +FFQ 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 97 0 0 0 0 0 +FFQ 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 94 0 0 0 1 0 +FFQ 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 93 0 0 0 0 0 +FFQ 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 7 0 0 0 86 0 +FFQ 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 7 0 0 0 84 0 +FFQ 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 12 0 0 0 83 0 +FFQ 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 85 0 +FFQ 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 5 0 0 0 87 0 +FFQ 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 90 0 +FFQ 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 88 0 +FFQ 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 8 0 0 0 84 0 +FFQ 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 6 0 0 0 86 0 +FFQ 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15 0 0 0 83 0 +FFQ 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0 90 0 +FFQ 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 86 0 +FFQ 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 93 0 +FFQ 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 2 0 0 0 86 0 +FFQ 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 4 0 0 0 85 0 +FFQ 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 95 0 +FFQ 22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 91 0 +FFQ 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 90 0 +FFQ 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 1 0 0 0 90 0 +FFQ 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 85 0 +FFQ 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 87 0 +FFQ 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 5 0 0 0 87 0 +FFQ 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 88 0 +FFQ 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 4 0 0 0 90 0 +FFQ 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 7 0 0 0 87 0 +FFQ 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 4 0 0 0 0 0 2 0 0 0 0 3 0 0 0 85 0 +FFQ 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 6 0 0 0 89 0 +FFQ 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 7 0 0 0 84 0 +FFQ 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 89 0 +FFQ 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 88 0 +FFQ 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 8 0 0 0 85 0 +FFQ 37 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 4 0 0 0 87 0 +FFQ 38 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 4 0 0 0 91 0 +FFQ 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 86 0 +FFQ 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 3 0 0 0 90 0 +FFQ 41 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 9 0 0 0 85 0 +FFQ 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 5 0 0 0 88 0 +FFQ 43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 4 0 0 0 83 0 +FFQ 44 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 8 0 0 0 83 0 +FFQ 45 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 86 0 +FFQ 46 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 9 0 0 0 85 0 +FFQ 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 10 0 0 0 77 0 +FFQ 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 12 0 0 0 80 0 +FFQ 49 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 8 0 0 0 79 0 +FFQ 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 81 0 +FFQ 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 12 0 0 0 83 0 +FFQ 52 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 12 0 0 0 80 0 +FFQ 53 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 15 0 0 0 77 0 +FFQ 54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 7 0 0 0 0 12 0 0 0 72 0 +FFQ 55 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 8 0 0 0 82 0 +FFQ 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 9 0 0 0 80 0 +FFQ 57 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 13 0 0 0 77 0 +FFQ 58 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 3 0 0 0 0 0 3 0 0 0 0 11 0 0 0 76 0 +FFQ 59 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 81 0 +FFQ 60 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 5 0 0 0 83 0 +FFQ 61 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 8 0 0 0 81 0 +FFQ 62 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 6 0 0 0 81 0 +FFQ 63 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 84 0 +FFQ 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 7 0 0 0 77 0 +FFQ 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 10 0 0 0 77 0 +FFQ 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 10 0 0 0 76 0 +FFQ 67 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 15 0 0 0 77 0 +FFQ 68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 10 0 0 0 81 0 +FFQ 69 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 5 0 0 0 0 4 0 0 0 82 0 +FFQ 70 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 3 0 0 0 0 0 4 0 0 0 0 7 0 0 0 78 0 +FFQ 71 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 9 0 0 0 79 0 +FFQ 72 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 12 0 0 0 81 0 +FFQ 73 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 9 0 0 0 78 0 +FFQ 74 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 82 0 +FFQ 75 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 12 0 0 0 78 0 +FFQ 76 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 6 0 0 0 80 0 +FFQ 77 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 79 0 +FFQ 78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 13 0 0 0 73 0 +FFQ 79 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 15 0 0 0 72 0 +FFQ 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 15 0 0 0 72 0 +FFQ 81 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 74 0 +FFQ 82 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 12 0 0 0 72 0 +FFQ 83 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 7 0 0 0 74 0 +FFQ 84 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 5 0 0 0 80 0 +FFQ 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 10 0 0 0 70 0 +FFQ 86 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 17 0 0 0 68 0 +FFQ 87 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 7 0 0 0 72 0 +FFQ 88 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 7 0 0 0 77 0 +FFQ 89 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 9 0 0 0 78 0 +FFQ 90 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 11 0 0 0 72 0 +FFQ 91 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 10 0 0 0 74 0 +FFQ 92 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 7 0 0 0 75 0 +FFQ 93 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 12 0 0 0 68 0 +FFQ 94 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 77 0 +FFQ 95 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 10 0 0 0 70 0 +FFQ 96 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 7 0 0 0 75 0 +FFQ 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 11 0 0 0 71 0 +FFQ 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 14 0 0 0 61 0 +FFQ 99 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 67 0 +FFQ 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 12 0 0 0 64 0 +FFQ 101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 5 0 0 0 67 0 +FFQ 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 9 0 0 0 68 0 +FFQ 103 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 14 0 0 0 61 0 +FFQ 104 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 17 0 0 0 59 0 +FFQ 105 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 19 0 0 0 56 0 +FFQ 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 16 0 0 0 57 0 +FFQ 107 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 17 0 0 0 58 0 +FFQ 108 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 15 0 0 0 56 0 +FFQ 109 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 15 0 0 0 52 0 +FFQ 110 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 19 0 0 0 50 0 +FFQ 111 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 19 0 0 0 52 0 +FFQ 112 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 16 0 0 0 55 0 +FFQ 113 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 8 0 0 0 0 22 0 0 0 45 0 +FFQ 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 9 0 0 0 0 22 0 0 0 45 0 +FFQ 115 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 14 0 0 0 0 16 0 0 0 48 0 +FFQ 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 8 0 0 0 0 27 0 0 0 43 0 +FFQ 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 21 0 0 0 49 0 +FFQ 118 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 24 0 0 0 44 0 +FFQ 119 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 23 0 0 0 43 0 +FFQ 120 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 28 0 0 0 48 0 +FFQ 121 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 18 0 0 0 48 0 +FFQ 122 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 6 0 0 0 0 17 0 0 0 54 0 +FFQ 123 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 20 0 0 0 52 0 +FFQ 124 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 20 0 0 0 49 0 +FFQ 125 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 29 0 0 0 42 0 +FFQ 126 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 2 0 0 0 0 0 5 0 0 0 0 22 0 0 0 42 0 +FFQ 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 5 0 0 0 0 0 7 0 0 0 0 20 0 0 0 42 0 +FFQ 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 2 0 0 0 0 0 8 0 0 0 0 20 0 0 0 42 0 +FFQ 129 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 20 0 0 0 48 0 +FFQ 130 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 21 0 0 0 45 0 +FFQ 131 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 20 0 0 0 49 0 +FFQ 132 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 5 0 0 0 0 25 0 0 0 41 0 +FFQ 133 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 18 0 0 0 47 0 +FFQ 134 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 6 0 0 0 0 21 0 0 0 43 0 +FFQ 135 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 9 0 0 0 0 26 0 0 0 37 0 +FFQ 136 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 15 0 0 0 45 0 +FFQ 137 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 19 0 0 0 46 0 +FFQ 138 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 28 0 0 0 34 0 +FFQ 139 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 23 0 0 0 34 0 +FFQ 140 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 19 0 0 0 46 0 +FFQ 141 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 4 0 0 0 0 0 6 0 0 0 0 18 0 0 0 40 0 +FFQ 142 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 24 0 0 0 37 0 +FFQ 143 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 19 0 0 0 34 0 +FFQ 144 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 16 0 0 0 35 0 +FFQ 145 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 34 0 0 0 34 0 +FFQ 146 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 7 0 0 0 0 21 0 0 0 37 0 +FFQ 147 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 16 0 0 0 43 0 +FFQ 148 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 23 0 0 0 33 0 +FFQ 149 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 22 0 0 0 35 0 +FFQ 150 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 18 0 0 0 36 0 +FFQ 151 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 18 0 0 0 11 0 +# Last Fragment Qualities. Use `grep ^LFQ | cut -f 2-` to extract this part. +# Columns correspond to qualities and rows to cycles. First column is the cycle number. +LFQ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100 0 0 0 0 0 +LFQ 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 95 0 0 0 0 0 +LFQ 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 93 0 0 0 0 0 +LFQ 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 94 0 0 0 0 0 +LFQ 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 94 0 0 0 1 0 +LFQ 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 10 0 0 0 83 0 +LFQ 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 93 0 +LFQ 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 3 0 0 0 91 0 +LFQ 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 87 0 +LFQ 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 4 0 0 0 90 0 +LFQ 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 4 0 0 0 90 0 +LFQ 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 7 0 0 0 83 0 +LFQ 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 2 0 0 0 90 0 +LFQ 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 10 0 0 0 86 0 +LFQ 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 6 0 0 0 87 0 +LFQ 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 10 0 0 0 84 0 +LFQ 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0 91 0 +LFQ 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 4 0 0 0 91 0 +LFQ 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 92 0 +LFQ 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 3 0 0 0 90 0 +LFQ 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 89 0 +LFQ 22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 3 0 0 0 88 0 +LFQ 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 89 0 +LFQ 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 88 0 +LFQ 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 9 0 0 0 84 0 +LFQ 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 4 0 0 0 89 0 +LFQ 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 8 0 0 0 87 0 +LFQ 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 2 0 0 0 90 0 +LFQ 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 5 0 0 0 86 0 +LFQ 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 5 0 0 0 88 0 +LFQ 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 92 0 +LFQ 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 5 0 0 0 86 0 +LFQ 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 1 0 0 0 89 0 +LFQ 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 84 0 +LFQ 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 4 0 0 0 87 0 +LFQ 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 8 0 0 0 82 0 +LFQ 37 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 83 0 +LFQ 38 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 8 0 0 0 85 0 +LFQ 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 7 0 0 0 85 0 +LFQ 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 88 0 +LFQ 41 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 11 0 0 0 78 0 +LFQ 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 87 0 +LFQ 43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 9 0 0 0 81 0 +LFQ 44 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 6 0 0 0 86 0 +LFQ 45 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 85 0 +LFQ 46 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 9 0 0 0 81 0 +LFQ 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 5 0 0 0 88 0 +LFQ 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 8 0 0 0 84 0 +LFQ 49 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 11 0 0 0 80 0 +LFQ 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 10 0 0 0 79 0 +LFQ 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 8 0 0 0 80 0 +LFQ 52 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 8 0 0 0 79 0 +LFQ 53 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 7 0 0 0 81 0 +LFQ 54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 15 0 0 0 79 0 +LFQ 55 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 8 0 0 0 85 0 +LFQ 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 8 0 0 0 80 0 +LFQ 57 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 83 0 +LFQ 58 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 9 0 0 0 80 0 +LFQ 59 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 8 0 0 0 82 0 +LFQ 60 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 6 0 0 0 77 0 +LFQ 61 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 9 0 0 0 81 0 +LFQ 62 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 7 0 0 0 80 0 +LFQ 63 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 8 0 0 0 84 0 +LFQ 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 10 0 0 0 80 0 +LFQ 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 5 0 0 0 74 0 +LFQ 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 7 0 0 0 79 0 +LFQ 67 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 10 0 0 0 79 0 +LFQ 68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 7 0 0 0 83 0 +LFQ 69 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 9 0 0 0 76 0 +LFQ 70 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 76 0 +LFQ 71 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 7 0 0 0 74 0 +LFQ 72 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 71 0 +LFQ 73 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 80 0 +LFQ 74 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 8 0 0 0 75 0 +LFQ 75 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 11 0 0 0 80 0 +LFQ 76 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 80 0 +LFQ 77 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 6 0 0 0 77 0 +LFQ 78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 13 0 0 0 69 0 +LFQ 79 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0 0 0 74 0 +LFQ 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 3 0 0 0 0 12 0 0 0 72 0 +LFQ 81 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 10 0 0 0 79 0 +LFQ 82 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 9 0 0 0 78 0 +LFQ 83 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 9 0 0 0 74 0 +LFQ 84 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 12 0 0 0 72 0 +LFQ 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 14 0 0 0 66 0 +LFQ 86 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 12 0 0 0 72 0 +LFQ 87 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 4 0 0 0 78 0 +LFQ 88 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 8 0 0 0 70 0 +LFQ 89 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 73 0 +LFQ 90 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 11 0 0 0 72 0 +LFQ 91 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 11 0 0 0 72 0 +LFQ 92 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 14 0 0 0 68 0 +LFQ 93 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 9 0 0 0 68 0 +LFQ 94 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 15 0 0 0 68 0 +LFQ 95 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 19 0 0 0 64 0 +LFQ 96 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 13 0 0 0 66 0 +LFQ 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 12 0 0 0 70 0 +LFQ 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 13 0 0 0 67 0 +LFQ 99 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 12 0 0 0 62 0 +LFQ 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 15 0 0 0 59 0 +LFQ 101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 11 0 0 0 63 0 +LFQ 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 15 0 0 0 60 0 +LFQ 103 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 14 0 0 0 64 0 +LFQ 104 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 21 0 0 0 57 0 +LFQ 105 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 19 0 0 0 55 0 +LFQ 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 19 0 0 0 55 0 +LFQ 107 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 17 0 0 0 60 0 +LFQ 108 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 13 0 0 0 58 0 +LFQ 109 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 19 0 0 0 55 0 +LFQ 110 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 16 0 0 0 48 0 +LFQ 111 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 14 0 0 0 55 0 +LFQ 112 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 22 0 0 0 43 0 +LFQ 113 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 18 0 0 0 47 0 +LFQ 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 13 0 0 0 50 0 +LFQ 115 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 19 0 0 0 44 0 +LFQ 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 18 0 0 0 49 0 +LFQ 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 25 0 0 0 39 0 +LFQ 118 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 2 0 0 0 0 0 8 0 0 0 0 32 0 0 0 35 0 +LFQ 119 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 25 0 0 0 41 0 +LFQ 120 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 21 0 0 0 46 0 +LFQ 121 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 28 0 0 0 35 0 +LFQ 122 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 21 0 0 0 40 0 +LFQ 123 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 12 0 0 0 0 19 0 0 0 42 0 +LFQ 124 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 15 0 0 0 0 23 0 0 0 35 0 +LFQ 125 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 30 0 0 0 32 0 +LFQ 126 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 4 0 0 0 0 27 0 0 0 41 0 +LFQ 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 26 0 0 0 41 0 +LFQ 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 24 0 0 0 38 0 +LFQ 129 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 8 0 0 0 0 20 0 0 0 41 0 +LFQ 130 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 10 0 0 0 0 31 0 0 0 30 0 +LFQ 131 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 23 0 0 0 36 0 +LFQ 132 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 3 0 0 0 0 0 9 0 0 0 0 21 0 0 0 35 0 +LFQ 133 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 26 0 0 0 36 0 +LFQ 134 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 4 0 0 0 0 0 3 0 0 0 0 28 0 0 0 35 0 +LFQ 135 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 9 0 0 0 0 23 0 0 0 35 0 +LFQ 136 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 26 0 0 0 41 0 +LFQ 137 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 7 0 0 0 0 24 0 0 0 38 0 +LFQ 138 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 20 0 0 0 36 0 +LFQ 139 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 25 0 0 0 38 0 +LFQ 140 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 3 0 0 0 0 0 8 0 0 0 0 19 0 0 0 36 0 +LFQ 141 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 6 0 0 0 0 22 0 0 0 38 0 +LFQ 142 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 9 0 0 0 0 20 0 0 0 35 0 +LFQ 143 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 3 0 0 0 0 0 9 0 0 0 0 17 0 0 0 35 0 +LFQ 144 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 22 0 0 0 38 0 +LFQ 145 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 20 0 0 0 38 0 +LFQ 146 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 7 0 0 0 0 23 0 0 0 35 0 +LFQ 147 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 31 0 0 0 28 0 +LFQ 148 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 9 0 0 0 0 23 0 0 0 28 0 +LFQ 149 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 19 0 0 0 29 0 +LFQ 150 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 23 0 0 0 30 0 +LFQ 151 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 0 0 0 4 0 +# GC Content of first fragments. Use `grep ^GCF | cut -f 2-` to extract this part. +GCF 15.08 0 +GCF 30.40 1 +GCF 31.16 2 +GCF 32.16 0 +GCF 33.17 2 +GCF 33.92 5 +GCF 34.42 4 +GCF 34.92 2 +GCF 35.43 3 +GCF 35.93 7 +GCF 36.43 9 +GCF 36.93 4 +GCF 37.44 7 +GCF 37.94 8 +GCF 38.44 10 +GCF 38.94 7 +GCF 39.70 6 +GCF 40.45 8 +GCF 40.95 9 +GCF 41.71 4 +GCF 42.46 5 +GCF 42.96 7 +GCF 43.72 2 +GCF 44.72 1 +GCF 45.48 3 +GCF 46.48 2 +GCF 47.74 1 +GCF 48.74 2 +GCF 50.25 0 +GCF 52.01 1 +GCF 54.77 0 +GCF 57.54 1 +# GC Content of last fragments. Use `grep ^GCL | cut -f 2-` to extract this part. +GCL 15.08 0 +GCL 30.65 1 +GCL 31.66 0 +GCL 32.41 2 +GCL 32.91 1 +GCL 33.42 3 +GCL 33.92 4 +GCL 34.42 3 +GCL 34.92 4 +GCL 35.68 5 +GCL 36.43 10 +GCL 36.93 8 +GCL 37.44 7 +GCL 37.94 9 +GCL 38.44 10 +GCL 38.94 13 +GCL 39.45 8 +GCL 39.95 7 +GCL 40.45 2 +GCL 40.95 4 +GCL 41.46 3 +GCL 41.96 1 +GCL 42.46 4 +GCL 42.96 6 +GCL 43.47 4 +GCL 44.22 2 +GCL 44.97 4 +GCL 45.48 7 +GCL 45.98 3 +GCL 46.48 2 +GCL 46.98 3 +GCL 47.49 1 +GCL 48.49 0 +GCL 49.75 2 +# ACGT content per cycle. Use `grep ^GCC | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%]; and N and O counts as a percentage of all A/C/G/T bases [%] +GCC 1 19.50 26.50 31.50 22.50 0.00 0.00 +GCC 2 30.50 20.50 17.00 32.00 0.00 0.00 +GCC 3 32.00 15.00 16.50 36.50 0.00 0.00 +GCC 4 30.50 21.00 17.50 31.00 0.00 0.00 +GCC 5 39.50 9.50 12.50 38.50 0.00 0.00 +GCC 6 28.00 17.50 18.50 36.00 0.00 0.00 +GCC 7 29.50 19.50 21.00 30.00 0.00 0.00 +GCC 8 29.50 21.00 23.00 26.50 0.00 0.00 +GCC 9 22.00 32.50 27.00 18.50 0.00 0.00 +GCC 10 36.00 12.00 16.00 36.00 0.00 0.00 +GCC 11 28.00 18.50 20.50 33.00 0.00 0.00 +GCC 12 33.50 21.00 16.00 29.50 0.00 0.00 +GCC 13 28.00 19.00 27.50 25.50 0.00 0.00 +GCC 14 24.50 21.50 19.00 35.00 0.00 0.00 +GCC 15 29.50 16.50 20.00 34.00 0.00 0.00 +GCC 16 31.00 20.00 21.50 27.50 0.00 0.00 +GCC 17 27.50 16.50 19.50 36.50 0.00 0.00 +GCC 18 30.50 24.00 19.50 26.00 0.00 0.00 +GCC 19 23.50 21.50 17.50 37.50 0.00 0.00 +GCC 20 31.50 17.00 21.50 30.00 0.00 0.00 +GCC 21 26.00 22.00 17.50 34.50 0.00 0.00 +GCC 22 30.50 19.00 23.00 27.50 0.00 0.00 +GCC 23 31.50 15.50 22.50 30.50 0.00 0.00 +GCC 24 32.00 18.00 21.00 29.00 0.00 0.00 +GCC 25 27.50 16.50 22.00 34.00 0.00 0.00 +GCC 26 27.50 18.50 23.50 30.50 0.00 0.00 +GCC 27 28.50 19.00 19.50 33.00 0.00 0.00 +GCC 28 22.50 21.00 22.50 34.00 0.00 0.00 +GCC 29 27.00 18.50 22.00 32.50 0.00 0.00 +GCC 30 30.50 20.00 21.50 28.00 0.00 0.00 +GCC 31 24.50 21.00 24.00 30.50 0.00 0.00 +GCC 32 32.50 17.50 16.50 33.50 0.00 0.00 +GCC 33 28.50 16.00 25.00 30.50 0.00 0.00 +GCC 34 29.00 21.00 23.50 26.50 0.00 0.00 +GCC 35 32.50 18.50 21.00 28.00 0.00 0.00 +GCC 36 35.00 12.50 20.00 32.50 0.00 0.00 +GCC 37 26.50 20.00 18.50 35.00 0.00 0.00 +GCC 38 27.00 21.00 19.50 32.50 0.00 0.00 +GCC 39 31.00 20.00 19.00 30.00 0.00 0.00 +GCC 40 27.50 20.00 21.50 31.00 0.00 0.00 +GCC 41 37.00 16.50 19.00 27.50 0.00 0.00 +GCC 42 26.50 19.50 18.50 35.50 0.00 0.00 +GCC 43 33.50 20.00 17.50 29.00 0.00 0.00 +GCC 44 31.50 16.00 21.00 31.50 0.00 0.00 +GCC 45 28.50 19.00 20.00 32.50 0.00 0.00 +GCC 46 24.50 23.50 17.50 34.50 0.00 0.00 +GCC 47 22.50 24.50 19.50 33.50 0.00 0.00 +GCC 48 27.50 17.50 22.50 32.50 0.00 0.00 +GCC 49 28.50 17.00 20.00 34.50 0.00 0.00 +GCC 50 32.00 16.50 20.00 31.50 0.00 0.00 +GCC 51 27.50 20.50 21.00 31.00 0.00 0.00 +GCC 52 27.50 21.50 19.50 31.50 0.00 0.00 +GCC 53 26.00 19.00 25.50 29.50 0.00 0.00 +GCC 54 30.65 23.62 16.58 29.15 0.00 0.00 +GCC 55 29.65 21.61 20.10 28.64 0.00 0.00 +GCC 56 32.16 16.58 22.11 29.15 0.00 0.00 +GCC 57 28.64 20.60 21.11 29.65 0.00 0.00 +GCC 58 29.65 14.57 24.62 31.16 0.00 0.00 +GCC 59 31.16 21.61 17.59 29.65 0.00 0.00 +GCC 60 28.64 17.59 22.11 31.66 0.00 0.00 +GCC 61 25.13 21.61 22.61 30.65 0.00 0.00 +GCC 62 27.14 26.13 21.61 25.13 0.00 0.00 +GCC 63 29.15 14.57 18.59 37.69 0.00 0.00 +GCC 64 29.15 15.08 21.61 34.17 0.00 0.00 +GCC 65 28.64 20.10 19.10 32.16 0.00 0.00 +GCC 66 31.66 19.10 16.08 33.17 0.00 0.00 +GCC 67 24.75 20.20 24.24 30.81 0.00 0.00 +GCC 68 26.77 19.70 23.23 30.30 0.00 0.00 +GCC 69 30.96 17.26 22.84 28.93 0.00 0.00 +GCC 70 33.67 16.84 21.94 27.55 0.00 0.00 +GCC 71 35.20 20.41 18.88 25.51 0.00 0.00 +GCC 72 33.67 15.82 18.88 31.63 0.00 0.00 +GCC 73 32.31 18.46 18.46 30.77 0.00 0.00 +GCC 74 27.69 18.46 24.10 29.74 0.00 0.00 +GCC 75 32.31 14.87 21.54 31.28 0.00 0.00 +GCC 76 24.62 20.00 21.03 34.36 0.00 0.00 +GCC 77 29.74 17.44 17.95 34.87 0.00 0.00 +GCC 78 24.48 20.83 17.19 37.50 0.00 0.00 +GCC 79 33.33 20.83 19.79 26.04 0.00 0.00 +GCC 80 31.05 16.32 22.11 30.53 0.00 0.00 +GCC 81 33.33 15.87 15.34 35.45 0.00 0.00 +GCC 82 31.75 19.58 19.58 29.10 0.00 0.00 +GCC 83 30.32 21.81 18.62 29.26 0.00 0.00 +GCC 84 27.66 21.81 15.96 34.57 0.00 0.00 +GCC 85 26.06 15.43 22.34 36.17 0.00 0.00 +GCC 86 25.00 18.09 21.81 35.11 0.00 0.00 +GCC 87 30.85 18.09 15.43 35.64 0.00 0.00 +GCC 88 32.45 25.00 18.09 24.47 0.00 0.00 +GCC 89 24.47 15.43 19.68 40.43 0.00 0.00 +GCC 90 27.27 21.93 20.86 29.95 0.00 0.00 +GCC 91 28.34 14.97 20.86 35.83 0.00 0.00 +GCC 92 28.34 18.18 20.32 33.16 0.00 0.00 +GCC 93 28.65 18.38 18.38 34.59 0.00 0.00 +GCC 94 29.19 17.84 20.54 32.43 0.00 0.00 +GCC 95 27.72 23.91 21.20 27.17 0.00 0.00 +GCC 96 31.32 18.68 16.48 33.52 0.00 0.00 +GCC 97 21.98 17.58 21.43 39.01 0.00 0.00 +GCC 98 27.47 15.93 18.68 37.91 0.00 0.00 +GCC 99 27.53 20.22 17.98 34.27 0.00 0.00 +GCC 100 34.83 15.17 19.66 30.34 0.00 0.00 +GCC 101 36.52 16.85 20.22 26.40 0.00 0.00 +GCC 102 29.55 22.16 23.30 25.00 0.00 0.00 +GCC 103 27.84 18.75 19.32 34.09 0.00 0.00 +GCC 104 26.14 14.77 22.16 36.93 0.00 0.00 +GCC 105 33.52 11.36 19.89 35.23 0.00 0.00 +GCC 106 28.00 20.00 19.43 32.57 0.00 0.00 +GCC 107 25.88 16.47 24.12 33.53 0.00 0.00 +GCC 108 30.77 20.71 15.98 32.54 0.00 0.00 +GCC 109 26.63 30.18 16.57 26.63 0.00 0.00 +GCC 110 27.81 9.47 23.67 39.05 0.00 0.00 +GCC 111 30.18 16.57 23.67 29.59 0.00 0.00 +GCC 112 28.40 21.30 24.85 25.44 0.00 0.00 +GCC 113 28.57 19.64 22.02 29.76 0.00 0.00 +GCC 114 31.55 23.21 17.86 27.38 0.00 0.00 +GCC 115 35.12 19.64 15.48 29.76 0.00 0.00 +GCC 116 26.79 17.86 22.62 32.74 0.00 0.00 +GCC 117 34.73 22.75 14.37 28.14 0.00 0.00 +GCC 118 27.11 23.49 15.06 34.34 0.00 0.00 +GCC 119 31.93 19.28 20.48 28.31 0.00 0.00 +GCC 120 35.15 16.97 18.18 29.70 0.00 0.00 +GCC 121 26.67 24.85 18.18 30.30 0.00 0.00 +GCC 122 33.94 17.58 19.39 29.09 0.00 0.00 +GCC 123 29.45 19.63 18.40 32.52 0.00 0.00 +GCC 124 24.54 22.09 23.31 30.06 0.00 0.00 +GCC 125 28.22 17.18 20.86 33.74 0.00 0.00 +GCC 126 40.99 17.39 16.15 25.47 0.00 0.00 +GCC 127 28.75 18.12 19.38 33.75 0.00 0.00 +GCC 128 25.16 22.01 20.13 32.70 0.00 0.00 +GCC 129 23.27 16.98 23.27 36.48 0.00 0.00 +GCC 130 33.12 12.74 24.20 29.94 0.00 0.00 +GCC 131 25.48 16.56 21.66 36.31 0.00 0.00 +GCC 132 31.21 19.11 22.29 27.39 0.00 0.00 +GCC 133 30.97 19.35 19.35 30.32 0.00 0.00 +GCC 134 32.90 14.84 23.23 29.03 0.00 0.00 +GCC 135 32.26 18.71 18.06 30.97 0.00 0.00 +GCC 136 34.19 19.35 22.58 23.87 0.00 0.00 +GCC 137 27.27 18.18 20.13 34.42 0.00 0.00 +GCC 138 30.52 18.18 17.53 33.77 0.00 0.00 +GCC 139 26.62 22.08 19.48 31.82 0.00 0.00 +GCC 140 27.81 24.50 19.87 27.81 0.00 0.00 +GCC 141 28.00 23.33 21.33 27.33 0.00 0.00 +GCC 142 29.53 15.44 28.19 26.85 0.00 0.00 +GCC 143 24.66 15.07 23.97 36.30 0.00 0.00 +GCC 144 27.40 16.44 19.86 36.30 0.00 0.00 +GCC 145 29.45 13.70 19.86 36.99 0.00 0.00 +GCC 146 35.86 12.41 18.62 33.10 0.00 0.00 +GCC 147 32.87 20.98 16.08 30.07 0.00 0.00 +GCC 148 31.11 20.74 23.70 24.44 0.00 0.00 +GCC 149 33.07 14.96 19.69 32.28 0.00 0.00 +GCC 150 36.94 14.41 14.41 34.23 0.00 0.00 +GCC 151 40.82 18.37 14.29 26.53 0.00 0.00 +# ACGT content per cycle, read oriented. Use `grep ^GCT | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%] +GCT 1 22.50 26.00 32.00 19.50 +GCT 2 20.00 21.50 16.00 42.50 +GCT 3 30.00 16.50 15.00 38.50 +GCT 4 21.50 26.50 12.00 40.00 +GCT 5 44.50 10.00 12.00 33.50 +GCT 6 42.50 13.50 22.50 21.50 +GCT 7 34.50 17.00 23.50 25.00 +GCT 8 37.50 22.50 21.50 18.50 +GCT 9 17.00 39.00 20.50 23.50 +GCT 10 33.00 14.50 13.50 39.00 +GCT 11 34.50 12.50 26.50 26.50 +GCT 12 27.50 14.50 22.50 35.50 +GCT 13 21.50 22.00 24.50 32.00 +GCT 14 28.00 27.50 13.00 31.50 +GCT 15 35.00 15.50 21.00 28.50 +GCT 16 36.50 24.00 17.50 22.00 +GCT 17 36.50 18.00 18.00 27.50 +GCT 18 29.50 23.50 20.00 27.00 +GCT 19 30.00 17.50 21.50 31.00 +GCT 20 30.00 19.00 19.50 31.50 +GCT 21 25.50 20.00 19.50 35.00 +GCT 22 29.00 23.00 19.00 29.00 +GCT 23 30.50 21.00 17.00 31.50 +GCT 24 30.50 22.00 17.00 30.50 +GCT 25 28.50 19.00 19.50 33.00 +GCT 26 27.50 19.00 23.00 30.50 +GCT 27 33.50 21.50 17.00 28.00 +GCT 28 28.50 23.50 20.00 28.00 +GCT 29 32.00 21.00 19.50 27.50 +GCT 30 30.50 20.50 21.00 28.00 +GCT 31 25.00 24.00 21.00 30.00 +GCT 32 37.00 17.50 16.50 29.00 +GCT 33 27.00 19.00 22.00 32.00 +GCT 34 29.50 22.00 22.50 26.00 +GCT 35 29.00 19.50 20.00 31.50 +GCT 36 37.50 17.50 15.00 30.00 +GCT 37 32.50 21.50 17.00 29.00 +GCT 38 30.00 20.50 20.00 29.50 +GCT 39 34.00 20.50 18.50 27.00 +GCT 40 27.00 22.00 19.50 31.50 +GCT 41 32.00 20.00 15.50 32.50 +GCT 42 37.50 17.00 21.00 24.50 +GCT 43 25.50 19.50 18.00 37.00 +GCT 44 31.50 18.50 18.50 31.50 +GCT 45 27.00 20.00 19.00 34.00 +GCT 46 29.00 20.50 20.50 30.00 +GCT 47 29.00 20.50 23.50 27.00 +GCT 48 27.00 21.50 18.50 33.00 +GCT 49 27.00 17.00 20.00 36.00 +GCT 50 29.00 21.00 15.50 34.50 +GCT 51 33.00 21.50 20.00 25.50 +GCT 52 30.50 21.00 20.00 28.50 +GCT 53 24.50 23.00 21.50 31.00 +GCT 54 30.15 20.60 19.60 29.65 +GCT 55 25.13 20.60 21.11 33.17 +GCT 56 26.13 21.11 17.59 35.18 +GCT 57 27.14 20.60 21.11 31.16 +GCT 58 30.15 17.59 21.61 30.65 +GCT 59 32.66 20.60 18.59 28.14 +GCT 60 31.66 18.09 21.61 28.64 +GCT 61 25.13 23.12 21.11 30.65 +GCT 62 24.62 23.12 24.62 27.64 +GCT 63 36.68 17.59 15.58 30.15 +GCT 64 35.18 16.58 20.10 28.14 +GCT 65 30.65 18.59 20.60 30.15 +GCT 66 34.67 15.58 19.60 30.15 +GCT 67 29.29 24.75 19.70 26.26 +GCT 68 28.28 21.21 21.72 28.79 +GCT 69 29.44 22.84 17.26 30.46 +GCT 70 36.22 19.90 18.88 25.00 +GCT 71 34.18 20.92 18.37 26.53 +GCT 72 32.14 17.86 16.84 33.16 +GCT 73 32.82 14.36 22.56 30.26 +GCT 74 30.26 21.54 21.03 27.18 +GCT 75 33.33 18.46 17.95 30.26 +GCT 76 29.23 23.08 17.95 29.74 +GCT 77 29.74 17.95 17.44 34.87 +GCT 78 31.25 20.83 17.19 30.73 +GCT 79 29.17 23.44 17.19 30.21 +GCT 80 35.79 21.05 17.37 25.79 +GCT 81 39.68 20.11 11.11 29.10 +GCT 82 28.04 16.93 22.22 32.80 +GCT 83 29.26 20.21 20.21 30.32 +GCT 84 35.11 18.09 19.68 27.13 +GCT 85 28.72 20.74 17.02 33.51 +GCT 86 29.79 21.28 18.62 30.32 +GCT 87 31.38 18.09 15.43 35.11 +GCT 88 28.72 21.81 21.28 28.19 +GCT 89 30.32 18.62 16.49 34.57 +GCT 90 29.95 13.90 28.88 27.27 +GCT 91 32.09 15.51 20.32 32.09 +GCT 92 26.20 18.18 20.32 35.29 +GCT 93 31.35 18.38 18.38 31.89 +GCT 94 29.73 15.68 22.70 31.89 +GCT 95 28.80 19.57 25.54 26.09 +GCT 96 32.42 20.33 14.84 32.42 +GCT 97 31.87 21.43 17.58 29.12 +GCT 98 30.77 14.29 20.33 34.62 +GCT 99 28.65 17.42 20.79 33.15 +GCT 100 28.65 14.04 20.79 36.52 +GCT 101 27.53 23.03 14.04 35.39 +GCT 102 26.70 17.05 28.41 27.84 +GCT 103 29.55 20.45 17.61 32.39 +GCT 104 34.66 22.16 14.77 28.41 +GCT 105 40.91 13.07 18.18 27.84 +GCT 106 24.57 20.57 18.86 36.00 +GCT 107 26.47 18.24 22.35 32.94 +GCT 108 31.95 17.16 19.53 31.36 +GCT 109 26.04 24.85 21.89 27.22 +GCT 110 32.54 17.75 15.38 34.32 +GCT 111 26.63 17.75 22.49 33.14 +GCT 112 27.81 23.08 23.08 26.04 +GCT 113 35.12 16.67 25.00 23.21 +GCT 114 30.95 21.43 19.64 27.98 +GCT 115 29.17 18.45 16.67 35.71 +GCT 116 30.36 17.86 22.62 29.17 +GCT 117 27.54 21.56 15.57 35.33 +GCT 118 33.13 22.89 15.66 28.31 +GCT 119 33.73 16.87 22.89 26.51 +GCT 120 26.67 13.94 21.21 38.18 +GCT 121 29.09 18.18 24.85 27.88 +GCT 122 27.27 21.21 15.76 35.76 +GCT 123 30.06 17.79 20.25 31.90 +GCT 124 28.22 22.09 23.31 26.38 +GCT 125 27.61 20.25 17.79 34.36 +GCT 126 31.06 16.77 16.77 35.40 +GCT 127 32.50 15.00 22.50 30.00 +GCT 128 25.79 18.87 23.27 32.08 +GCT 129 28.30 20.75 19.50 31.45 +GCT 130 33.12 18.47 18.47 29.94 +GCT 131 31.85 19.75 18.47 29.94 +GCT 132 30.57 22.93 18.47 28.03 +GCT 133 29.68 18.06 20.65 31.61 +GCT 134 30.97 23.23 14.84 30.97 +GCT 135 32.90 16.77 20.00 30.32 +GCT 136 29.03 19.35 22.58 29.03 +GCT 137 27.92 24.68 13.64 33.77 +GCT 138 35.06 16.88 18.83 29.22 +GCT 139 33.12 22.73 18.83 25.32 +GCT 140 34.44 22.52 21.85 21.19 +GCT 141 25.33 22.67 22.00 30.00 +GCT 142 31.54 21.48 22.15 24.83 +GCT 143 35.62 20.55 18.49 25.34 +GCT 144 25.34 14.38 21.92 38.36 +GCT 145 35.62 15.75 17.81 30.82 +GCT 146 33.79 14.48 16.55 35.17 +GCT 147 32.17 20.98 16.08 30.77 +GCT 148 26.67 23.70 20.74 28.89 +GCT 149 40.16 16.54 18.11 25.20 +GCT 150 33.33 9.91 18.92 37.84 +GCT 151 24.49 0.00 32.65 42.86 +# ACGT content per cycle for first fragments. Use `grep ^FBC | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%]; and N and O counts as a percentage of all A/C/G/T bases [%] +FBC 1 20.00 26.00 32.00 22.00 0.00 0.00 +FBC 2 34.00 16.00 18.00 32.00 0.00 0.00 +FBC 3 35.00 17.00 16.00 32.00 0.00 0.00 +FBC 4 27.00 22.00 22.00 29.00 0.00 0.00 +FBC 5 33.00 10.00 14.00 43.00 0.00 0.00 +FBC 6 30.00 18.00 13.00 39.00 0.00 0.00 +FBC 7 27.00 22.00 21.00 30.00 0.00 0.00 +FBC 8 35.00 20.00 20.00 25.00 0.00 0.00 +FBC 9 23.00 34.00 23.00 20.00 0.00 0.00 +FBC 10 33.00 13.00 14.00 40.00 0.00 0.00 +FBC 11 33.00 17.00 21.00 29.00 0.00 0.00 +FBC 12 35.00 21.00 11.00 33.00 0.00 0.00 +FBC 13 31.00 20.00 21.00 28.00 0.00 0.00 +FBC 14 26.00 23.00 21.00 30.00 0.00 0.00 +FBC 15 25.00 24.00 18.00 33.00 0.00 0.00 +FBC 16 32.00 24.00 23.00 21.00 0.00 0.00 +FBC 17 27.00 13.00 21.00 39.00 0.00 0.00 +FBC 18 26.00 28.00 15.00 31.00 0.00 0.00 +FBC 19 24.00 18.00 19.00 39.00 0.00 0.00 +FBC 20 29.00 16.00 22.00 33.00 0.00 0.00 +FBC 21 21.00 20.00 13.00 46.00 0.00 0.00 +FBC 22 32.00 17.00 21.00 30.00 0.00 0.00 +FBC 23 33.00 13.00 24.00 30.00 0.00 0.00 +FBC 24 34.00 16.00 17.00 33.00 0.00 0.00 +FBC 25 27.00 18.00 22.00 33.00 0.00 0.00 +FBC 26 31.00 15.00 23.00 31.00 0.00 0.00 +FBC 27 29.00 18.00 20.00 33.00 0.00 0.00 +FBC 28 23.00 21.00 20.00 36.00 0.00 0.00 +FBC 29 26.00 14.00 24.00 36.00 0.00 0.00 +FBC 30 26.00 21.00 23.00 30.00 0.00 0.00 +FBC 31 25.00 19.00 22.00 34.00 0.00 0.00 +FBC 32 30.00 21.00 15.00 34.00 0.00 0.00 +FBC 33 31.00 16.00 22.00 31.00 0.00 0.00 +FBC 34 29.00 19.00 22.00 30.00 0.00 0.00 +FBC 35 38.00 13.00 27.00 22.00 0.00 0.00 +FBC 36 33.00 13.00 20.00 34.00 0.00 0.00 +FBC 37 32.00 14.00 18.00 36.00 0.00 0.00 +FBC 38 31.00 22.00 17.00 30.00 0.00 0.00 +FBC 39 32.00 18.00 16.00 34.00 0.00 0.00 +FBC 40 28.00 23.00 20.00 29.00 0.00 0.00 +FBC 41 41.00 14.00 16.00 29.00 0.00 0.00 +FBC 42 27.00 20.00 21.00 32.00 0.00 0.00 +FBC 43 35.00 23.00 14.00 28.00 0.00 0.00 +FBC 44 33.00 14.00 18.00 35.00 0.00 0.00 +FBC 45 30.00 18.00 19.00 33.00 0.00 0.00 +FBC 46 26.00 22.00 24.00 28.00 0.00 0.00 +FBC 47 25.00 26.00 22.00 27.00 0.00 0.00 +FBC 48 27.00 15.00 24.00 34.00 0.00 0.00 +FBC 49 23.00 20.00 21.00 36.00 0.00 0.00 +FBC 50 30.00 14.00 26.00 30.00 0.00 0.00 +FBC 51 32.00 15.00 15.00 38.00 0.00 0.00 +FBC 52 31.00 20.00 19.00 30.00 0.00 0.00 +FBC 53 28.00 17.00 28.00 27.00 0.00 0.00 +FBC 54 28.00 24.00 21.00 27.00 0.00 0.00 +FBC 55 23.00 25.00 20.00 32.00 0.00 0.00 +FBC 56 31.00 19.00 22.00 28.00 0.00 0.00 +FBC 57 33.00 19.00 18.00 30.00 0.00 0.00 +FBC 58 34.00 16.00 25.00 25.00 0.00 0.00 +FBC 59 35.00 22.00 17.00 26.00 0.00 0.00 +FBC 60 24.00 22.00 24.00 30.00 0.00 0.00 +FBC 61 22.00 25.00 27.00 26.00 0.00 0.00 +FBC 62 23.00 30.00 20.00 27.00 0.00 0.00 +FBC 63 30.00 10.00 22.00 38.00 0.00 0.00 +FBC 64 25.00 17.00 20.00 38.00 0.00 0.00 +FBC 65 25.00 24.00 21.00 30.00 0.00 0.00 +FBC 66 33.00 12.00 19.00 36.00 0.00 0.00 +FBC 67 23.00 22.00 19.00 36.00 0.00 0.00 +FBC 68 23.00 21.00 25.00 31.00 0.00 0.00 +FBC 69 31.00 17.00 24.00 28.00 0.00 0.00 +FBC 70 31.00 18.00 27.00 24.00 0.00 0.00 +FBC 71 42.00 17.00 15.00 26.00 0.00 0.00 +FBC 72 34.00 15.00 23.00 28.00 0.00 0.00 +FBC 73 31.31 23.23 19.19 26.26 0.00 0.00 +FBC 74 21.21 22.22 26.26 30.30 0.00 0.00 +FBC 75 32.32 15.15 20.20 32.32 0.00 0.00 +FBC 76 29.29 13.13 17.17 40.40 0.00 0.00 +FBC 77 26.26 18.18 21.21 34.34 0.00 0.00 +FBC 78 28.87 17.53 22.68 30.93 0.00 0.00 +FBC 79 32.99 20.62 20.62 25.77 0.00 0.00 +FBC 80 29.47 16.84 26.32 27.37 0.00 0.00 +FBC 81 32.98 12.77 12.77 41.49 0.00 0.00 +FBC 82 37.23 20.21 21.28 21.28 0.00 0.00 +FBC 83 31.91 23.40 18.09 26.60 0.00 0.00 +FBC 84 24.47 23.40 14.89 37.23 0.00 0.00 +FBC 85 36.17 18.09 20.21 25.53 0.00 0.00 +FBC 86 25.53 19.15 20.21 35.11 0.00 0.00 +FBC 87 29.79 18.09 13.83 38.30 0.00 0.00 +FBC 88 32.98 28.72 15.96 22.34 0.00 0.00 +FBC 89 24.47 20.21 15.96 39.36 0.00 0.00 +FBC 90 31.18 19.35 13.98 35.48 0.00 0.00 +FBC 91 25.81 19.35 18.28 36.56 0.00 0.00 +FBC 92 30.11 18.28 18.28 33.33 0.00 0.00 +FBC 93 28.26 13.04 20.65 38.04 0.00 0.00 +FBC 94 31.52 18.48 20.65 29.35 0.00 0.00 +FBC 95 26.37 21.98 21.98 29.67 0.00 0.00 +FBC 96 24.44 17.78 23.33 34.44 0.00 0.00 +FBC 97 17.78 17.78 21.11 43.33 0.00 0.00 +FBC 98 26.67 13.33 14.44 45.56 0.00 0.00 +FBC 99 27.27 20.45 19.32 32.95 0.00 0.00 +FBC 100 36.36 13.64 22.73 27.27 0.00 0.00 +FBC 101 40.91 15.91 17.05 26.14 0.00 0.00 +FBC 102 28.41 23.86 22.73 25.00 0.00 0.00 +FBC 103 30.68 19.32 18.18 31.82 0.00 0.00 +FBC 104 18.18 18.18 25.00 38.64 0.00 0.00 +FBC 105 30.68 10.23 19.32 39.77 0.00 0.00 +FBC 106 36.36 15.91 21.59 26.14 0.00 0.00 +FBC 107 25.58 15.12 19.77 39.53 0.00 0.00 +FBC 108 32.94 18.82 12.94 35.29 0.00 0.00 +FBC 109 28.24 29.41 17.65 24.71 0.00 0.00 +FBC 110 28.24 10.59 24.71 36.47 0.00 0.00 +FBC 111 34.12 14.12 25.88 25.88 0.00 0.00 +FBC 112 23.53 21.18 28.24 27.06 0.00 0.00 +FBC 113 21.18 21.18 23.53 34.12 0.00 0.00 +FBC 114 23.53 23.53 16.47 36.47 0.00 0.00 +FBC 115 30.59 27.06 12.94 29.41 0.00 0.00 +FBC 116 24.71 15.29 29.41 30.59 0.00 0.00 +FBC 117 29.41 27.06 12.94 30.59 0.00 0.00 +FBC 118 24.71 27.06 15.29 32.94 0.00 0.00 +FBC 119 27.06 22.35 22.35 28.24 0.00 0.00 +FBC 120 36.90 20.24 14.29 28.57 0.00 0.00 +FBC 121 33.33 20.24 15.48 30.95 0.00 0.00 +FBC 122 35.71 20.24 14.29 29.76 0.00 0.00 +FBC 123 24.10 25.30 16.87 33.73 0.00 0.00 +FBC 124 27.71 24.10 19.28 28.92 0.00 0.00 +FBC 125 26.51 16.87 19.28 37.35 0.00 0.00 +FBC 126 41.46 15.85 13.41 29.27 0.00 0.00 +FBC 127 28.05 18.29 24.39 29.27 0.00 0.00 +FBC 128 20.99 20.99 22.22 35.80 0.00 0.00 +FBC 129 22.22 13.58 22.22 41.98 0.00 0.00 +FBC 130 32.50 10.00 26.25 31.25 0.00 0.00 +FBC 131 26.25 15.00 26.25 32.50 0.00 0.00 +FBC 132 30.00 18.75 21.25 30.00 0.00 0.00 +FBC 133 32.91 20.25 17.72 29.11 0.00 0.00 +FBC 134 29.11 15.19 25.32 30.38 0.00 0.00 +FBC 135 31.65 18.99 18.99 30.38 0.00 0.00 +FBC 136 34.18 18.99 25.32 21.52 0.00 0.00 +FBC 137 29.11 10.13 25.32 35.44 0.00 0.00 +FBC 138 25.32 24.05 17.72 32.91 0.00 0.00 +FBC 139 25.32 25.32 18.99 30.38 0.00 0.00 +FBC 140 29.87 24.68 19.48 25.97 0.00 0.00 +FBC 141 29.87 22.08 18.18 29.87 0.00 0.00 +FBC 142 27.63 15.79 30.26 26.32 0.00 0.00 +FBC 143 27.03 18.92 24.32 29.73 0.00 0.00 +FBC 144 28.38 18.92 18.92 33.78 0.00 0.00 +FBC 145 32.43 16.22 14.86 36.49 0.00 0.00 +FBC 146 36.49 13.51 16.22 33.78 0.00 0.00 +FBC 147 34.72 22.22 13.89 29.17 0.00 0.00 +FBC 148 26.87 20.90 26.87 25.37 0.00 0.00 +FBC 149 31.25 12.50 25.00 31.25 0.00 0.00 +FBC 150 32.73 16.36 10.91 40.00 0.00 0.00 +FBC 151 48.28 17.24 13.79 20.69 0.00 0.00 +# ACGT raw counters for first fragments. Use `grep ^FTC | cut -f 2-` to extract this part. The columns are: A,C,G,T,N base counters +FTC 4077 2634 2796 4390 0 +# ACGT content per cycle for last fragments. Use `grep ^LBC | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%]; and N and O counts as a percentage of all A/C/G/T bases [%] +LBC 1 19.00 27.00 31.00 23.00 0.00 0.00 +LBC 2 27.00 25.00 16.00 32.00 0.00 0.00 +LBC 3 29.00 13.00 17.00 41.00 0.00 0.00 +LBC 4 34.00 20.00 13.00 33.00 0.00 0.00 +LBC 5 46.00 9.00 11.00 34.00 0.00 0.00 +LBC 6 26.00 17.00 24.00 33.00 0.00 0.00 +LBC 7 32.00 17.00 21.00 30.00 0.00 0.00 +LBC 8 24.00 22.00 26.00 28.00 0.00 0.00 +LBC 9 21.00 31.00 31.00 17.00 0.00 0.00 +LBC 10 39.00 11.00 18.00 32.00 0.00 0.00 +LBC 11 23.00 20.00 20.00 37.00 0.00 0.00 +LBC 12 32.00 21.00 21.00 26.00 0.00 0.00 +LBC 13 25.00 18.00 34.00 23.00 0.00 0.00 +LBC 14 23.00 20.00 17.00 40.00 0.00 0.00 +LBC 15 34.00 9.00 22.00 35.00 0.00 0.00 +LBC 16 30.00 16.00 20.00 34.00 0.00 0.00 +LBC 17 28.00 20.00 18.00 34.00 0.00 0.00 +LBC 18 35.00 20.00 24.00 21.00 0.00 0.00 +LBC 19 23.00 25.00 16.00 36.00 0.00 0.00 +LBC 20 34.00 18.00 21.00 27.00 0.00 0.00 +LBC 21 31.00 24.00 22.00 23.00 0.00 0.00 +LBC 22 29.00 21.00 25.00 25.00 0.00 0.00 +LBC 23 30.00 18.00 21.00 31.00 0.00 0.00 +LBC 24 30.00 20.00 25.00 25.00 0.00 0.00 +LBC 25 28.00 15.00 22.00 35.00 0.00 0.00 +LBC 26 24.00 22.00 24.00 30.00 0.00 0.00 +LBC 27 28.00 20.00 19.00 33.00 0.00 0.00 +LBC 28 22.00 21.00 25.00 32.00 0.00 0.00 +LBC 29 28.00 23.00 20.00 29.00 0.00 0.00 +LBC 30 35.00 19.00 20.00 26.00 0.00 0.00 +LBC 31 24.00 23.00 26.00 27.00 0.00 0.00 +LBC 32 35.00 14.00 18.00 33.00 0.00 0.00 +LBC 33 26.00 16.00 28.00 30.00 0.00 0.00 +LBC 34 29.00 23.00 25.00 23.00 0.00 0.00 +LBC 35 27.00 24.00 15.00 34.00 0.00 0.00 +LBC 36 37.00 12.00 20.00 31.00 0.00 0.00 +LBC 37 21.00 26.00 19.00 34.00 0.00 0.00 +LBC 38 23.00 20.00 22.00 35.00 0.00 0.00 +LBC 39 30.00 22.00 22.00 26.00 0.00 0.00 +LBC 40 27.00 17.00 23.00 33.00 0.00 0.00 +LBC 41 33.00 19.00 22.00 26.00 0.00 0.00 +LBC 42 26.00 19.00 16.00 39.00 0.00 0.00 +LBC 43 32.00 17.00 21.00 30.00 0.00 0.00 +LBC 44 30.00 18.00 24.00 28.00 0.00 0.00 +LBC 45 27.00 20.00 21.00 32.00 0.00 0.00 +LBC 46 23.00 25.00 11.00 41.00 0.00 0.00 +LBC 47 20.00 23.00 17.00 40.00 0.00 0.00 +LBC 48 28.00 20.00 21.00 31.00 0.00 0.00 +LBC 49 34.00 14.00 19.00 33.00 0.00 0.00 +LBC 50 34.00 19.00 14.00 33.00 0.00 0.00 +LBC 51 23.00 26.00 27.00 24.00 0.00 0.00 +LBC 52 24.00 23.00 20.00 33.00 0.00 0.00 +LBC 53 24.00 21.00 23.00 32.00 0.00 0.00 +LBC 54 33.33 23.23 12.12 31.31 0.00 0.00 +LBC 55 36.36 18.18 20.20 25.25 0.00 0.00 +LBC 56 33.33 14.14 22.22 30.30 0.00 0.00 +LBC 57 24.24 22.22 24.24 29.29 0.00 0.00 +LBC 58 25.25 13.13 24.24 37.37 0.00 0.00 +LBC 59 27.27 21.21 18.18 33.33 0.00 0.00 +LBC 60 33.33 13.13 20.20 33.33 0.00 0.00 +LBC 61 28.28 18.18 18.18 35.35 0.00 0.00 +LBC 62 31.31 22.22 23.23 23.23 0.00 0.00 +LBC 63 28.28 19.19 15.15 37.37 0.00 0.00 +LBC 64 33.33 13.13 23.23 30.30 0.00 0.00 +LBC 65 32.32 16.16 17.17 34.34 0.00 0.00 +LBC 66 30.30 26.26 13.13 30.30 0.00 0.00 +LBC 67 26.53 18.37 29.59 25.51 0.00 0.00 +LBC 68 30.61 18.37 21.43 29.59 0.00 0.00 +LBC 69 30.93 17.53 21.65 29.90 0.00 0.00 +LBC 70 36.46 15.62 16.67 31.25 0.00 0.00 +LBC 71 28.12 23.96 22.92 25.00 0.00 0.00 +LBC 72 33.33 16.67 14.58 35.42 0.00 0.00 +LBC 73 33.33 13.54 17.71 35.42 0.00 0.00 +LBC 74 34.38 14.58 21.88 29.17 0.00 0.00 +LBC 75 32.29 14.58 22.92 30.21 0.00 0.00 +LBC 76 19.79 27.08 25.00 28.12 0.00 0.00 +LBC 77 33.33 16.67 14.58 35.42 0.00 0.00 +LBC 78 20.00 24.21 11.58 44.21 0.00 0.00 +LBC 79 33.68 21.05 18.95 26.32 0.00 0.00 +LBC 80 32.63 15.79 17.89 33.68 0.00 0.00 +LBC 81 33.68 18.95 17.89 29.47 0.00 0.00 +LBC 82 26.32 18.95 17.89 36.84 0.00 0.00 +LBC 83 28.72 20.21 19.15 31.91 0.00 0.00 +LBC 84 30.85 20.21 17.02 31.91 0.00 0.00 +LBC 85 15.96 12.77 24.47 46.81 0.00 0.00 +LBC 86 24.47 17.02 23.40 35.11 0.00 0.00 +LBC 87 31.91 18.09 17.02 32.98 0.00 0.00 +LBC 88 31.91 21.28 20.21 26.60 0.00 0.00 +LBC 89 24.47 10.64 23.40 41.49 0.00 0.00 +LBC 90 23.40 24.47 27.66 24.47 0.00 0.00 +LBC 91 30.85 10.64 23.40 35.11 0.00 0.00 +LBC 92 26.60 18.09 22.34 32.98 0.00 0.00 +LBC 93 29.03 23.66 16.13 31.18 0.00 0.00 +LBC 94 26.88 17.20 20.43 35.48 0.00 0.00 +LBC 95 29.03 25.81 20.43 24.73 0.00 0.00 +LBC 96 38.04 19.57 9.78 32.61 0.00 0.00 +LBC 97 26.09 17.39 21.74 34.78 0.00 0.00 +LBC 98 28.26 18.48 22.83 30.43 0.00 0.00 +LBC 99 27.78 20.00 16.67 35.56 0.00 0.00 +LBC 100 33.33 16.67 16.67 33.33 0.00 0.00 +LBC 101 32.22 17.78 23.33 26.67 0.00 0.00 +LBC 102 30.68 20.45 23.86 25.00 0.00 0.00 +LBC 103 25.00 18.18 20.45 36.36 0.00 0.00 +LBC 104 34.09 11.36 19.32 35.23 0.00 0.00 +LBC 105 36.36 12.50 20.45 30.68 0.00 0.00 +LBC 106 19.54 24.14 17.24 39.08 0.00 0.00 +LBC 107 26.19 17.86 28.57 27.38 0.00 0.00 +LBC 108 28.57 22.62 19.05 29.76 0.00 0.00 +LBC 109 25.00 30.95 15.48 28.57 0.00 0.00 +LBC 110 27.38 8.33 22.62 41.67 0.00 0.00 +LBC 111 26.19 19.05 21.43 33.33 0.00 0.00 +LBC 112 33.33 21.43 21.43 23.81 0.00 0.00 +LBC 113 36.14 18.07 20.48 25.30 0.00 0.00 +LBC 114 39.76 22.89 19.28 18.07 0.00 0.00 +LBC 115 39.76 12.05 18.07 30.12 0.00 0.00 +LBC 116 28.92 20.48 15.66 34.94 0.00 0.00 +LBC 117 40.24 18.29 15.85 25.61 0.00 0.00 +LBC 118 29.63 19.75 14.81 35.80 0.00 0.00 +LBC 119 37.04 16.05 18.52 28.40 0.00 0.00 +LBC 120 33.33 13.58 22.22 30.86 0.00 0.00 +LBC 121 19.75 29.63 20.99 29.63 0.00 0.00 +LBC 122 32.10 14.81 24.69 28.40 0.00 0.00 +LBC 123 35.00 13.75 20.00 31.25 0.00 0.00 +LBC 124 21.25 20.00 27.50 31.25 0.00 0.00 +LBC 125 30.00 17.50 22.50 30.00 0.00 0.00 +LBC 126 40.51 18.99 18.99 21.52 0.00 0.00 +LBC 127 29.49 17.95 14.10 38.46 0.00 0.00 +LBC 128 29.49 23.08 17.95 29.49 0.00 0.00 +LBC 129 24.36 20.51 24.36 30.77 0.00 0.00 +LBC 130 33.77 15.58 22.08 28.57 0.00 0.00 +LBC 131 24.68 18.18 16.88 40.26 0.00 0.00 +LBC 132 32.47 19.48 23.38 24.68 0.00 0.00 +LBC 133 28.95 18.42 21.05 31.58 0.00 0.00 +LBC 134 36.84 14.47 21.05 27.63 0.00 0.00 +LBC 135 32.89 18.42 17.11 31.58 0.00 0.00 +LBC 136 34.21 19.74 19.74 26.32 0.00 0.00 +LBC 137 25.33 26.67 14.67 33.33 0.00 0.00 +LBC 138 36.00 12.00 17.33 34.67 0.00 0.00 +LBC 139 28.00 18.67 20.00 33.33 0.00 0.00 +LBC 140 25.68 24.32 20.27 29.73 0.00 0.00 +LBC 141 26.03 24.66 24.66 24.66 0.00 0.00 +LBC 142 31.51 15.07 26.03 27.40 0.00 0.00 +LBC 143 22.22 11.11 23.61 43.06 0.00 0.00 +LBC 144 26.39 13.89 20.83 38.89 0.00 0.00 +LBC 145 26.39 11.11 25.00 37.50 0.00 0.00 +LBC 146 35.21 11.27 21.13 32.39 0.00 0.00 +LBC 147 30.99 19.72 18.31 30.99 0.00 0.00 +LBC 148 35.29 20.59 20.59 23.53 0.00 0.00 +LBC 149 34.92 17.46 14.29 33.33 0.00 0.00 +LBC 150 41.07 12.50 17.86 28.57 0.00 0.00 +LBC 151 30.00 20.00 15.00 35.00 0.00 0.00 +# ACGT raw counters for last fragments. Use `grep ^LTC | cut -f 2-` to extract this part. The columns are: A,C,G,T,N base counters +LTC 4051 2592 2808 4297 0 +# Insert sizes. Use `grep ^IS | cut -f 2-` to extract this part. The columns are: insert size, pairs total, inward oriented pairs, outward oriented pairs, other pairs +IS 0 0 0 0 0 +IS 1 0 0 0 0 +IS 2 0 0 0 0 +IS 3 0 0 0 0 +IS 4 0 0 0 0 +IS 5 0 0 0 0 +IS 6 0 0 0 0 +IS 7 0 0 0 0 +IS 8 0 0 0 0 +IS 9 0 0 0 0 +IS 10 0 0 0 0 +IS 11 0 0 0 0 +IS 12 0 0 0 0 +IS 13 0 0 0 0 +IS 14 0 0 0 0 +IS 15 0 0 0 0 +IS 16 0 0 0 0 +IS 17 0 0 0 0 +IS 18 0 0 0 0 +IS 19 0 0 0 0 +IS 20 0 0 0 0 +IS 21 0 0 0 0 +IS 22 0 0 0 0 +IS 23 0 0 0 0 +IS 24 0 0 0 0 +IS 25 0 0 0 0 +IS 26 0 0 0 0 +IS 27 0 0 0 0 +IS 28 0 0 0 0 +IS 29 0 0 0 0 +IS 30 0 0 0 0 +IS 31 0 0 0 0 +IS 32 0 0 0 0 +IS 33 0 0 0 0 +IS 34 0 0 0 0 +IS 35 0 0 0 0 +IS 36 0 0 0 0 +IS 37 0 0 0 0 +IS 38 0 0 0 0 +IS 39 0 0 0 0 +IS 40 0 0 0 0 +IS 41 0 0 0 0 +IS 42 0 0 0 0 +IS 43 0 0 0 0 +IS 44 0 0 0 0 +IS 45 0 0 0 0 +IS 46 0 0 0 0 +IS 47 0 0 0 0 +IS 48 0 0 0 0 +IS 49 0 0 0 0 +IS 50 0 0 0 0 +IS 51 0 0 0 0 +IS 52 0 0 0 0 +IS 53 0 0 0 0 +IS 54 0 0 0 0 +IS 55 0 0 0 0 +IS 56 0 0 0 0 +IS 57 0 0 0 0 +IS 58 0 0 0 0 +IS 59 0 0 0 0 +IS 60 0 0 0 0 +IS 61 0 0 0 0 +IS 62 0 0 0 0 +IS 63 0 0 0 0 +IS 64 0 0 0 0 +IS 65 0 0 0 0 +IS 66 0 0 0 0 +IS 67 0 0 0 0 +IS 68 0 0 0 0 +IS 69 0 0 0 0 +IS 70 0 0 0 0 +IS 71 0 0 0 0 +IS 72 0 0 0 0 +IS 73 0 0 0 0 +IS 74 0 0 0 0 +IS 75 0 0 0 0 +IS 76 0 0 0 0 +IS 77 1 0 1 0 +IS 78 0 0 0 0 +IS 79 0 0 0 0 +IS 80 0 0 0 0 +IS 81 0 0 0 0 +IS 82 1 1 0 0 +IS 83 0 0 0 0 +IS 84 0 0 0 0 +IS 85 0 0 0 0 +IS 86 1 1 0 0 +IS 87 0 0 0 0 +IS 88 0 0 0 0 +IS 89 0 0 0 0 +IS 90 0 0 0 0 +IS 91 0 0 0 0 +IS 92 1 1 0 0 +IS 93 0 0 0 0 +IS 94 0 0 0 0 +IS 95 0 0 0 0 +IS 96 0 0 0 0 +IS 97 0 0 0 0 +IS 98 2 1 1 0 +IS 99 0 0 0 0 +IS 100 0 0 0 0 +IS 101 0 0 0 0 +IS 102 0 0 0 0 +IS 103 0 0 0 0 +IS 104 0 0 0 0 +IS 105 0 0 0 0 +IS 106 2 1 1 0 +IS 107 1 1 0 0 +IS 108 0 0 0 0 +IS 109 0 0 0 0 +IS 110 0 0 0 0 +IS 111 0 0 0 0 +IS 112 1 1 0 0 +IS 113 0 0 0 0 +IS 114 0 0 0 0 +IS 115 0 0 0 0 +IS 116 0 0 0 0 +IS 117 0 0 0 0 +IS 118 1 1 0 0 +IS 119 0 0 0 0 +IS 120 0 0 0 0 +IS 121 0 0 0 0 +IS 122 1 0 1 0 +IS 123 0 0 0 0 +IS 124 0 0 0 0 +IS 125 1 0 1 0 +IS 126 0 0 0 0 +IS 127 1 0 1 0 +IS 128 0 0 0 0 +IS 129 1 0 1 0 +IS 130 0 0 0 0 +IS 131 0 0 0 0 +IS 132 1 1 0 0 +IS 133 0 0 0 0 +IS 134 0 0 0 0 +IS 135 0 0 0 0 +IS 136 0 0 0 0 +IS 137 0 0 0 0 +IS 138 0 0 0 0 +IS 139 1 1 0 0 +IS 140 1 1 0 0 +IS 141 0 0 0 0 +IS 142 1 0 1 0 +IS 143 0 0 0 0 +IS 144 0 0 0 0 +IS 145 0 0 0 0 +IS 146 0 0 0 0 +IS 147 1 1 0 0 +IS 148 1 0 1 0 +IS 149 0 0 0 0 +IS 150 1 1 0 0 +IS 151 0 0 0 0 +IS 152 0 0 0 0 +IS 153 0 0 0 0 +IS 154 0 0 0 0 +IS 155 0 0 0 0 +IS 156 0 0 0 0 +IS 157 0 0 0 0 +IS 158 1 1 0 0 +IS 159 3 3 0 0 +IS 160 0 0 0 0 +IS 161 0 0 0 0 +IS 162 0 0 0 0 +IS 163 0 0 0 0 +IS 164 0 0 0 0 +IS 165 0 0 0 0 +IS 166 2 2 0 0 +IS 167 0 0 0 0 +IS 168 2 2 0 0 +IS 169 0 0 0 0 +IS 170 0 0 0 0 +IS 171 1 1 0 0 +IS 172 1 1 0 0 +IS 173 0 0 0 0 +IS 174 1 1 0 0 +IS 175 0 0 0 0 +IS 176 0 0 0 0 +IS 177 1 1 0 0 +IS 178 1 1 0 0 +IS 179 0 0 0 0 +IS 180 2 2 0 0 +IS 181 0 0 0 0 +IS 182 0 0 0 0 +IS 183 0 0 0 0 +IS 184 0 0 0 0 +IS 185 1 1 0 0 +IS 186 0 0 0 0 +IS 187 1 1 0 0 +IS 188 0 0 0 0 +IS 189 1 1 0 0 +IS 190 0 0 0 0 +IS 191 1 1 0 0 +IS 192 0 0 0 0 +IS 193 0 0 0 0 +IS 194 0 0 0 0 +IS 195 1 1 0 0 +IS 196 0 0 0 0 +IS 197 1 1 0 0 +IS 198 1 1 0 0 +IS 199 0 0 0 0 +IS 200 0 0 0 0 +IS 201 2 2 0 0 +IS 202 1 1 0 0 +IS 203 0 0 0 0 +IS 204 1 1 0 0 +IS 205 0 0 0 0 +IS 206 0 0 0 0 +IS 207 0 0 0 0 +IS 208 0 0 0 0 +IS 209 1 1 0 0 +IS 210 0 0 0 0 +IS 211 0 0 0 0 +IS 212 0 0 0 0 +IS 213 0 0 0 0 +IS 214 1 1 0 0 +IS 215 0 0 0 0 +IS 216 0 0 0 0 +IS 217 0 0 0 0 +IS 218 1 1 0 0 +IS 219 1 1 0 0 +IS 220 0 0 0 0 +IS 221 0 0 0 0 +IS 222 1 1 0 0 +IS 223 0 0 0 0 +IS 224 0 0 0 0 +IS 225 0 0 0 0 +IS 226 0 0 0 0 +IS 227 1 1 0 0 +IS 228 0 0 0 0 +IS 229 0 0 0 0 +IS 230 0 0 0 0 +IS 231 1 1 0 0 +IS 232 1 1 0 0 +IS 233 1 1 0 0 +IS 234 2 2 0 0 +IS 235 3 3 0 0 +IS 236 1 1 0 0 +IS 237 0 0 0 0 +IS 238 2 2 0 0 +IS 239 0 0 0 0 +IS 240 1 1 0 0 +IS 241 0 0 0 0 +IS 242 0 0 0 0 +IS 243 0 0 0 0 +IS 244 1 1 0 0 +IS 245 1 1 0 0 +IS 246 1 1 0 0 +IS 247 2 2 0 0 +IS 248 0 0 0 0 +IS 249 1 1 0 0 +IS 250 0 0 0 0 +IS 251 1 1 0 0 +IS 252 0 0 0 0 +IS 253 0 0 0 0 +IS 254 1 1 0 0 +IS 255 1 1 0 0 +IS 256 0 0 0 0 +IS 257 0 0 0 0 +IS 258 0 0 0 0 +IS 259 1 1 0 0 +IS 260 0 0 0 0 +IS 261 0 0 0 0 +IS 262 0 0 0 0 +IS 263 0 0 0 0 +IS 264 0 0 0 0 +IS 265 0 0 0 0 +IS 266 1 1 0 0 +IS 267 1 1 0 0 +IS 268 1 1 0 0 +IS 269 0 0 0 0 +IS 270 0 0 0 0 +IS 271 0 0 0 0 +IS 272 2 2 0 0 +IS 273 0 0 0 0 +IS 274 0 0 0 0 +IS 275 0 0 0 0 +IS 276 1 1 0 0 +IS 277 0 0 0 0 +IS 278 1 1 0 0 +IS 279 0 0 0 0 +IS 280 0 0 0 0 +IS 281 1 1 0 0 +IS 282 1 1 0 0 +IS 283 0 0 0 0 +IS 284 1 1 0 0 +IS 285 0 0 0 0 +IS 286 0 0 0 0 +IS 287 0 0 0 0 +IS 288 0 0 0 0 +IS 289 0 0 0 0 +IS 290 0 0 0 0 +IS 291 1 1 0 0 +IS 292 0 0 0 0 +IS 293 0 0 0 0 +IS 294 1 1 0 0 +IS 295 0 0 0 0 +IS 296 0 0 0 0 +IS 297 0 0 0 0 +IS 298 0 0 0 0 +IS 299 0 0 0 0 +IS 300 0 0 0 0 +IS 301 0 0 0 0 +IS 302 0 0 0 0 +IS 303 0 0 0 0 +IS 304 1 1 0 0 +IS 305 1 1 0 0 +IS 306 0 0 0 0 +IS 307 0 0 0 0 +IS 308 0 0 0 0 +IS 309 0 0 0 0 +IS 310 1 1 0 0 +IS 311 0 0 0 0 +IS 312 0 0 0 0 +IS 313 0 0 0 0 +IS 314 1 1 0 0 +IS 315 0 0 0 0 +IS 316 0 0 0 0 +IS 317 0 0 0 0 +IS 318 1 1 0 0 +IS 319 0 0 0 0 +IS 320 1 1 0 0 +IS 321 0 0 0 0 +IS 322 0 0 0 0 +IS 323 0 0 0 0 +IS 324 0 0 0 0 +IS 325 0 0 0 0 +IS 326 0 0 0 0 +IS 327 0 0 0 0 +IS 328 0 0 0 0 +IS 329 0 0 0 0 +IS 330 0 0 0 0 +IS 331 0 0 0 0 +IS 332 0 0 0 0 +IS 333 0 0 0 0 +IS 334 0 0 0 0 +IS 335 0 0 0 0 +IS 336 0 0 0 0 +IS 337 0 0 0 0 +IS 338 0 0 0 0 +IS 339 1 1 0 0 +IS 340 0 0 0 0 +IS 341 0 0 0 0 +IS 342 0 0 0 0 +IS 343 1 1 0 0 +IS 344 0 0 0 0 +IS 345 0 0 0 0 +IS 346 0 0 0 0 +IS 347 0 0 0 0 +IS 348 0 0 0 0 +IS 349 0 0 0 0 +IS 350 0 0 0 0 +IS 351 0 0 0 0 +IS 352 0 0 0 0 +IS 353 0 0 0 0 +IS 354 0 0 0 0 +IS 355 0 0 0 0 +IS 356 0 0 0 0 +IS 357 0 0 0 0 +IS 358 0 0 0 0 +IS 359 0 0 0 0 +IS 360 0 0 0 0 +IS 361 0 0 0 0 +IS 362 0 0 0 0 +IS 363 0 0 0 0 +IS 364 1 1 0 0 +# Read lengths. Use `grep ^RL | cut -f 2-` to extract this part. The columns are: read length, count +RL 53 1 +RL 66 1 +RL 68 1 +RL 69 1 +RL 72 1 +RL 77 3 +RL 79 2 +RL 80 1 +RL 82 1 +RL 89 1 +RL 92 2 +RL 94 1 +RL 95 2 +RL 98 4 +RL 101 2 +RL 105 1 +RL 106 5 +RL 107 1 +RL 112 1 +RL 116 1 +RL 117 1 +RL 119 1 +RL 122 2 +RL 125 2 +RL 126 1 +RL 127 1 +RL 129 2 +RL 132 2 +RL 136 1 +RL 139 3 +RL 140 1 +RL 141 1 +RL 142 3 +RL 145 1 +RL 146 2 +RL 147 8 +RL 148 8 +RL 149 16 +RL 150 62 +RL 151 49 +# Read lengths - first fragments. Use `grep ^FRL | cut -f 2-` to extract this part. The columns are: read length, count +FRL 72 1 +FRL 77 2 +FRL 79 2 +FRL 80 1 +FRL 89 1 +FRL 92 1 +FRL 94 1 +FRL 95 1 +FRL 98 2 +FRL 106 2 +FRL 107 1 +FRL 119 1 +FRL 122 1 +FRL 125 1 +FRL 127 1 +FRL 129 1 +FRL 132 1 +FRL 139 2 +FRL 141 1 +FRL 142 2 +FRL 146 2 +FRL 147 5 +FRL 148 3 +FRL 149 9 +FRL 150 26 +FRL 151 29 +# Read lengths - last fragments. Use `grep ^LRL | cut -f 2-` to extract this part. The columns are: read length, count +LRL 53 1 +LRL 66 1 +LRL 68 1 +LRL 69 1 +LRL 77 1 +LRL 82 1 +LRL 92 1 +LRL 95 1 +LRL 98 2 +LRL 101 2 +LRL 105 1 +LRL 106 3 +LRL 112 1 +LRL 116 1 +LRL 117 1 +LRL 122 1 +LRL 125 1 +LRL 126 1 +LRL 129 1 +LRL 132 1 +LRL 136 1 +LRL 139 1 +LRL 140 1 +LRL 142 1 +LRL 145 1 +LRL 147 3 +LRL 148 5 +LRL 149 7 +LRL 150 36 +LRL 151 20 +# Mapping qualities for reads !(UNMAP|SECOND|SUPPL|QCFAIL|DUP). Use `grep ^MAPQ | cut -f 2-` to extract this part. The columns are: mapq, count +MAPQ 1 1 +MAPQ 36 1 +MAPQ 37 1 +MAPQ 38 2 +MAPQ 48 14 +MAPQ 49 1 +MAPQ 50 5 +MAPQ 51 1 +MAPQ 52 1 +MAPQ 55 2 +MAPQ 57 1 +MAPQ 59 1 +MAPQ 60 166 +# Indel distribution. Use `grep ^ID | cut -f 2-` to extract this part. The columns are: length, number of insertions, number of deletions +ID 1 0 8 +ID 2 0 1 +ID 32 0 1 +# Indels per cycle. Use `grep ^IC | cut -f 2-` to extract this part. The columns are: cycle, number of insertions (fwd), .. (rev) , number of deletions (fwd), .. (rev) +IC 5 0 0 1 0 +IC 7 0 0 1 1 +IC 72 0 0 1 0 +IC 85 0 0 1 0 +IC 97 0 0 1 0 +IC 107 0 0 0 1 +IC 121 0 0 0 1 +IC 135 0 0 0 1 +IC 137 0 0 1 0 +# Coverage distribution. Use `grep ^COV | cut -f 2-` to extract this part. +COV [1-1] 1 5542 +COV [2-2] 2 3794 +COV [3-3] 3 1571 +COV [4-4] 4 944 +COV [5-5] 5 491 +COV [6-6] 6 377 +COV [7-7] 7 50 +COV [8-8] 8 39 +COV [9-9] 9 27 +COV [10-10] 10 16 +# GC-depth. Use `grep ^GCD | cut -f 2-` to extract this part. The columns are: GC%, unique sequence percentiles, 10th, 25th, 50th, 75th and 90th depth percentile +GCD 0.0 66.667 0.000 0.000 0.000 0.000 0.000 +GCD 19.2 100.000 0.318 0.318 0.318 0.318 0.318 diff --git a/src/samtools/samtools_stats/test_data/ref.p.paired_end.sorted.txt b/src/samtools/samtools_stats/test_data/ref.p.paired_end.sorted.txt new file mode 100644 index 00000000..6355d2d0 --- /dev/null +++ b/src/samtools/samtools_stats/test_data/ref.p.paired_end.sorted.txt @@ -0,0 +1,1535 @@ +# This file was produced by samtools stats (1.19.2+htslib-1.19.1) and can be plotted using plot-bamstats +# This file contains statistics for all reads. +# The command line was: stats -p test_data/test.paired_end.sorted.bam +# CHK, Checksum [2]Read Names [3]Sequences [4]Qualities +# CHK, CRC32 of reads which passed filtering followed by addition (32bit overflow) +CHK 696e2242 1799722a a8072f55 +# Summary Numbers. Use `grep ^SN | cut -f 2-` to extract this part. +SN raw total sequences: 200 # excluding supplementary and secondary reads +SN filtered sequences: 0 +SN sequences: 200 +SN is sorted: 1 +SN 1st fragments: 100 +SN last fragments: 100 +SN reads mapped: 197 +SN reads mapped and paired: 194 # paired-end technology bit set + both mates mapped +SN reads unmapped: 3 +SN reads properly paired: 192 # proper-pair bit set +SN reads paired: 200 # paired-end technology bit set +SN reads duplicated: 0 # PCR or optical duplicate bit set +SN reads MQ0: 0 # mapped and MQ=0 +SN reads QC failed: 0 +SN non-primary alignments: 0 +SN supplementary alignments: 0 +SN total length: 27645 # ignores clipping +SN total first fragment length: 13897 # ignores clipping +SN total last fragment length: 13748 # ignores clipping +SN bases mapped: 27423 # ignores clipping +SN bases mapped (cigar): 20188 # more accurate +SN bases trimmed: 0 +SN bases duplicated: 0 +SN mismatches: 140 # from NM fields +SN error rate: 6.934813e-03 # mismatches / bases mapped (cigar) +SN average length: 138 +SN average first fragment length: 139 +SN average last fragment length: 137 +SN maximum length: 151 +SN maximum first fragment length: 151 +SN maximum last fragment length: 151 +SN average quality: 33.3 +SN insert size average: 207.7 +SN insert size standard deviation: 66.4 +SN inward oriented pairs: 88 +SN outward oriented pairs: 9 +SN pairs with other orientation: 0 +SN pairs on different chromosomes: 0 +SN percentage of properly paired reads (%): 96.0 +# First Fragment Qualities. Use `grep ^FFQ | cut -f 2-` to extract this part. +# Columns correspond to qualities and rows to cycles. First column is the cycle number. +FFQ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100 0 0 0 0 0 +FFQ 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 96 0 0 0 0 0 +FFQ 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 97 0 0 0 0 0 +FFQ 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 94 0 0 0 1 0 +FFQ 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 93 0 0 0 0 0 +FFQ 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 7 0 0 0 86 0 +FFQ 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 7 0 0 0 84 0 +FFQ 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 12 0 0 0 83 0 +FFQ 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 85 0 +FFQ 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 5 0 0 0 87 0 +FFQ 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 90 0 +FFQ 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 88 0 +FFQ 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 8 0 0 0 84 0 +FFQ 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 6 0 0 0 86 0 +FFQ 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15 0 0 0 83 0 +FFQ 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0 90 0 +FFQ 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 86 0 +FFQ 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 93 0 +FFQ 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 2 0 0 0 86 0 +FFQ 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 4 0 0 0 85 0 +FFQ 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 95 0 +FFQ 22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 91 0 +FFQ 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 90 0 +FFQ 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 1 0 0 0 90 0 +FFQ 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 85 0 +FFQ 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 87 0 +FFQ 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 5 0 0 0 87 0 +FFQ 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 88 0 +FFQ 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 4 0 0 0 90 0 +FFQ 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 7 0 0 0 87 0 +FFQ 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 4 0 0 0 0 0 2 0 0 0 0 3 0 0 0 85 0 +FFQ 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 6 0 0 0 89 0 +FFQ 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 7 0 0 0 84 0 +FFQ 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 89 0 +FFQ 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 88 0 +FFQ 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 8 0 0 0 85 0 +FFQ 37 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 4 0 0 0 87 0 +FFQ 38 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 4 0 0 0 91 0 +FFQ 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 86 0 +FFQ 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 3 0 0 0 90 0 +FFQ 41 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 9 0 0 0 85 0 +FFQ 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 5 0 0 0 88 0 +FFQ 43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 4 0 0 0 83 0 +FFQ 44 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 8 0 0 0 83 0 +FFQ 45 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 86 0 +FFQ 46 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 9 0 0 0 85 0 +FFQ 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 10 0 0 0 77 0 +FFQ 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 12 0 0 0 80 0 +FFQ 49 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 8 0 0 0 79 0 +FFQ 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 81 0 +FFQ 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 12 0 0 0 83 0 +FFQ 52 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 12 0 0 0 80 0 +FFQ 53 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 15 0 0 0 77 0 +FFQ 54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 7 0 0 0 0 12 0 0 0 72 0 +FFQ 55 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 8 0 0 0 82 0 +FFQ 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 9 0 0 0 80 0 +FFQ 57 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 13 0 0 0 77 0 +FFQ 58 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 3 0 0 0 0 0 3 0 0 0 0 11 0 0 0 76 0 +FFQ 59 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 81 0 +FFQ 60 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 5 0 0 0 83 0 +FFQ 61 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 8 0 0 0 81 0 +FFQ 62 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 6 0 0 0 81 0 +FFQ 63 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 84 0 +FFQ 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 7 0 0 0 77 0 +FFQ 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 10 0 0 0 77 0 +FFQ 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 10 0 0 0 76 0 +FFQ 67 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 15 0 0 0 77 0 +FFQ 68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 10 0 0 0 81 0 +FFQ 69 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 5 0 0 0 0 4 0 0 0 82 0 +FFQ 70 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 3 0 0 0 0 0 4 0 0 0 0 7 0 0 0 78 0 +FFQ 71 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 9 0 0 0 79 0 +FFQ 72 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 12 0 0 0 81 0 +FFQ 73 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 9 0 0 0 78 0 +FFQ 74 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 82 0 +FFQ 75 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 12 0 0 0 78 0 +FFQ 76 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 6 0 0 0 80 0 +FFQ 77 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 79 0 +FFQ 78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 13 0 0 0 73 0 +FFQ 79 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 15 0 0 0 72 0 +FFQ 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 15 0 0 0 72 0 +FFQ 81 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 74 0 +FFQ 82 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 12 0 0 0 72 0 +FFQ 83 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 7 0 0 0 74 0 +FFQ 84 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 5 0 0 0 80 0 +FFQ 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 10 0 0 0 70 0 +FFQ 86 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 17 0 0 0 68 0 +FFQ 87 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 7 0 0 0 72 0 +FFQ 88 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 7 0 0 0 77 0 +FFQ 89 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 9 0 0 0 78 0 +FFQ 90 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 11 0 0 0 72 0 +FFQ 91 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 10 0 0 0 74 0 +FFQ 92 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 7 0 0 0 75 0 +FFQ 93 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 12 0 0 0 68 0 +FFQ 94 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 77 0 +FFQ 95 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 10 0 0 0 70 0 +FFQ 96 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 7 0 0 0 75 0 +FFQ 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 11 0 0 0 71 0 +FFQ 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 14 0 0 0 61 0 +FFQ 99 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 67 0 +FFQ 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 12 0 0 0 64 0 +FFQ 101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 5 0 0 0 67 0 +FFQ 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 9 0 0 0 68 0 +FFQ 103 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 14 0 0 0 61 0 +FFQ 104 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 17 0 0 0 59 0 +FFQ 105 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 19 0 0 0 56 0 +FFQ 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 16 0 0 0 57 0 +FFQ 107 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 17 0 0 0 58 0 +FFQ 108 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 15 0 0 0 56 0 +FFQ 109 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 15 0 0 0 52 0 +FFQ 110 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 19 0 0 0 50 0 +FFQ 111 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 19 0 0 0 52 0 +FFQ 112 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 16 0 0 0 55 0 +FFQ 113 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 8 0 0 0 0 22 0 0 0 45 0 +FFQ 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 9 0 0 0 0 22 0 0 0 45 0 +FFQ 115 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 14 0 0 0 0 16 0 0 0 48 0 +FFQ 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 8 0 0 0 0 27 0 0 0 43 0 +FFQ 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 21 0 0 0 49 0 +FFQ 118 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 24 0 0 0 44 0 +FFQ 119 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 23 0 0 0 43 0 +FFQ 120 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 28 0 0 0 48 0 +FFQ 121 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 18 0 0 0 48 0 +FFQ 122 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 6 0 0 0 0 17 0 0 0 54 0 +FFQ 123 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 20 0 0 0 52 0 +FFQ 124 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 20 0 0 0 49 0 +FFQ 125 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 29 0 0 0 42 0 +FFQ 126 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 2 0 0 0 0 0 5 0 0 0 0 22 0 0 0 42 0 +FFQ 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 5 0 0 0 0 0 7 0 0 0 0 20 0 0 0 42 0 +FFQ 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 2 0 0 0 0 0 8 0 0 0 0 20 0 0 0 42 0 +FFQ 129 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 20 0 0 0 48 0 +FFQ 130 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 21 0 0 0 45 0 +FFQ 131 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 20 0 0 0 49 0 +FFQ 132 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 5 0 0 0 0 25 0 0 0 41 0 +FFQ 133 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 18 0 0 0 47 0 +FFQ 134 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 6 0 0 0 0 21 0 0 0 43 0 +FFQ 135 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 9 0 0 0 0 26 0 0 0 37 0 +FFQ 136 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 15 0 0 0 45 0 +FFQ 137 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 19 0 0 0 46 0 +FFQ 138 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 28 0 0 0 34 0 +FFQ 139 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 23 0 0 0 34 0 +FFQ 140 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 19 0 0 0 46 0 +FFQ 141 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 4 0 0 0 0 0 6 0 0 0 0 18 0 0 0 40 0 +FFQ 142 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 24 0 0 0 37 0 +FFQ 143 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 19 0 0 0 34 0 +FFQ 144 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 16 0 0 0 35 0 +FFQ 145 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 34 0 0 0 34 0 +FFQ 146 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 7 0 0 0 0 21 0 0 0 37 0 +FFQ 147 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 16 0 0 0 43 0 +FFQ 148 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 23 0 0 0 33 0 +FFQ 149 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 22 0 0 0 35 0 +FFQ 150 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 18 0 0 0 36 0 +FFQ 151 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 18 0 0 0 11 0 +# Last Fragment Qualities. Use `grep ^LFQ | cut -f 2-` to extract this part. +# Columns correspond to qualities and rows to cycles. First column is the cycle number. +LFQ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100 0 0 0 0 0 +LFQ 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 95 0 0 0 0 0 +LFQ 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 93 0 0 0 0 0 +LFQ 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 94 0 0 0 0 0 +LFQ 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 94 0 0 0 1 0 +LFQ 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 10 0 0 0 83 0 +LFQ 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 93 0 +LFQ 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 3 0 0 0 91 0 +LFQ 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 87 0 +LFQ 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 4 0 0 0 90 0 +LFQ 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 4 0 0 0 90 0 +LFQ 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 7 0 0 0 83 0 +LFQ 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 2 0 0 0 90 0 +LFQ 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 10 0 0 0 86 0 +LFQ 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 6 0 0 0 87 0 +LFQ 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 10 0 0 0 84 0 +LFQ 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0 91 0 +LFQ 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 4 0 0 0 91 0 +LFQ 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 92 0 +LFQ 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 3 0 0 0 90 0 +LFQ 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 89 0 +LFQ 22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 3 0 0 0 88 0 +LFQ 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 89 0 +LFQ 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 88 0 +LFQ 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 9 0 0 0 84 0 +LFQ 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 4 0 0 0 89 0 +LFQ 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 8 0 0 0 87 0 +LFQ 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 2 0 0 0 90 0 +LFQ 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 5 0 0 0 86 0 +LFQ 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 5 0 0 0 88 0 +LFQ 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 92 0 +LFQ 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 5 0 0 0 86 0 +LFQ 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 1 0 0 0 89 0 +LFQ 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 84 0 +LFQ 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 4 0 0 0 87 0 +LFQ 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 8 0 0 0 82 0 +LFQ 37 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 83 0 +LFQ 38 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 8 0 0 0 85 0 +LFQ 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 7 0 0 0 85 0 +LFQ 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 88 0 +LFQ 41 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 11 0 0 0 78 0 +LFQ 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 87 0 +LFQ 43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 9 0 0 0 81 0 +LFQ 44 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 6 0 0 0 86 0 +LFQ 45 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 85 0 +LFQ 46 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 9 0 0 0 81 0 +LFQ 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 5 0 0 0 88 0 +LFQ 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 8 0 0 0 84 0 +LFQ 49 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 11 0 0 0 80 0 +LFQ 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 10 0 0 0 79 0 +LFQ 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 8 0 0 0 80 0 +LFQ 52 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 8 0 0 0 79 0 +LFQ 53 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 7 0 0 0 81 0 +LFQ 54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 15 0 0 0 79 0 +LFQ 55 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 8 0 0 0 85 0 +LFQ 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 8 0 0 0 80 0 +LFQ 57 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 83 0 +LFQ 58 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 9 0 0 0 80 0 +LFQ 59 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 8 0 0 0 82 0 +LFQ 60 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 6 0 0 0 77 0 +LFQ 61 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 9 0 0 0 81 0 +LFQ 62 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 7 0 0 0 80 0 +LFQ 63 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 8 0 0 0 84 0 +LFQ 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 10 0 0 0 80 0 +LFQ 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 5 0 0 0 74 0 +LFQ 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 7 0 0 0 79 0 +LFQ 67 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 10 0 0 0 79 0 +LFQ 68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 7 0 0 0 83 0 +LFQ 69 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 9 0 0 0 76 0 +LFQ 70 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 76 0 +LFQ 71 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 7 0 0 0 74 0 +LFQ 72 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 71 0 +LFQ 73 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 80 0 +LFQ 74 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 8 0 0 0 75 0 +LFQ 75 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 11 0 0 0 80 0 +LFQ 76 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 80 0 +LFQ 77 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 6 0 0 0 77 0 +LFQ 78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 13 0 0 0 69 0 +LFQ 79 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0 0 0 74 0 +LFQ 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 3 0 0 0 0 12 0 0 0 72 0 +LFQ 81 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 10 0 0 0 79 0 +LFQ 82 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 9 0 0 0 78 0 +LFQ 83 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 9 0 0 0 74 0 +LFQ 84 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 12 0 0 0 72 0 +LFQ 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 14 0 0 0 66 0 +LFQ 86 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 12 0 0 0 72 0 +LFQ 87 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 4 0 0 0 78 0 +LFQ 88 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 8 0 0 0 70 0 +LFQ 89 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 73 0 +LFQ 90 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 11 0 0 0 72 0 +LFQ 91 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 11 0 0 0 72 0 +LFQ 92 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 14 0 0 0 68 0 +LFQ 93 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 9 0 0 0 68 0 +LFQ 94 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 15 0 0 0 68 0 +LFQ 95 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 19 0 0 0 64 0 +LFQ 96 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 13 0 0 0 66 0 +LFQ 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 12 0 0 0 70 0 +LFQ 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 13 0 0 0 67 0 +LFQ 99 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 12 0 0 0 62 0 +LFQ 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 15 0 0 0 59 0 +LFQ 101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 11 0 0 0 63 0 +LFQ 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 15 0 0 0 60 0 +LFQ 103 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 14 0 0 0 64 0 +LFQ 104 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 21 0 0 0 57 0 +LFQ 105 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 19 0 0 0 55 0 +LFQ 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 19 0 0 0 55 0 +LFQ 107 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 17 0 0 0 60 0 +LFQ 108 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 13 0 0 0 58 0 +LFQ 109 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 19 0 0 0 55 0 +LFQ 110 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 16 0 0 0 48 0 +LFQ 111 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 14 0 0 0 55 0 +LFQ 112 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 22 0 0 0 43 0 +LFQ 113 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 18 0 0 0 47 0 +LFQ 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 13 0 0 0 50 0 +LFQ 115 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 19 0 0 0 44 0 +LFQ 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 18 0 0 0 49 0 +LFQ 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 25 0 0 0 39 0 +LFQ 118 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 2 0 0 0 0 0 8 0 0 0 0 32 0 0 0 35 0 +LFQ 119 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 25 0 0 0 41 0 +LFQ 120 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 21 0 0 0 46 0 +LFQ 121 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 28 0 0 0 35 0 +LFQ 122 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 21 0 0 0 40 0 +LFQ 123 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 12 0 0 0 0 19 0 0 0 42 0 +LFQ 124 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 15 0 0 0 0 23 0 0 0 35 0 +LFQ 125 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 30 0 0 0 32 0 +LFQ 126 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 4 0 0 0 0 27 0 0 0 41 0 +LFQ 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 26 0 0 0 41 0 +LFQ 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 24 0 0 0 38 0 +LFQ 129 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 8 0 0 0 0 20 0 0 0 41 0 +LFQ 130 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 10 0 0 0 0 31 0 0 0 30 0 +LFQ 131 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 23 0 0 0 36 0 +LFQ 132 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 3 0 0 0 0 0 9 0 0 0 0 21 0 0 0 35 0 +LFQ 133 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 26 0 0 0 36 0 +LFQ 134 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 4 0 0 0 0 0 3 0 0 0 0 28 0 0 0 35 0 +LFQ 135 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 9 0 0 0 0 23 0 0 0 35 0 +LFQ 136 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 26 0 0 0 41 0 +LFQ 137 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 7 0 0 0 0 24 0 0 0 38 0 +LFQ 138 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 20 0 0 0 36 0 +LFQ 139 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 25 0 0 0 38 0 +LFQ 140 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 3 0 0 0 0 0 8 0 0 0 0 19 0 0 0 36 0 +LFQ 141 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 6 0 0 0 0 22 0 0 0 38 0 +LFQ 142 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 9 0 0 0 0 20 0 0 0 35 0 +LFQ 143 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 3 0 0 0 0 0 9 0 0 0 0 17 0 0 0 35 0 +LFQ 144 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 22 0 0 0 38 0 +LFQ 145 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 20 0 0 0 38 0 +LFQ 146 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 7 0 0 0 0 23 0 0 0 35 0 +LFQ 147 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 31 0 0 0 28 0 +LFQ 148 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 9 0 0 0 0 23 0 0 0 28 0 +LFQ 149 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 19 0 0 0 29 0 +LFQ 150 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 23 0 0 0 30 0 +LFQ 151 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 0 0 0 4 0 +# GC Content of first fragments. Use `grep ^GCF | cut -f 2-` to extract this part. +GCF 15.08 0 +GCF 30.40 1 +GCF 31.16 2 +GCF 32.16 0 +GCF 33.17 2 +GCF 33.92 5 +GCF 34.42 4 +GCF 34.92 2 +GCF 35.43 3 +GCF 35.93 7 +GCF 36.43 9 +GCF 36.93 4 +GCF 37.44 7 +GCF 37.94 8 +GCF 38.44 10 +GCF 38.94 7 +GCF 39.70 6 +GCF 40.45 8 +GCF 40.95 9 +GCF 41.71 4 +GCF 42.46 5 +GCF 42.96 7 +GCF 43.72 2 +GCF 44.72 1 +GCF 45.48 3 +GCF 46.48 2 +GCF 47.74 1 +GCF 48.74 2 +GCF 50.25 0 +GCF 52.01 1 +GCF 54.77 0 +GCF 57.54 1 +# GC Content of last fragments. Use `grep ^GCL | cut -f 2-` to extract this part. +GCL 15.08 0 +GCL 30.65 1 +GCL 31.66 0 +GCL 32.41 2 +GCL 32.91 1 +GCL 33.42 3 +GCL 33.92 4 +GCL 34.42 3 +GCL 34.92 4 +GCL 35.68 5 +GCL 36.43 10 +GCL 36.93 8 +GCL 37.44 7 +GCL 37.94 9 +GCL 38.44 10 +GCL 38.94 13 +GCL 39.45 8 +GCL 39.95 7 +GCL 40.45 2 +GCL 40.95 4 +GCL 41.46 3 +GCL 41.96 1 +GCL 42.46 4 +GCL 42.96 6 +GCL 43.47 4 +GCL 44.22 2 +GCL 44.97 4 +GCL 45.48 7 +GCL 45.98 3 +GCL 46.48 2 +GCL 46.98 3 +GCL 47.49 1 +GCL 48.49 0 +GCL 49.75 2 +# ACGT content per cycle. Use `grep ^GCC | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%]; and N and O counts as a percentage of all A/C/G/T bases [%] +GCC 1 19.50 26.50 31.50 22.50 0.00 0.00 +GCC 2 30.50 20.50 17.00 32.00 0.00 0.00 +GCC 3 32.00 15.00 16.50 36.50 0.00 0.00 +GCC 4 30.50 21.00 17.50 31.00 0.00 0.00 +GCC 5 39.50 9.50 12.50 38.50 0.00 0.00 +GCC 6 28.00 17.50 18.50 36.00 0.00 0.00 +GCC 7 29.50 19.50 21.00 30.00 0.00 0.00 +GCC 8 29.50 21.00 23.00 26.50 0.00 0.00 +GCC 9 22.00 32.50 27.00 18.50 0.00 0.00 +GCC 10 36.00 12.00 16.00 36.00 0.00 0.00 +GCC 11 28.00 18.50 20.50 33.00 0.00 0.00 +GCC 12 33.50 21.00 16.00 29.50 0.00 0.00 +GCC 13 28.00 19.00 27.50 25.50 0.00 0.00 +GCC 14 24.50 21.50 19.00 35.00 0.00 0.00 +GCC 15 29.50 16.50 20.00 34.00 0.00 0.00 +GCC 16 31.00 20.00 21.50 27.50 0.00 0.00 +GCC 17 27.50 16.50 19.50 36.50 0.00 0.00 +GCC 18 30.50 24.00 19.50 26.00 0.00 0.00 +GCC 19 23.50 21.50 17.50 37.50 0.00 0.00 +GCC 20 31.50 17.00 21.50 30.00 0.00 0.00 +GCC 21 26.00 22.00 17.50 34.50 0.00 0.00 +GCC 22 30.50 19.00 23.00 27.50 0.00 0.00 +GCC 23 31.50 15.50 22.50 30.50 0.00 0.00 +GCC 24 32.00 18.00 21.00 29.00 0.00 0.00 +GCC 25 27.50 16.50 22.00 34.00 0.00 0.00 +GCC 26 27.50 18.50 23.50 30.50 0.00 0.00 +GCC 27 28.50 19.00 19.50 33.00 0.00 0.00 +GCC 28 22.50 21.00 22.50 34.00 0.00 0.00 +GCC 29 27.00 18.50 22.00 32.50 0.00 0.00 +GCC 30 30.50 20.00 21.50 28.00 0.00 0.00 +GCC 31 24.50 21.00 24.00 30.50 0.00 0.00 +GCC 32 32.50 17.50 16.50 33.50 0.00 0.00 +GCC 33 28.50 16.00 25.00 30.50 0.00 0.00 +GCC 34 29.00 21.00 23.50 26.50 0.00 0.00 +GCC 35 32.50 18.50 21.00 28.00 0.00 0.00 +GCC 36 35.00 12.50 20.00 32.50 0.00 0.00 +GCC 37 26.50 20.00 18.50 35.00 0.00 0.00 +GCC 38 27.00 21.00 19.50 32.50 0.00 0.00 +GCC 39 31.00 20.00 19.00 30.00 0.00 0.00 +GCC 40 27.50 20.00 21.50 31.00 0.00 0.00 +GCC 41 37.00 16.50 19.00 27.50 0.00 0.00 +GCC 42 26.50 19.50 18.50 35.50 0.00 0.00 +GCC 43 33.50 20.00 17.50 29.00 0.00 0.00 +GCC 44 31.50 16.00 21.00 31.50 0.00 0.00 +GCC 45 28.50 19.00 20.00 32.50 0.00 0.00 +GCC 46 24.50 23.50 17.50 34.50 0.00 0.00 +GCC 47 22.50 24.50 19.50 33.50 0.00 0.00 +GCC 48 27.50 17.50 22.50 32.50 0.00 0.00 +GCC 49 28.50 17.00 20.00 34.50 0.00 0.00 +GCC 50 32.00 16.50 20.00 31.50 0.00 0.00 +GCC 51 27.50 20.50 21.00 31.00 0.00 0.00 +GCC 52 27.50 21.50 19.50 31.50 0.00 0.00 +GCC 53 26.00 19.00 25.50 29.50 0.00 0.00 +GCC 54 30.65 23.62 16.58 29.15 0.00 0.00 +GCC 55 29.65 21.61 20.10 28.64 0.00 0.00 +GCC 56 32.16 16.58 22.11 29.15 0.00 0.00 +GCC 57 28.64 20.60 21.11 29.65 0.00 0.00 +GCC 58 29.65 14.57 24.62 31.16 0.00 0.00 +GCC 59 31.16 21.61 17.59 29.65 0.00 0.00 +GCC 60 28.64 17.59 22.11 31.66 0.00 0.00 +GCC 61 25.13 21.61 22.61 30.65 0.00 0.00 +GCC 62 27.14 26.13 21.61 25.13 0.00 0.00 +GCC 63 29.15 14.57 18.59 37.69 0.00 0.00 +GCC 64 29.15 15.08 21.61 34.17 0.00 0.00 +GCC 65 28.64 20.10 19.10 32.16 0.00 0.00 +GCC 66 31.66 19.10 16.08 33.17 0.00 0.00 +GCC 67 24.75 20.20 24.24 30.81 0.00 0.00 +GCC 68 26.77 19.70 23.23 30.30 0.00 0.00 +GCC 69 30.96 17.26 22.84 28.93 0.00 0.00 +GCC 70 33.67 16.84 21.94 27.55 0.00 0.00 +GCC 71 35.20 20.41 18.88 25.51 0.00 0.00 +GCC 72 33.67 15.82 18.88 31.63 0.00 0.00 +GCC 73 32.31 18.46 18.46 30.77 0.00 0.00 +GCC 74 27.69 18.46 24.10 29.74 0.00 0.00 +GCC 75 32.31 14.87 21.54 31.28 0.00 0.00 +GCC 76 24.62 20.00 21.03 34.36 0.00 0.00 +GCC 77 29.74 17.44 17.95 34.87 0.00 0.00 +GCC 78 24.48 20.83 17.19 37.50 0.00 0.00 +GCC 79 33.33 20.83 19.79 26.04 0.00 0.00 +GCC 80 31.05 16.32 22.11 30.53 0.00 0.00 +GCC 81 33.33 15.87 15.34 35.45 0.00 0.00 +GCC 82 31.75 19.58 19.58 29.10 0.00 0.00 +GCC 83 30.32 21.81 18.62 29.26 0.00 0.00 +GCC 84 27.66 21.81 15.96 34.57 0.00 0.00 +GCC 85 26.06 15.43 22.34 36.17 0.00 0.00 +GCC 86 25.00 18.09 21.81 35.11 0.00 0.00 +GCC 87 30.85 18.09 15.43 35.64 0.00 0.00 +GCC 88 32.45 25.00 18.09 24.47 0.00 0.00 +GCC 89 24.47 15.43 19.68 40.43 0.00 0.00 +GCC 90 27.27 21.93 20.86 29.95 0.00 0.00 +GCC 91 28.34 14.97 20.86 35.83 0.00 0.00 +GCC 92 28.34 18.18 20.32 33.16 0.00 0.00 +GCC 93 28.65 18.38 18.38 34.59 0.00 0.00 +GCC 94 29.19 17.84 20.54 32.43 0.00 0.00 +GCC 95 27.72 23.91 21.20 27.17 0.00 0.00 +GCC 96 31.32 18.68 16.48 33.52 0.00 0.00 +GCC 97 21.98 17.58 21.43 39.01 0.00 0.00 +GCC 98 27.47 15.93 18.68 37.91 0.00 0.00 +GCC 99 27.53 20.22 17.98 34.27 0.00 0.00 +GCC 100 34.83 15.17 19.66 30.34 0.00 0.00 +GCC 101 36.52 16.85 20.22 26.40 0.00 0.00 +GCC 102 29.55 22.16 23.30 25.00 0.00 0.00 +GCC 103 27.84 18.75 19.32 34.09 0.00 0.00 +GCC 104 26.14 14.77 22.16 36.93 0.00 0.00 +GCC 105 33.52 11.36 19.89 35.23 0.00 0.00 +GCC 106 28.00 20.00 19.43 32.57 0.00 0.00 +GCC 107 25.88 16.47 24.12 33.53 0.00 0.00 +GCC 108 30.77 20.71 15.98 32.54 0.00 0.00 +GCC 109 26.63 30.18 16.57 26.63 0.00 0.00 +GCC 110 27.81 9.47 23.67 39.05 0.00 0.00 +GCC 111 30.18 16.57 23.67 29.59 0.00 0.00 +GCC 112 28.40 21.30 24.85 25.44 0.00 0.00 +GCC 113 28.57 19.64 22.02 29.76 0.00 0.00 +GCC 114 31.55 23.21 17.86 27.38 0.00 0.00 +GCC 115 35.12 19.64 15.48 29.76 0.00 0.00 +GCC 116 26.79 17.86 22.62 32.74 0.00 0.00 +GCC 117 34.73 22.75 14.37 28.14 0.00 0.00 +GCC 118 27.11 23.49 15.06 34.34 0.00 0.00 +GCC 119 31.93 19.28 20.48 28.31 0.00 0.00 +GCC 120 35.15 16.97 18.18 29.70 0.00 0.00 +GCC 121 26.67 24.85 18.18 30.30 0.00 0.00 +GCC 122 33.94 17.58 19.39 29.09 0.00 0.00 +GCC 123 29.45 19.63 18.40 32.52 0.00 0.00 +GCC 124 24.54 22.09 23.31 30.06 0.00 0.00 +GCC 125 28.22 17.18 20.86 33.74 0.00 0.00 +GCC 126 40.99 17.39 16.15 25.47 0.00 0.00 +GCC 127 28.75 18.12 19.38 33.75 0.00 0.00 +GCC 128 25.16 22.01 20.13 32.70 0.00 0.00 +GCC 129 23.27 16.98 23.27 36.48 0.00 0.00 +GCC 130 33.12 12.74 24.20 29.94 0.00 0.00 +GCC 131 25.48 16.56 21.66 36.31 0.00 0.00 +GCC 132 31.21 19.11 22.29 27.39 0.00 0.00 +GCC 133 30.97 19.35 19.35 30.32 0.00 0.00 +GCC 134 32.90 14.84 23.23 29.03 0.00 0.00 +GCC 135 32.26 18.71 18.06 30.97 0.00 0.00 +GCC 136 34.19 19.35 22.58 23.87 0.00 0.00 +GCC 137 27.27 18.18 20.13 34.42 0.00 0.00 +GCC 138 30.52 18.18 17.53 33.77 0.00 0.00 +GCC 139 26.62 22.08 19.48 31.82 0.00 0.00 +GCC 140 27.81 24.50 19.87 27.81 0.00 0.00 +GCC 141 28.00 23.33 21.33 27.33 0.00 0.00 +GCC 142 29.53 15.44 28.19 26.85 0.00 0.00 +GCC 143 24.66 15.07 23.97 36.30 0.00 0.00 +GCC 144 27.40 16.44 19.86 36.30 0.00 0.00 +GCC 145 29.45 13.70 19.86 36.99 0.00 0.00 +GCC 146 35.86 12.41 18.62 33.10 0.00 0.00 +GCC 147 32.87 20.98 16.08 30.07 0.00 0.00 +GCC 148 31.11 20.74 23.70 24.44 0.00 0.00 +GCC 149 33.07 14.96 19.69 32.28 0.00 0.00 +GCC 150 36.94 14.41 14.41 34.23 0.00 0.00 +GCC 151 40.82 18.37 14.29 26.53 0.00 0.00 +# ACGT content per cycle, read oriented. Use `grep ^GCT | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%] +GCT 1 22.50 26.00 32.00 19.50 +GCT 2 20.00 21.50 16.00 42.50 +GCT 3 30.00 16.50 15.00 38.50 +GCT 4 21.50 26.50 12.00 40.00 +GCT 5 44.50 10.00 12.00 33.50 +GCT 6 42.50 13.50 22.50 21.50 +GCT 7 34.50 17.00 23.50 25.00 +GCT 8 37.50 22.50 21.50 18.50 +GCT 9 17.00 39.00 20.50 23.50 +GCT 10 33.00 14.50 13.50 39.00 +GCT 11 34.50 12.50 26.50 26.50 +GCT 12 27.50 14.50 22.50 35.50 +GCT 13 21.50 22.00 24.50 32.00 +GCT 14 28.00 27.50 13.00 31.50 +GCT 15 35.00 15.50 21.00 28.50 +GCT 16 36.50 24.00 17.50 22.00 +GCT 17 36.50 18.00 18.00 27.50 +GCT 18 29.50 23.50 20.00 27.00 +GCT 19 30.00 17.50 21.50 31.00 +GCT 20 30.00 19.00 19.50 31.50 +GCT 21 25.50 20.00 19.50 35.00 +GCT 22 29.00 23.00 19.00 29.00 +GCT 23 30.50 21.00 17.00 31.50 +GCT 24 30.50 22.00 17.00 30.50 +GCT 25 28.50 19.00 19.50 33.00 +GCT 26 27.50 19.00 23.00 30.50 +GCT 27 33.50 21.50 17.00 28.00 +GCT 28 28.50 23.50 20.00 28.00 +GCT 29 32.00 21.00 19.50 27.50 +GCT 30 30.50 20.50 21.00 28.00 +GCT 31 25.00 24.00 21.00 30.00 +GCT 32 37.00 17.50 16.50 29.00 +GCT 33 27.00 19.00 22.00 32.00 +GCT 34 29.50 22.00 22.50 26.00 +GCT 35 29.00 19.50 20.00 31.50 +GCT 36 37.50 17.50 15.00 30.00 +GCT 37 32.50 21.50 17.00 29.00 +GCT 38 30.00 20.50 20.00 29.50 +GCT 39 34.00 20.50 18.50 27.00 +GCT 40 27.00 22.00 19.50 31.50 +GCT 41 32.00 20.00 15.50 32.50 +GCT 42 37.50 17.00 21.00 24.50 +GCT 43 25.50 19.50 18.00 37.00 +GCT 44 31.50 18.50 18.50 31.50 +GCT 45 27.00 20.00 19.00 34.00 +GCT 46 29.00 20.50 20.50 30.00 +GCT 47 29.00 20.50 23.50 27.00 +GCT 48 27.00 21.50 18.50 33.00 +GCT 49 27.00 17.00 20.00 36.00 +GCT 50 29.00 21.00 15.50 34.50 +GCT 51 33.00 21.50 20.00 25.50 +GCT 52 30.50 21.00 20.00 28.50 +GCT 53 24.50 23.00 21.50 31.00 +GCT 54 30.15 20.60 19.60 29.65 +GCT 55 25.13 20.60 21.11 33.17 +GCT 56 26.13 21.11 17.59 35.18 +GCT 57 27.14 20.60 21.11 31.16 +GCT 58 30.15 17.59 21.61 30.65 +GCT 59 32.66 20.60 18.59 28.14 +GCT 60 31.66 18.09 21.61 28.64 +GCT 61 25.13 23.12 21.11 30.65 +GCT 62 24.62 23.12 24.62 27.64 +GCT 63 36.68 17.59 15.58 30.15 +GCT 64 35.18 16.58 20.10 28.14 +GCT 65 30.65 18.59 20.60 30.15 +GCT 66 34.67 15.58 19.60 30.15 +GCT 67 29.29 24.75 19.70 26.26 +GCT 68 28.28 21.21 21.72 28.79 +GCT 69 29.44 22.84 17.26 30.46 +GCT 70 36.22 19.90 18.88 25.00 +GCT 71 34.18 20.92 18.37 26.53 +GCT 72 32.14 17.86 16.84 33.16 +GCT 73 32.82 14.36 22.56 30.26 +GCT 74 30.26 21.54 21.03 27.18 +GCT 75 33.33 18.46 17.95 30.26 +GCT 76 29.23 23.08 17.95 29.74 +GCT 77 29.74 17.95 17.44 34.87 +GCT 78 31.25 20.83 17.19 30.73 +GCT 79 29.17 23.44 17.19 30.21 +GCT 80 35.79 21.05 17.37 25.79 +GCT 81 39.68 20.11 11.11 29.10 +GCT 82 28.04 16.93 22.22 32.80 +GCT 83 29.26 20.21 20.21 30.32 +GCT 84 35.11 18.09 19.68 27.13 +GCT 85 28.72 20.74 17.02 33.51 +GCT 86 29.79 21.28 18.62 30.32 +GCT 87 31.38 18.09 15.43 35.11 +GCT 88 28.72 21.81 21.28 28.19 +GCT 89 30.32 18.62 16.49 34.57 +GCT 90 29.95 13.90 28.88 27.27 +GCT 91 32.09 15.51 20.32 32.09 +GCT 92 26.20 18.18 20.32 35.29 +GCT 93 31.35 18.38 18.38 31.89 +GCT 94 29.73 15.68 22.70 31.89 +GCT 95 28.80 19.57 25.54 26.09 +GCT 96 32.42 20.33 14.84 32.42 +GCT 97 31.87 21.43 17.58 29.12 +GCT 98 30.77 14.29 20.33 34.62 +GCT 99 28.65 17.42 20.79 33.15 +GCT 100 28.65 14.04 20.79 36.52 +GCT 101 27.53 23.03 14.04 35.39 +GCT 102 26.70 17.05 28.41 27.84 +GCT 103 29.55 20.45 17.61 32.39 +GCT 104 34.66 22.16 14.77 28.41 +GCT 105 40.91 13.07 18.18 27.84 +GCT 106 24.57 20.57 18.86 36.00 +GCT 107 26.47 18.24 22.35 32.94 +GCT 108 31.95 17.16 19.53 31.36 +GCT 109 26.04 24.85 21.89 27.22 +GCT 110 32.54 17.75 15.38 34.32 +GCT 111 26.63 17.75 22.49 33.14 +GCT 112 27.81 23.08 23.08 26.04 +GCT 113 35.12 16.67 25.00 23.21 +GCT 114 30.95 21.43 19.64 27.98 +GCT 115 29.17 18.45 16.67 35.71 +GCT 116 30.36 17.86 22.62 29.17 +GCT 117 27.54 21.56 15.57 35.33 +GCT 118 33.13 22.89 15.66 28.31 +GCT 119 33.73 16.87 22.89 26.51 +GCT 120 26.67 13.94 21.21 38.18 +GCT 121 29.09 18.18 24.85 27.88 +GCT 122 27.27 21.21 15.76 35.76 +GCT 123 30.06 17.79 20.25 31.90 +GCT 124 28.22 22.09 23.31 26.38 +GCT 125 27.61 20.25 17.79 34.36 +GCT 126 31.06 16.77 16.77 35.40 +GCT 127 32.50 15.00 22.50 30.00 +GCT 128 25.79 18.87 23.27 32.08 +GCT 129 28.30 20.75 19.50 31.45 +GCT 130 33.12 18.47 18.47 29.94 +GCT 131 31.85 19.75 18.47 29.94 +GCT 132 30.57 22.93 18.47 28.03 +GCT 133 29.68 18.06 20.65 31.61 +GCT 134 30.97 23.23 14.84 30.97 +GCT 135 32.90 16.77 20.00 30.32 +GCT 136 29.03 19.35 22.58 29.03 +GCT 137 27.92 24.68 13.64 33.77 +GCT 138 35.06 16.88 18.83 29.22 +GCT 139 33.12 22.73 18.83 25.32 +GCT 140 34.44 22.52 21.85 21.19 +GCT 141 25.33 22.67 22.00 30.00 +GCT 142 31.54 21.48 22.15 24.83 +GCT 143 35.62 20.55 18.49 25.34 +GCT 144 25.34 14.38 21.92 38.36 +GCT 145 35.62 15.75 17.81 30.82 +GCT 146 33.79 14.48 16.55 35.17 +GCT 147 32.17 20.98 16.08 30.77 +GCT 148 26.67 23.70 20.74 28.89 +GCT 149 40.16 16.54 18.11 25.20 +GCT 150 33.33 9.91 18.92 37.84 +GCT 151 24.49 0.00 32.65 42.86 +# ACGT content per cycle for first fragments. Use `grep ^FBC | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%]; and N and O counts as a percentage of all A/C/G/T bases [%] +FBC 1 20.00 26.00 32.00 22.00 0.00 0.00 +FBC 2 34.00 16.00 18.00 32.00 0.00 0.00 +FBC 3 35.00 17.00 16.00 32.00 0.00 0.00 +FBC 4 27.00 22.00 22.00 29.00 0.00 0.00 +FBC 5 33.00 10.00 14.00 43.00 0.00 0.00 +FBC 6 30.00 18.00 13.00 39.00 0.00 0.00 +FBC 7 27.00 22.00 21.00 30.00 0.00 0.00 +FBC 8 35.00 20.00 20.00 25.00 0.00 0.00 +FBC 9 23.00 34.00 23.00 20.00 0.00 0.00 +FBC 10 33.00 13.00 14.00 40.00 0.00 0.00 +FBC 11 33.00 17.00 21.00 29.00 0.00 0.00 +FBC 12 35.00 21.00 11.00 33.00 0.00 0.00 +FBC 13 31.00 20.00 21.00 28.00 0.00 0.00 +FBC 14 26.00 23.00 21.00 30.00 0.00 0.00 +FBC 15 25.00 24.00 18.00 33.00 0.00 0.00 +FBC 16 32.00 24.00 23.00 21.00 0.00 0.00 +FBC 17 27.00 13.00 21.00 39.00 0.00 0.00 +FBC 18 26.00 28.00 15.00 31.00 0.00 0.00 +FBC 19 24.00 18.00 19.00 39.00 0.00 0.00 +FBC 20 29.00 16.00 22.00 33.00 0.00 0.00 +FBC 21 21.00 20.00 13.00 46.00 0.00 0.00 +FBC 22 32.00 17.00 21.00 30.00 0.00 0.00 +FBC 23 33.00 13.00 24.00 30.00 0.00 0.00 +FBC 24 34.00 16.00 17.00 33.00 0.00 0.00 +FBC 25 27.00 18.00 22.00 33.00 0.00 0.00 +FBC 26 31.00 15.00 23.00 31.00 0.00 0.00 +FBC 27 29.00 18.00 20.00 33.00 0.00 0.00 +FBC 28 23.00 21.00 20.00 36.00 0.00 0.00 +FBC 29 26.00 14.00 24.00 36.00 0.00 0.00 +FBC 30 26.00 21.00 23.00 30.00 0.00 0.00 +FBC 31 25.00 19.00 22.00 34.00 0.00 0.00 +FBC 32 30.00 21.00 15.00 34.00 0.00 0.00 +FBC 33 31.00 16.00 22.00 31.00 0.00 0.00 +FBC 34 29.00 19.00 22.00 30.00 0.00 0.00 +FBC 35 38.00 13.00 27.00 22.00 0.00 0.00 +FBC 36 33.00 13.00 20.00 34.00 0.00 0.00 +FBC 37 32.00 14.00 18.00 36.00 0.00 0.00 +FBC 38 31.00 22.00 17.00 30.00 0.00 0.00 +FBC 39 32.00 18.00 16.00 34.00 0.00 0.00 +FBC 40 28.00 23.00 20.00 29.00 0.00 0.00 +FBC 41 41.00 14.00 16.00 29.00 0.00 0.00 +FBC 42 27.00 20.00 21.00 32.00 0.00 0.00 +FBC 43 35.00 23.00 14.00 28.00 0.00 0.00 +FBC 44 33.00 14.00 18.00 35.00 0.00 0.00 +FBC 45 30.00 18.00 19.00 33.00 0.00 0.00 +FBC 46 26.00 22.00 24.00 28.00 0.00 0.00 +FBC 47 25.00 26.00 22.00 27.00 0.00 0.00 +FBC 48 27.00 15.00 24.00 34.00 0.00 0.00 +FBC 49 23.00 20.00 21.00 36.00 0.00 0.00 +FBC 50 30.00 14.00 26.00 30.00 0.00 0.00 +FBC 51 32.00 15.00 15.00 38.00 0.00 0.00 +FBC 52 31.00 20.00 19.00 30.00 0.00 0.00 +FBC 53 28.00 17.00 28.00 27.00 0.00 0.00 +FBC 54 28.00 24.00 21.00 27.00 0.00 0.00 +FBC 55 23.00 25.00 20.00 32.00 0.00 0.00 +FBC 56 31.00 19.00 22.00 28.00 0.00 0.00 +FBC 57 33.00 19.00 18.00 30.00 0.00 0.00 +FBC 58 34.00 16.00 25.00 25.00 0.00 0.00 +FBC 59 35.00 22.00 17.00 26.00 0.00 0.00 +FBC 60 24.00 22.00 24.00 30.00 0.00 0.00 +FBC 61 22.00 25.00 27.00 26.00 0.00 0.00 +FBC 62 23.00 30.00 20.00 27.00 0.00 0.00 +FBC 63 30.00 10.00 22.00 38.00 0.00 0.00 +FBC 64 25.00 17.00 20.00 38.00 0.00 0.00 +FBC 65 25.00 24.00 21.00 30.00 0.00 0.00 +FBC 66 33.00 12.00 19.00 36.00 0.00 0.00 +FBC 67 23.00 22.00 19.00 36.00 0.00 0.00 +FBC 68 23.00 21.00 25.00 31.00 0.00 0.00 +FBC 69 31.00 17.00 24.00 28.00 0.00 0.00 +FBC 70 31.00 18.00 27.00 24.00 0.00 0.00 +FBC 71 42.00 17.00 15.00 26.00 0.00 0.00 +FBC 72 34.00 15.00 23.00 28.00 0.00 0.00 +FBC 73 31.31 23.23 19.19 26.26 0.00 0.00 +FBC 74 21.21 22.22 26.26 30.30 0.00 0.00 +FBC 75 32.32 15.15 20.20 32.32 0.00 0.00 +FBC 76 29.29 13.13 17.17 40.40 0.00 0.00 +FBC 77 26.26 18.18 21.21 34.34 0.00 0.00 +FBC 78 28.87 17.53 22.68 30.93 0.00 0.00 +FBC 79 32.99 20.62 20.62 25.77 0.00 0.00 +FBC 80 29.47 16.84 26.32 27.37 0.00 0.00 +FBC 81 32.98 12.77 12.77 41.49 0.00 0.00 +FBC 82 37.23 20.21 21.28 21.28 0.00 0.00 +FBC 83 31.91 23.40 18.09 26.60 0.00 0.00 +FBC 84 24.47 23.40 14.89 37.23 0.00 0.00 +FBC 85 36.17 18.09 20.21 25.53 0.00 0.00 +FBC 86 25.53 19.15 20.21 35.11 0.00 0.00 +FBC 87 29.79 18.09 13.83 38.30 0.00 0.00 +FBC 88 32.98 28.72 15.96 22.34 0.00 0.00 +FBC 89 24.47 20.21 15.96 39.36 0.00 0.00 +FBC 90 31.18 19.35 13.98 35.48 0.00 0.00 +FBC 91 25.81 19.35 18.28 36.56 0.00 0.00 +FBC 92 30.11 18.28 18.28 33.33 0.00 0.00 +FBC 93 28.26 13.04 20.65 38.04 0.00 0.00 +FBC 94 31.52 18.48 20.65 29.35 0.00 0.00 +FBC 95 26.37 21.98 21.98 29.67 0.00 0.00 +FBC 96 24.44 17.78 23.33 34.44 0.00 0.00 +FBC 97 17.78 17.78 21.11 43.33 0.00 0.00 +FBC 98 26.67 13.33 14.44 45.56 0.00 0.00 +FBC 99 27.27 20.45 19.32 32.95 0.00 0.00 +FBC 100 36.36 13.64 22.73 27.27 0.00 0.00 +FBC 101 40.91 15.91 17.05 26.14 0.00 0.00 +FBC 102 28.41 23.86 22.73 25.00 0.00 0.00 +FBC 103 30.68 19.32 18.18 31.82 0.00 0.00 +FBC 104 18.18 18.18 25.00 38.64 0.00 0.00 +FBC 105 30.68 10.23 19.32 39.77 0.00 0.00 +FBC 106 36.36 15.91 21.59 26.14 0.00 0.00 +FBC 107 25.58 15.12 19.77 39.53 0.00 0.00 +FBC 108 32.94 18.82 12.94 35.29 0.00 0.00 +FBC 109 28.24 29.41 17.65 24.71 0.00 0.00 +FBC 110 28.24 10.59 24.71 36.47 0.00 0.00 +FBC 111 34.12 14.12 25.88 25.88 0.00 0.00 +FBC 112 23.53 21.18 28.24 27.06 0.00 0.00 +FBC 113 21.18 21.18 23.53 34.12 0.00 0.00 +FBC 114 23.53 23.53 16.47 36.47 0.00 0.00 +FBC 115 30.59 27.06 12.94 29.41 0.00 0.00 +FBC 116 24.71 15.29 29.41 30.59 0.00 0.00 +FBC 117 29.41 27.06 12.94 30.59 0.00 0.00 +FBC 118 24.71 27.06 15.29 32.94 0.00 0.00 +FBC 119 27.06 22.35 22.35 28.24 0.00 0.00 +FBC 120 36.90 20.24 14.29 28.57 0.00 0.00 +FBC 121 33.33 20.24 15.48 30.95 0.00 0.00 +FBC 122 35.71 20.24 14.29 29.76 0.00 0.00 +FBC 123 24.10 25.30 16.87 33.73 0.00 0.00 +FBC 124 27.71 24.10 19.28 28.92 0.00 0.00 +FBC 125 26.51 16.87 19.28 37.35 0.00 0.00 +FBC 126 41.46 15.85 13.41 29.27 0.00 0.00 +FBC 127 28.05 18.29 24.39 29.27 0.00 0.00 +FBC 128 20.99 20.99 22.22 35.80 0.00 0.00 +FBC 129 22.22 13.58 22.22 41.98 0.00 0.00 +FBC 130 32.50 10.00 26.25 31.25 0.00 0.00 +FBC 131 26.25 15.00 26.25 32.50 0.00 0.00 +FBC 132 30.00 18.75 21.25 30.00 0.00 0.00 +FBC 133 32.91 20.25 17.72 29.11 0.00 0.00 +FBC 134 29.11 15.19 25.32 30.38 0.00 0.00 +FBC 135 31.65 18.99 18.99 30.38 0.00 0.00 +FBC 136 34.18 18.99 25.32 21.52 0.00 0.00 +FBC 137 29.11 10.13 25.32 35.44 0.00 0.00 +FBC 138 25.32 24.05 17.72 32.91 0.00 0.00 +FBC 139 25.32 25.32 18.99 30.38 0.00 0.00 +FBC 140 29.87 24.68 19.48 25.97 0.00 0.00 +FBC 141 29.87 22.08 18.18 29.87 0.00 0.00 +FBC 142 27.63 15.79 30.26 26.32 0.00 0.00 +FBC 143 27.03 18.92 24.32 29.73 0.00 0.00 +FBC 144 28.38 18.92 18.92 33.78 0.00 0.00 +FBC 145 32.43 16.22 14.86 36.49 0.00 0.00 +FBC 146 36.49 13.51 16.22 33.78 0.00 0.00 +FBC 147 34.72 22.22 13.89 29.17 0.00 0.00 +FBC 148 26.87 20.90 26.87 25.37 0.00 0.00 +FBC 149 31.25 12.50 25.00 31.25 0.00 0.00 +FBC 150 32.73 16.36 10.91 40.00 0.00 0.00 +FBC 151 48.28 17.24 13.79 20.69 0.00 0.00 +# ACGT raw counters for first fragments. Use `grep ^FTC | cut -f 2-` to extract this part. The columns are: A,C,G,T,N base counters +FTC 4077 2634 2796 4390 0 +# ACGT content per cycle for last fragments. Use `grep ^LBC | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%]; and N and O counts as a percentage of all A/C/G/T bases [%] +LBC 1 19.00 27.00 31.00 23.00 0.00 0.00 +LBC 2 27.00 25.00 16.00 32.00 0.00 0.00 +LBC 3 29.00 13.00 17.00 41.00 0.00 0.00 +LBC 4 34.00 20.00 13.00 33.00 0.00 0.00 +LBC 5 46.00 9.00 11.00 34.00 0.00 0.00 +LBC 6 26.00 17.00 24.00 33.00 0.00 0.00 +LBC 7 32.00 17.00 21.00 30.00 0.00 0.00 +LBC 8 24.00 22.00 26.00 28.00 0.00 0.00 +LBC 9 21.00 31.00 31.00 17.00 0.00 0.00 +LBC 10 39.00 11.00 18.00 32.00 0.00 0.00 +LBC 11 23.00 20.00 20.00 37.00 0.00 0.00 +LBC 12 32.00 21.00 21.00 26.00 0.00 0.00 +LBC 13 25.00 18.00 34.00 23.00 0.00 0.00 +LBC 14 23.00 20.00 17.00 40.00 0.00 0.00 +LBC 15 34.00 9.00 22.00 35.00 0.00 0.00 +LBC 16 30.00 16.00 20.00 34.00 0.00 0.00 +LBC 17 28.00 20.00 18.00 34.00 0.00 0.00 +LBC 18 35.00 20.00 24.00 21.00 0.00 0.00 +LBC 19 23.00 25.00 16.00 36.00 0.00 0.00 +LBC 20 34.00 18.00 21.00 27.00 0.00 0.00 +LBC 21 31.00 24.00 22.00 23.00 0.00 0.00 +LBC 22 29.00 21.00 25.00 25.00 0.00 0.00 +LBC 23 30.00 18.00 21.00 31.00 0.00 0.00 +LBC 24 30.00 20.00 25.00 25.00 0.00 0.00 +LBC 25 28.00 15.00 22.00 35.00 0.00 0.00 +LBC 26 24.00 22.00 24.00 30.00 0.00 0.00 +LBC 27 28.00 20.00 19.00 33.00 0.00 0.00 +LBC 28 22.00 21.00 25.00 32.00 0.00 0.00 +LBC 29 28.00 23.00 20.00 29.00 0.00 0.00 +LBC 30 35.00 19.00 20.00 26.00 0.00 0.00 +LBC 31 24.00 23.00 26.00 27.00 0.00 0.00 +LBC 32 35.00 14.00 18.00 33.00 0.00 0.00 +LBC 33 26.00 16.00 28.00 30.00 0.00 0.00 +LBC 34 29.00 23.00 25.00 23.00 0.00 0.00 +LBC 35 27.00 24.00 15.00 34.00 0.00 0.00 +LBC 36 37.00 12.00 20.00 31.00 0.00 0.00 +LBC 37 21.00 26.00 19.00 34.00 0.00 0.00 +LBC 38 23.00 20.00 22.00 35.00 0.00 0.00 +LBC 39 30.00 22.00 22.00 26.00 0.00 0.00 +LBC 40 27.00 17.00 23.00 33.00 0.00 0.00 +LBC 41 33.00 19.00 22.00 26.00 0.00 0.00 +LBC 42 26.00 19.00 16.00 39.00 0.00 0.00 +LBC 43 32.00 17.00 21.00 30.00 0.00 0.00 +LBC 44 30.00 18.00 24.00 28.00 0.00 0.00 +LBC 45 27.00 20.00 21.00 32.00 0.00 0.00 +LBC 46 23.00 25.00 11.00 41.00 0.00 0.00 +LBC 47 20.00 23.00 17.00 40.00 0.00 0.00 +LBC 48 28.00 20.00 21.00 31.00 0.00 0.00 +LBC 49 34.00 14.00 19.00 33.00 0.00 0.00 +LBC 50 34.00 19.00 14.00 33.00 0.00 0.00 +LBC 51 23.00 26.00 27.00 24.00 0.00 0.00 +LBC 52 24.00 23.00 20.00 33.00 0.00 0.00 +LBC 53 24.00 21.00 23.00 32.00 0.00 0.00 +LBC 54 33.33 23.23 12.12 31.31 0.00 0.00 +LBC 55 36.36 18.18 20.20 25.25 0.00 0.00 +LBC 56 33.33 14.14 22.22 30.30 0.00 0.00 +LBC 57 24.24 22.22 24.24 29.29 0.00 0.00 +LBC 58 25.25 13.13 24.24 37.37 0.00 0.00 +LBC 59 27.27 21.21 18.18 33.33 0.00 0.00 +LBC 60 33.33 13.13 20.20 33.33 0.00 0.00 +LBC 61 28.28 18.18 18.18 35.35 0.00 0.00 +LBC 62 31.31 22.22 23.23 23.23 0.00 0.00 +LBC 63 28.28 19.19 15.15 37.37 0.00 0.00 +LBC 64 33.33 13.13 23.23 30.30 0.00 0.00 +LBC 65 32.32 16.16 17.17 34.34 0.00 0.00 +LBC 66 30.30 26.26 13.13 30.30 0.00 0.00 +LBC 67 26.53 18.37 29.59 25.51 0.00 0.00 +LBC 68 30.61 18.37 21.43 29.59 0.00 0.00 +LBC 69 30.93 17.53 21.65 29.90 0.00 0.00 +LBC 70 36.46 15.62 16.67 31.25 0.00 0.00 +LBC 71 28.12 23.96 22.92 25.00 0.00 0.00 +LBC 72 33.33 16.67 14.58 35.42 0.00 0.00 +LBC 73 33.33 13.54 17.71 35.42 0.00 0.00 +LBC 74 34.38 14.58 21.88 29.17 0.00 0.00 +LBC 75 32.29 14.58 22.92 30.21 0.00 0.00 +LBC 76 19.79 27.08 25.00 28.12 0.00 0.00 +LBC 77 33.33 16.67 14.58 35.42 0.00 0.00 +LBC 78 20.00 24.21 11.58 44.21 0.00 0.00 +LBC 79 33.68 21.05 18.95 26.32 0.00 0.00 +LBC 80 32.63 15.79 17.89 33.68 0.00 0.00 +LBC 81 33.68 18.95 17.89 29.47 0.00 0.00 +LBC 82 26.32 18.95 17.89 36.84 0.00 0.00 +LBC 83 28.72 20.21 19.15 31.91 0.00 0.00 +LBC 84 30.85 20.21 17.02 31.91 0.00 0.00 +LBC 85 15.96 12.77 24.47 46.81 0.00 0.00 +LBC 86 24.47 17.02 23.40 35.11 0.00 0.00 +LBC 87 31.91 18.09 17.02 32.98 0.00 0.00 +LBC 88 31.91 21.28 20.21 26.60 0.00 0.00 +LBC 89 24.47 10.64 23.40 41.49 0.00 0.00 +LBC 90 23.40 24.47 27.66 24.47 0.00 0.00 +LBC 91 30.85 10.64 23.40 35.11 0.00 0.00 +LBC 92 26.60 18.09 22.34 32.98 0.00 0.00 +LBC 93 29.03 23.66 16.13 31.18 0.00 0.00 +LBC 94 26.88 17.20 20.43 35.48 0.00 0.00 +LBC 95 29.03 25.81 20.43 24.73 0.00 0.00 +LBC 96 38.04 19.57 9.78 32.61 0.00 0.00 +LBC 97 26.09 17.39 21.74 34.78 0.00 0.00 +LBC 98 28.26 18.48 22.83 30.43 0.00 0.00 +LBC 99 27.78 20.00 16.67 35.56 0.00 0.00 +LBC 100 33.33 16.67 16.67 33.33 0.00 0.00 +LBC 101 32.22 17.78 23.33 26.67 0.00 0.00 +LBC 102 30.68 20.45 23.86 25.00 0.00 0.00 +LBC 103 25.00 18.18 20.45 36.36 0.00 0.00 +LBC 104 34.09 11.36 19.32 35.23 0.00 0.00 +LBC 105 36.36 12.50 20.45 30.68 0.00 0.00 +LBC 106 19.54 24.14 17.24 39.08 0.00 0.00 +LBC 107 26.19 17.86 28.57 27.38 0.00 0.00 +LBC 108 28.57 22.62 19.05 29.76 0.00 0.00 +LBC 109 25.00 30.95 15.48 28.57 0.00 0.00 +LBC 110 27.38 8.33 22.62 41.67 0.00 0.00 +LBC 111 26.19 19.05 21.43 33.33 0.00 0.00 +LBC 112 33.33 21.43 21.43 23.81 0.00 0.00 +LBC 113 36.14 18.07 20.48 25.30 0.00 0.00 +LBC 114 39.76 22.89 19.28 18.07 0.00 0.00 +LBC 115 39.76 12.05 18.07 30.12 0.00 0.00 +LBC 116 28.92 20.48 15.66 34.94 0.00 0.00 +LBC 117 40.24 18.29 15.85 25.61 0.00 0.00 +LBC 118 29.63 19.75 14.81 35.80 0.00 0.00 +LBC 119 37.04 16.05 18.52 28.40 0.00 0.00 +LBC 120 33.33 13.58 22.22 30.86 0.00 0.00 +LBC 121 19.75 29.63 20.99 29.63 0.00 0.00 +LBC 122 32.10 14.81 24.69 28.40 0.00 0.00 +LBC 123 35.00 13.75 20.00 31.25 0.00 0.00 +LBC 124 21.25 20.00 27.50 31.25 0.00 0.00 +LBC 125 30.00 17.50 22.50 30.00 0.00 0.00 +LBC 126 40.51 18.99 18.99 21.52 0.00 0.00 +LBC 127 29.49 17.95 14.10 38.46 0.00 0.00 +LBC 128 29.49 23.08 17.95 29.49 0.00 0.00 +LBC 129 24.36 20.51 24.36 30.77 0.00 0.00 +LBC 130 33.77 15.58 22.08 28.57 0.00 0.00 +LBC 131 24.68 18.18 16.88 40.26 0.00 0.00 +LBC 132 32.47 19.48 23.38 24.68 0.00 0.00 +LBC 133 28.95 18.42 21.05 31.58 0.00 0.00 +LBC 134 36.84 14.47 21.05 27.63 0.00 0.00 +LBC 135 32.89 18.42 17.11 31.58 0.00 0.00 +LBC 136 34.21 19.74 19.74 26.32 0.00 0.00 +LBC 137 25.33 26.67 14.67 33.33 0.00 0.00 +LBC 138 36.00 12.00 17.33 34.67 0.00 0.00 +LBC 139 28.00 18.67 20.00 33.33 0.00 0.00 +LBC 140 25.68 24.32 20.27 29.73 0.00 0.00 +LBC 141 26.03 24.66 24.66 24.66 0.00 0.00 +LBC 142 31.51 15.07 26.03 27.40 0.00 0.00 +LBC 143 22.22 11.11 23.61 43.06 0.00 0.00 +LBC 144 26.39 13.89 20.83 38.89 0.00 0.00 +LBC 145 26.39 11.11 25.00 37.50 0.00 0.00 +LBC 146 35.21 11.27 21.13 32.39 0.00 0.00 +LBC 147 30.99 19.72 18.31 30.99 0.00 0.00 +LBC 148 35.29 20.59 20.59 23.53 0.00 0.00 +LBC 149 34.92 17.46 14.29 33.33 0.00 0.00 +LBC 150 41.07 12.50 17.86 28.57 0.00 0.00 +LBC 151 30.00 20.00 15.00 35.00 0.00 0.00 +# ACGT raw counters for last fragments. Use `grep ^LTC | cut -f 2-` to extract this part. The columns are: A,C,G,T,N base counters +LTC 4051 2592 2808 4297 0 +# Insert sizes. Use `grep ^IS | cut -f 2-` to extract this part. The columns are: insert size, pairs total, inward oriented pairs, outward oriented pairs, other pairs +IS 0 0 0 0 0 +IS 1 0 0 0 0 +IS 2 0 0 0 0 +IS 3 0 0 0 0 +IS 4 0 0 0 0 +IS 5 0 0 0 0 +IS 6 0 0 0 0 +IS 7 0 0 0 0 +IS 8 0 0 0 0 +IS 9 0 0 0 0 +IS 10 0 0 0 0 +IS 11 0 0 0 0 +IS 12 0 0 0 0 +IS 13 0 0 0 0 +IS 14 0 0 0 0 +IS 15 0 0 0 0 +IS 16 0 0 0 0 +IS 17 0 0 0 0 +IS 18 0 0 0 0 +IS 19 0 0 0 0 +IS 20 0 0 0 0 +IS 21 0 0 0 0 +IS 22 0 0 0 0 +IS 23 0 0 0 0 +IS 24 0 0 0 0 +IS 25 0 0 0 0 +IS 26 0 0 0 0 +IS 27 0 0 0 0 +IS 28 0 0 0 0 +IS 29 0 0 0 0 +IS 30 0 0 0 0 +IS 31 0 0 0 0 +IS 32 0 0 0 0 +IS 33 0 0 0 0 +IS 34 0 0 0 0 +IS 35 0 0 0 0 +IS 36 0 0 0 0 +IS 37 0 0 0 0 +IS 38 0 0 0 0 +IS 39 0 0 0 0 +IS 40 0 0 0 0 +IS 41 0 0 0 0 +IS 42 0 0 0 0 +IS 43 0 0 0 0 +IS 44 0 0 0 0 +IS 45 0 0 0 0 +IS 46 0 0 0 0 +IS 47 0 0 0 0 +IS 48 0 0 0 0 +IS 49 0 0 0 0 +IS 50 0 0 0 0 +IS 51 0 0 0 0 +IS 52 0 0 0 0 +IS 53 0 0 0 0 +IS 54 0 0 0 0 +IS 55 0 0 0 0 +IS 56 0 0 0 0 +IS 57 0 0 0 0 +IS 58 0 0 0 0 +IS 59 0 0 0 0 +IS 60 0 0 0 0 +IS 61 0 0 0 0 +IS 62 0 0 0 0 +IS 63 0 0 0 0 +IS 64 0 0 0 0 +IS 65 0 0 0 0 +IS 66 0 0 0 0 +IS 67 0 0 0 0 +IS 68 0 0 0 0 +IS 69 0 0 0 0 +IS 70 0 0 0 0 +IS 71 0 0 0 0 +IS 72 0 0 0 0 +IS 73 0 0 0 0 +IS 74 0 0 0 0 +IS 75 0 0 0 0 +IS 76 0 0 0 0 +IS 77 1 0 1 0 +IS 78 0 0 0 0 +IS 79 0 0 0 0 +IS 80 0 0 0 0 +IS 81 0 0 0 0 +IS 82 1 1 0 0 +IS 83 0 0 0 0 +IS 84 0 0 0 0 +IS 85 0 0 0 0 +IS 86 1 1 0 0 +IS 87 0 0 0 0 +IS 88 0 0 0 0 +IS 89 0 0 0 0 +IS 90 0 0 0 0 +IS 91 0 0 0 0 +IS 92 1 1 0 0 +IS 93 0 0 0 0 +IS 94 0 0 0 0 +IS 95 0 0 0 0 +IS 96 0 0 0 0 +IS 97 0 0 0 0 +IS 98 2 1 1 0 +IS 99 0 0 0 0 +IS 100 0 0 0 0 +IS 101 0 0 0 0 +IS 102 0 0 0 0 +IS 103 0 0 0 0 +IS 104 0 0 0 0 +IS 105 0 0 0 0 +IS 106 2 1 1 0 +IS 107 1 1 0 0 +IS 108 0 0 0 0 +IS 109 0 0 0 0 +IS 110 0 0 0 0 +IS 111 0 0 0 0 +IS 112 1 1 0 0 +IS 113 0 0 0 0 +IS 114 0 0 0 0 +IS 115 0 0 0 0 +IS 116 0 0 0 0 +IS 117 0 0 0 0 +IS 118 1 1 0 0 +IS 119 0 0 0 0 +IS 120 0 0 0 0 +IS 121 0 0 0 0 +IS 122 1 0 1 0 +IS 123 0 0 0 0 +IS 124 0 0 0 0 +IS 125 1 0 1 0 +IS 126 0 0 0 0 +IS 127 1 0 1 0 +IS 128 0 0 0 0 +IS 129 1 0 1 0 +IS 130 0 0 0 0 +IS 131 0 0 0 0 +IS 132 1 1 0 0 +IS 133 0 0 0 0 +IS 134 0 0 0 0 +IS 135 0 0 0 0 +IS 136 0 0 0 0 +IS 137 0 0 0 0 +IS 138 0 0 0 0 +IS 139 1 1 0 0 +IS 140 1 1 0 0 +IS 141 0 0 0 0 +IS 142 1 0 1 0 +IS 143 0 0 0 0 +IS 144 0 0 0 0 +IS 145 0 0 0 0 +IS 146 0 0 0 0 +IS 147 1 1 0 0 +IS 148 1 0 1 0 +IS 149 0 0 0 0 +IS 150 1 1 0 0 +IS 151 0 0 0 0 +IS 152 0 0 0 0 +IS 153 0 0 0 0 +IS 154 0 0 0 0 +IS 155 0 0 0 0 +IS 156 0 0 0 0 +IS 157 0 0 0 0 +IS 158 1 1 0 0 +IS 159 3 3 0 0 +IS 160 0 0 0 0 +IS 161 0 0 0 0 +IS 162 0 0 0 0 +IS 163 0 0 0 0 +IS 164 0 0 0 0 +IS 165 0 0 0 0 +IS 166 2 2 0 0 +IS 167 0 0 0 0 +IS 168 2 2 0 0 +IS 169 0 0 0 0 +IS 170 0 0 0 0 +IS 171 1 1 0 0 +IS 172 1 1 0 0 +IS 173 0 0 0 0 +IS 174 1 1 0 0 +IS 175 0 0 0 0 +IS 176 0 0 0 0 +IS 177 1 1 0 0 +IS 178 1 1 0 0 +IS 179 0 0 0 0 +IS 180 2 2 0 0 +IS 181 0 0 0 0 +IS 182 0 0 0 0 +IS 183 0 0 0 0 +IS 184 0 0 0 0 +IS 185 1 1 0 0 +IS 186 0 0 0 0 +IS 187 1 1 0 0 +IS 188 0 0 0 0 +IS 189 1 1 0 0 +IS 190 0 0 0 0 +IS 191 1 1 0 0 +IS 192 0 0 0 0 +IS 193 0 0 0 0 +IS 194 0 0 0 0 +IS 195 1 1 0 0 +IS 196 0 0 0 0 +IS 197 1 1 0 0 +IS 198 1 1 0 0 +IS 199 0 0 0 0 +IS 200 0 0 0 0 +IS 201 2 2 0 0 +IS 202 1 1 0 0 +IS 203 0 0 0 0 +IS 204 1 1 0 0 +IS 205 0 0 0 0 +IS 206 0 0 0 0 +IS 207 0 0 0 0 +IS 208 0 0 0 0 +IS 209 1 1 0 0 +IS 210 0 0 0 0 +IS 211 0 0 0 0 +IS 212 0 0 0 0 +IS 213 0 0 0 0 +IS 214 1 1 0 0 +IS 215 0 0 0 0 +IS 216 0 0 0 0 +IS 217 0 0 0 0 +IS 218 1 1 0 0 +IS 219 1 1 0 0 +IS 220 0 0 0 0 +IS 221 0 0 0 0 +IS 222 1 1 0 0 +IS 223 0 0 0 0 +IS 224 0 0 0 0 +IS 225 0 0 0 0 +IS 226 0 0 0 0 +IS 227 1 1 0 0 +IS 228 0 0 0 0 +IS 229 0 0 0 0 +IS 230 0 0 0 0 +IS 231 1 1 0 0 +IS 232 1 1 0 0 +IS 233 1 1 0 0 +IS 234 2 2 0 0 +IS 235 3 3 0 0 +IS 236 1 1 0 0 +IS 237 0 0 0 0 +IS 238 2 2 0 0 +IS 239 0 0 0 0 +IS 240 1 1 0 0 +IS 241 0 0 0 0 +IS 242 0 0 0 0 +IS 243 0 0 0 0 +IS 244 1 1 0 0 +IS 245 1 1 0 0 +IS 246 1 1 0 0 +IS 247 2 2 0 0 +IS 248 0 0 0 0 +IS 249 1 1 0 0 +IS 250 0 0 0 0 +IS 251 1 1 0 0 +IS 252 0 0 0 0 +IS 253 0 0 0 0 +IS 254 1 1 0 0 +IS 255 1 1 0 0 +IS 256 0 0 0 0 +IS 257 0 0 0 0 +IS 258 0 0 0 0 +IS 259 1 1 0 0 +IS 260 0 0 0 0 +IS 261 0 0 0 0 +IS 262 0 0 0 0 +IS 263 0 0 0 0 +IS 264 0 0 0 0 +IS 265 0 0 0 0 +IS 266 1 1 0 0 +IS 267 1 1 0 0 +IS 268 1 1 0 0 +IS 269 0 0 0 0 +IS 270 0 0 0 0 +IS 271 0 0 0 0 +IS 272 2 2 0 0 +IS 273 0 0 0 0 +IS 274 0 0 0 0 +IS 275 0 0 0 0 +IS 276 1 1 0 0 +IS 277 0 0 0 0 +IS 278 1 1 0 0 +IS 279 0 0 0 0 +IS 280 0 0 0 0 +IS 281 1 1 0 0 +IS 282 1 1 0 0 +IS 283 0 0 0 0 +IS 284 1 1 0 0 +IS 285 0 0 0 0 +IS 286 0 0 0 0 +IS 287 0 0 0 0 +IS 288 0 0 0 0 +IS 289 0 0 0 0 +IS 290 0 0 0 0 +IS 291 1 1 0 0 +IS 292 0 0 0 0 +IS 293 0 0 0 0 +IS 294 1 1 0 0 +IS 295 0 0 0 0 +IS 296 0 0 0 0 +IS 297 0 0 0 0 +IS 298 0 0 0 0 +IS 299 0 0 0 0 +IS 300 0 0 0 0 +IS 301 0 0 0 0 +IS 302 0 0 0 0 +IS 303 0 0 0 0 +IS 304 1 1 0 0 +IS 305 1 1 0 0 +IS 306 0 0 0 0 +IS 307 0 0 0 0 +IS 308 0 0 0 0 +IS 309 0 0 0 0 +IS 310 1 1 0 0 +IS 311 0 0 0 0 +IS 312 0 0 0 0 +IS 313 0 0 0 0 +IS 314 1 1 0 0 +IS 315 0 0 0 0 +IS 316 0 0 0 0 +IS 317 0 0 0 0 +IS 318 1 1 0 0 +IS 319 0 0 0 0 +IS 320 1 1 0 0 +IS 321 0 0 0 0 +IS 322 0 0 0 0 +IS 323 0 0 0 0 +IS 324 0 0 0 0 +IS 325 0 0 0 0 +IS 326 0 0 0 0 +IS 327 0 0 0 0 +IS 328 0 0 0 0 +IS 329 0 0 0 0 +IS 330 0 0 0 0 +IS 331 0 0 0 0 +IS 332 0 0 0 0 +IS 333 0 0 0 0 +IS 334 0 0 0 0 +IS 335 0 0 0 0 +IS 336 0 0 0 0 +IS 337 0 0 0 0 +IS 338 0 0 0 0 +IS 339 1 1 0 0 +IS 340 0 0 0 0 +IS 341 0 0 0 0 +IS 342 0 0 0 0 +IS 343 1 1 0 0 +IS 344 0 0 0 0 +IS 345 0 0 0 0 +IS 346 0 0 0 0 +IS 347 0 0 0 0 +IS 348 0 0 0 0 +IS 349 0 0 0 0 +IS 350 0 0 0 0 +IS 351 0 0 0 0 +IS 352 0 0 0 0 +IS 353 0 0 0 0 +IS 354 0 0 0 0 +IS 355 0 0 0 0 +IS 356 0 0 0 0 +IS 357 0 0 0 0 +IS 358 0 0 0 0 +IS 359 0 0 0 0 +IS 360 0 0 0 0 +IS 361 0 0 0 0 +IS 362 0 0 0 0 +IS 363 0 0 0 0 +IS 364 1 1 0 0 +# Read lengths. Use `grep ^RL | cut -f 2-` to extract this part. The columns are: read length, count +RL 53 1 +RL 66 1 +RL 68 1 +RL 69 1 +RL 72 1 +RL 77 3 +RL 79 2 +RL 80 1 +RL 82 1 +RL 89 1 +RL 92 2 +RL 94 1 +RL 95 2 +RL 98 4 +RL 101 2 +RL 105 1 +RL 106 5 +RL 107 1 +RL 112 1 +RL 116 1 +RL 117 1 +RL 119 1 +RL 122 2 +RL 125 2 +RL 126 1 +RL 127 1 +RL 129 2 +RL 132 2 +RL 136 1 +RL 139 3 +RL 140 1 +RL 141 1 +RL 142 3 +RL 145 1 +RL 146 2 +RL 147 8 +RL 148 8 +RL 149 16 +RL 150 62 +RL 151 49 +# Read lengths - first fragments. Use `grep ^FRL | cut -f 2-` to extract this part. The columns are: read length, count +FRL 72 1 +FRL 77 2 +FRL 79 2 +FRL 80 1 +FRL 89 1 +FRL 92 1 +FRL 94 1 +FRL 95 1 +FRL 98 2 +FRL 106 2 +FRL 107 1 +FRL 119 1 +FRL 122 1 +FRL 125 1 +FRL 127 1 +FRL 129 1 +FRL 132 1 +FRL 139 2 +FRL 141 1 +FRL 142 2 +FRL 146 2 +FRL 147 5 +FRL 148 3 +FRL 149 9 +FRL 150 26 +FRL 151 29 +# Read lengths - last fragments. Use `grep ^LRL | cut -f 2-` to extract this part. The columns are: read length, count +LRL 53 1 +LRL 66 1 +LRL 68 1 +LRL 69 1 +LRL 77 1 +LRL 82 1 +LRL 92 1 +LRL 95 1 +LRL 98 2 +LRL 101 2 +LRL 105 1 +LRL 106 3 +LRL 112 1 +LRL 116 1 +LRL 117 1 +LRL 122 1 +LRL 125 1 +LRL 126 1 +LRL 129 1 +LRL 132 1 +LRL 136 1 +LRL 139 1 +LRL 140 1 +LRL 142 1 +LRL 145 1 +LRL 147 3 +LRL 148 5 +LRL 149 7 +LRL 150 36 +LRL 151 20 +# Mapping qualities for reads !(UNMAP|SECOND|SUPPL|QCFAIL|DUP). Use `grep ^MAPQ | cut -f 2-` to extract this part. The columns are: mapq, count +MAPQ 1 1 +MAPQ 36 1 +MAPQ 37 1 +MAPQ 38 2 +MAPQ 48 14 +MAPQ 49 1 +MAPQ 50 5 +MAPQ 51 1 +MAPQ 52 1 +MAPQ 55 2 +MAPQ 57 1 +MAPQ 59 1 +MAPQ 60 166 +# Indel distribution. Use `grep ^ID | cut -f 2-` to extract this part. The columns are: length, number of insertions, number of deletions +ID 1 0 8 +ID 2 0 1 +ID 32 0 1 +# Indels per cycle. Use `grep ^IC | cut -f 2-` to extract this part. The columns are: cycle, number of insertions (fwd), .. (rev) , number of deletions (fwd), .. (rev) +IC 5 0 0 1 0 +IC 7 0 0 1 1 +IC 72 0 0 1 0 +IC 85 0 0 1 0 +IC 97 0 0 1 0 +IC 107 0 0 0 1 +IC 121 0 0 0 1 +IC 135 0 0 0 1 +IC 137 0 0 1 0 +# Coverage distribution. Use `grep ^COV | cut -f 2-` to extract this part. +COV [1-1] 1 8276 +COV [2-2] 2 2632 +COV [3-3] 3 1381 +COV [4-4] 4 365 +COV [5-5] 5 137 +COV [6-6] 6 60 +# GC-depth. Use `grep ^GCD | cut -f 2-` to extract this part. The columns are: GC%, unique sequence percentiles, 10th, 25th, 50th, 75th and 90th depth percentile +GCD 0.0 66.667 0.000 0.000 0.000 0.000 0.000 +GCD 19.2 100.000 0.318 0.318 0.318 0.318 0.318 diff --git a/src/samtools/samtools_stats/test_data/ref.paired_end.sorted.txt b/src/samtools/samtools_stats/test_data/ref.paired_end.sorted.txt new file mode 100644 index 00000000..7a1cda92 --- /dev/null +++ b/src/samtools/samtools_stats/test_data/ref.paired_end.sorted.txt @@ -0,0 +1,1539 @@ +# This file was produced by samtools stats (1.19.2+htslib-1.19.1) and can be plotted using plot-bamstats +# This file contains statistics for all reads. +# The command line was: stats test_data/test.paired_end.sorted.bam +# CHK, Checksum [2]Read Names [3]Sequences [4]Qualities +# CHK, CRC32 of reads which passed filtering followed by addition (32bit overflow) +CHK 696e2242 1799722a a8072f55 +# Summary Numbers. Use `grep ^SN | cut -f 2-` to extract this part. +SN raw total sequences: 200 # excluding supplementary and secondary reads +SN filtered sequences: 0 +SN sequences: 200 +SN is sorted: 1 +SN 1st fragments: 100 +SN last fragments: 100 +SN reads mapped: 197 +SN reads mapped and paired: 194 # paired-end technology bit set + both mates mapped +SN reads unmapped: 3 +SN reads properly paired: 192 # proper-pair bit set +SN reads paired: 200 # paired-end technology bit set +SN reads duplicated: 0 # PCR or optical duplicate bit set +SN reads MQ0: 0 # mapped and MQ=0 +SN reads QC failed: 0 +SN non-primary alignments: 0 +SN supplementary alignments: 0 +SN total length: 27645 # ignores clipping +SN total first fragment length: 13897 # ignores clipping +SN total last fragment length: 13748 # ignores clipping +SN bases mapped: 27423 # ignores clipping +SN bases mapped (cigar): 27401 # more accurate +SN bases trimmed: 0 +SN bases duplicated: 0 +SN mismatches: 140 # from NM fields +SN error rate: 5.109303e-03 # mismatches / bases mapped (cigar) +SN average length: 138 +SN average first fragment length: 139 +SN average last fragment length: 137 +SN maximum length: 151 +SN maximum first fragment length: 151 +SN maximum last fragment length: 151 +SN average quality: 33.3 +SN insert size average: 207.7 +SN insert size standard deviation: 66.4 +SN inward oriented pairs: 88 +SN outward oriented pairs: 9 +SN pairs with other orientation: 0 +SN pairs on different chromosomes: 0 +SN percentage of properly paired reads (%): 96.0 +# First Fragment Qualities. Use `grep ^FFQ | cut -f 2-` to extract this part. +# Columns correspond to qualities and rows to cycles. First column is the cycle number. +FFQ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100 0 0 0 0 0 +FFQ 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 96 0 0 0 0 0 +FFQ 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 97 0 0 0 0 0 +FFQ 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 94 0 0 0 1 0 +FFQ 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 93 0 0 0 0 0 +FFQ 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 7 0 0 0 86 0 +FFQ 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 7 0 0 0 84 0 +FFQ 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 12 0 0 0 83 0 +FFQ 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 85 0 +FFQ 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 5 0 0 0 87 0 +FFQ 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 90 0 +FFQ 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 88 0 +FFQ 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 8 0 0 0 84 0 +FFQ 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 6 0 0 0 86 0 +FFQ 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15 0 0 0 83 0 +FFQ 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0 90 0 +FFQ 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 86 0 +FFQ 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 93 0 +FFQ 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 2 0 0 0 86 0 +FFQ 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 4 0 0 0 85 0 +FFQ 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 95 0 +FFQ 22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 91 0 +FFQ 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 90 0 +FFQ 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 1 0 0 0 90 0 +FFQ 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 85 0 +FFQ 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 87 0 +FFQ 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 5 0 0 0 87 0 +FFQ 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 88 0 +FFQ 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 4 0 0 0 90 0 +FFQ 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 7 0 0 0 87 0 +FFQ 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 4 0 0 0 0 0 2 0 0 0 0 3 0 0 0 85 0 +FFQ 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 6 0 0 0 89 0 +FFQ 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 7 0 0 0 84 0 +FFQ 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 89 0 +FFQ 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 88 0 +FFQ 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 8 0 0 0 85 0 +FFQ 37 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 4 0 0 0 87 0 +FFQ 38 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 4 0 0 0 91 0 +FFQ 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 86 0 +FFQ 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 3 0 0 0 90 0 +FFQ 41 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 9 0 0 0 85 0 +FFQ 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 5 0 0 0 88 0 +FFQ 43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 4 0 0 0 83 0 +FFQ 44 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 8 0 0 0 83 0 +FFQ 45 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 86 0 +FFQ 46 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 9 0 0 0 85 0 +FFQ 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 10 0 0 0 77 0 +FFQ 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 12 0 0 0 80 0 +FFQ 49 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 8 0 0 0 79 0 +FFQ 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 81 0 +FFQ 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 12 0 0 0 83 0 +FFQ 52 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 12 0 0 0 80 0 +FFQ 53 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 15 0 0 0 77 0 +FFQ 54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 7 0 0 0 0 12 0 0 0 72 0 +FFQ 55 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 8 0 0 0 82 0 +FFQ 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 9 0 0 0 80 0 +FFQ 57 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 13 0 0 0 77 0 +FFQ 58 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 3 0 0 0 0 0 3 0 0 0 0 11 0 0 0 76 0 +FFQ 59 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 81 0 +FFQ 60 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 5 0 0 0 83 0 +FFQ 61 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 8 0 0 0 81 0 +FFQ 62 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 6 0 0 0 81 0 +FFQ 63 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 84 0 +FFQ 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 7 0 0 0 77 0 +FFQ 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 10 0 0 0 77 0 +FFQ 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 10 0 0 0 76 0 +FFQ 67 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 15 0 0 0 77 0 +FFQ 68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 10 0 0 0 81 0 +FFQ 69 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 5 0 0 0 0 4 0 0 0 82 0 +FFQ 70 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 3 0 0 0 0 0 4 0 0 0 0 7 0 0 0 78 0 +FFQ 71 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 9 0 0 0 79 0 +FFQ 72 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 12 0 0 0 81 0 +FFQ 73 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 9 0 0 0 78 0 +FFQ 74 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 82 0 +FFQ 75 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 12 0 0 0 78 0 +FFQ 76 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 6 0 0 0 80 0 +FFQ 77 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 79 0 +FFQ 78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 13 0 0 0 73 0 +FFQ 79 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 15 0 0 0 72 0 +FFQ 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 15 0 0 0 72 0 +FFQ 81 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 74 0 +FFQ 82 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 12 0 0 0 72 0 +FFQ 83 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 7 0 0 0 74 0 +FFQ 84 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 5 0 0 0 80 0 +FFQ 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 10 0 0 0 70 0 +FFQ 86 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 17 0 0 0 68 0 +FFQ 87 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 7 0 0 0 72 0 +FFQ 88 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 7 0 0 0 77 0 +FFQ 89 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 9 0 0 0 78 0 +FFQ 90 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 11 0 0 0 72 0 +FFQ 91 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 10 0 0 0 74 0 +FFQ 92 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 7 0 0 0 75 0 +FFQ 93 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 12 0 0 0 68 0 +FFQ 94 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 77 0 +FFQ 95 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 10 0 0 0 70 0 +FFQ 96 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 7 0 0 0 75 0 +FFQ 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 11 0 0 0 71 0 +FFQ 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 14 0 0 0 61 0 +FFQ 99 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 67 0 +FFQ 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 12 0 0 0 64 0 +FFQ 101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 5 0 0 0 67 0 +FFQ 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 9 0 0 0 68 0 +FFQ 103 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 14 0 0 0 61 0 +FFQ 104 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 17 0 0 0 59 0 +FFQ 105 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 19 0 0 0 56 0 +FFQ 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 16 0 0 0 57 0 +FFQ 107 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 17 0 0 0 58 0 +FFQ 108 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 15 0 0 0 56 0 +FFQ 109 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 15 0 0 0 52 0 +FFQ 110 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 19 0 0 0 50 0 +FFQ 111 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 19 0 0 0 52 0 +FFQ 112 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 16 0 0 0 55 0 +FFQ 113 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 8 0 0 0 0 22 0 0 0 45 0 +FFQ 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 9 0 0 0 0 22 0 0 0 45 0 +FFQ 115 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 14 0 0 0 0 16 0 0 0 48 0 +FFQ 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 8 0 0 0 0 27 0 0 0 43 0 +FFQ 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 21 0 0 0 49 0 +FFQ 118 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 24 0 0 0 44 0 +FFQ 119 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 23 0 0 0 43 0 +FFQ 120 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 28 0 0 0 48 0 +FFQ 121 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 18 0 0 0 48 0 +FFQ 122 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 6 0 0 0 0 17 0 0 0 54 0 +FFQ 123 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 20 0 0 0 52 0 +FFQ 124 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 20 0 0 0 49 0 +FFQ 125 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 29 0 0 0 42 0 +FFQ 126 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 2 0 0 0 0 0 5 0 0 0 0 22 0 0 0 42 0 +FFQ 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 5 0 0 0 0 0 7 0 0 0 0 20 0 0 0 42 0 +FFQ 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 2 0 0 0 0 0 8 0 0 0 0 20 0 0 0 42 0 +FFQ 129 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 20 0 0 0 48 0 +FFQ 130 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 21 0 0 0 45 0 +FFQ 131 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 20 0 0 0 49 0 +FFQ 132 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 5 0 0 0 0 25 0 0 0 41 0 +FFQ 133 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 18 0 0 0 47 0 +FFQ 134 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 6 0 0 0 0 21 0 0 0 43 0 +FFQ 135 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 9 0 0 0 0 26 0 0 0 37 0 +FFQ 136 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 15 0 0 0 45 0 +FFQ 137 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 19 0 0 0 46 0 +FFQ 138 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 28 0 0 0 34 0 +FFQ 139 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 23 0 0 0 34 0 +FFQ 140 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 19 0 0 0 46 0 +FFQ 141 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 4 0 0 0 0 0 6 0 0 0 0 18 0 0 0 40 0 +FFQ 142 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 24 0 0 0 37 0 +FFQ 143 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 19 0 0 0 34 0 +FFQ 144 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 16 0 0 0 35 0 +FFQ 145 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 34 0 0 0 34 0 +FFQ 146 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 7 0 0 0 0 21 0 0 0 37 0 +FFQ 147 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 16 0 0 0 43 0 +FFQ 148 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 23 0 0 0 33 0 +FFQ 149 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 22 0 0 0 35 0 +FFQ 150 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 18 0 0 0 36 0 +FFQ 151 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 18 0 0 0 11 0 +# Last Fragment Qualities. Use `grep ^LFQ | cut -f 2-` to extract this part. +# Columns correspond to qualities and rows to cycles. First column is the cycle number. +LFQ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100 0 0 0 0 0 +LFQ 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 95 0 0 0 0 0 +LFQ 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 93 0 0 0 0 0 +LFQ 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 94 0 0 0 0 0 +LFQ 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 94 0 0 0 1 0 +LFQ 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 10 0 0 0 83 0 +LFQ 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 93 0 +LFQ 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 3 0 0 0 91 0 +LFQ 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 87 0 +LFQ 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 4 0 0 0 90 0 +LFQ 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 4 0 0 0 90 0 +LFQ 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 7 0 0 0 83 0 +LFQ 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 2 0 0 0 90 0 +LFQ 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 10 0 0 0 86 0 +LFQ 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 6 0 0 0 87 0 +LFQ 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 10 0 0 0 84 0 +LFQ 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0 91 0 +LFQ 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 4 0 0 0 91 0 +LFQ 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 92 0 +LFQ 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 3 0 0 0 90 0 +LFQ 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 89 0 +LFQ 22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 3 0 0 0 88 0 +LFQ 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 89 0 +LFQ 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 88 0 +LFQ 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 9 0 0 0 84 0 +LFQ 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 4 0 0 0 89 0 +LFQ 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 8 0 0 0 87 0 +LFQ 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 2 0 0 0 90 0 +LFQ 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 5 0 0 0 86 0 +LFQ 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 5 0 0 0 88 0 +LFQ 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 92 0 +LFQ 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 5 0 0 0 86 0 +LFQ 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 1 0 0 0 89 0 +LFQ 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 84 0 +LFQ 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 4 0 0 0 87 0 +LFQ 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 8 0 0 0 82 0 +LFQ 37 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 8 0 0 0 83 0 +LFQ 38 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 8 0 0 0 85 0 +LFQ 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 7 0 0 0 85 0 +LFQ 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 6 0 0 0 88 0 +LFQ 41 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 11 0 0 0 78 0 +LFQ 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 87 0 +LFQ 43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 9 0 0 0 81 0 +LFQ 44 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 6 0 0 0 86 0 +LFQ 45 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 85 0 +LFQ 46 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 9 0 0 0 81 0 +LFQ 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 5 0 0 0 88 0 +LFQ 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 8 0 0 0 84 0 +LFQ 49 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 11 0 0 0 80 0 +LFQ 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 10 0 0 0 79 0 +LFQ 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 8 0 0 0 80 0 +LFQ 52 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 8 0 0 0 79 0 +LFQ 53 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 7 0 0 0 81 0 +LFQ 54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 15 0 0 0 79 0 +LFQ 55 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 8 0 0 0 85 0 +LFQ 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 8 0 0 0 80 0 +LFQ 57 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 83 0 +LFQ 58 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 0 0 9 0 0 0 80 0 +LFQ 59 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 8 0 0 0 82 0 +LFQ 60 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 6 0 0 0 77 0 +LFQ 61 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 9 0 0 0 81 0 +LFQ 62 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 7 0 0 0 80 0 +LFQ 63 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 8 0 0 0 84 0 +LFQ 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 10 0 0 0 80 0 +LFQ 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 5 0 0 0 74 0 +LFQ 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 7 0 0 0 79 0 +LFQ 67 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 10 0 0 0 79 0 +LFQ 68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 7 0 0 0 83 0 +LFQ 69 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 9 0 0 0 76 0 +LFQ 70 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 76 0 +LFQ 71 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 7 0 0 0 74 0 +LFQ 72 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 11 0 0 0 71 0 +LFQ 73 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 6 0 0 0 80 0 +LFQ 74 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 0 0 8 0 0 0 75 0 +LFQ 75 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 11 0 0 0 80 0 +LFQ 76 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 8 0 0 0 80 0 +LFQ 77 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 6 0 0 0 77 0 +LFQ 78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 13 0 0 0 69 0 +LFQ 79 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0 0 0 74 0 +LFQ 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 3 0 0 0 0 12 0 0 0 72 0 +LFQ 81 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 10 0 0 0 79 0 +LFQ 82 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 9 0 0 0 78 0 +LFQ 83 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 9 0 0 0 74 0 +LFQ 84 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 12 0 0 0 72 0 +LFQ 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 14 0 0 0 66 0 +LFQ 86 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 12 0 0 0 72 0 +LFQ 87 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 4 0 0 0 78 0 +LFQ 88 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 8 0 0 0 70 0 +LFQ 89 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 10 0 0 0 73 0 +LFQ 90 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 11 0 0 0 72 0 +LFQ 91 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 11 0 0 0 72 0 +LFQ 92 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 14 0 0 0 68 0 +LFQ 93 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 9 0 0 0 68 0 +LFQ 94 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 15 0 0 0 68 0 +LFQ 95 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 19 0 0 0 64 0 +LFQ 96 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 13 0 0 0 66 0 +LFQ 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 12 0 0 0 70 0 +LFQ 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 13 0 0 0 67 0 +LFQ 99 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 12 0 0 0 62 0 +LFQ 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 15 0 0 0 59 0 +LFQ 101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 11 0 0 0 63 0 +LFQ 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 15 0 0 0 60 0 +LFQ 103 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 14 0 0 0 64 0 +LFQ 104 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 21 0 0 0 57 0 +LFQ 105 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 19 0 0 0 55 0 +LFQ 106 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 19 0 0 0 55 0 +LFQ 107 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 17 0 0 0 60 0 +LFQ 108 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 13 0 0 0 58 0 +LFQ 109 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 19 0 0 0 55 0 +LFQ 110 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 16 0 0 0 48 0 +LFQ 111 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 14 0 0 0 55 0 +LFQ 112 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 22 0 0 0 43 0 +LFQ 113 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 18 0 0 0 47 0 +LFQ 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 13 0 0 0 50 0 +LFQ 115 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 11 0 0 0 0 19 0 0 0 44 0 +LFQ 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 18 0 0 0 49 0 +LFQ 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 25 0 0 0 39 0 +LFQ 118 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 2 0 0 0 0 0 8 0 0 0 0 32 0 0 0 35 0 +LFQ 119 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 25 0 0 0 41 0 +LFQ 120 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 21 0 0 0 46 0 +LFQ 121 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 28 0 0 0 35 0 +LFQ 122 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 21 0 0 0 40 0 +LFQ 123 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 12 0 0 0 0 19 0 0 0 42 0 +LFQ 124 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 2 0 0 0 0 0 15 0 0 0 0 23 0 0 0 35 0 +LFQ 125 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 30 0 0 0 32 0 +LFQ 126 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 4 0 0 0 0 27 0 0 0 41 0 +LFQ 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 6 0 0 0 0 26 0 0 0 41 0 +LFQ 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 24 0 0 0 38 0 +LFQ 129 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 3 0 0 0 0 0 8 0 0 0 0 20 0 0 0 41 0 +LFQ 130 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 10 0 0 0 0 31 0 0 0 30 0 +LFQ 131 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 23 0 0 0 36 0 +LFQ 132 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 3 0 0 0 0 0 9 0 0 0 0 21 0 0 0 35 0 +LFQ 133 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 26 0 0 0 36 0 +LFQ 134 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 4 0 0 0 0 0 3 0 0 0 0 28 0 0 0 35 0 +LFQ 135 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 9 0 0 0 0 23 0 0 0 35 0 +LFQ 136 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 26 0 0 0 41 0 +LFQ 137 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 4 0 0 0 0 0 7 0 0 0 0 24 0 0 0 38 0 +LFQ 138 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 20 0 0 0 36 0 +LFQ 139 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 1 0 0 0 0 0 7 0 0 0 0 25 0 0 0 38 0 +LFQ 140 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 3 0 0 0 0 0 8 0 0 0 0 19 0 0 0 36 0 +LFQ 141 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 3 0 0 0 0 0 6 0 0 0 0 22 0 0 0 38 0 +LFQ 142 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 9 0 0 0 0 20 0 0 0 35 0 +LFQ 143 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 3 0 0 0 0 0 9 0 0 0 0 17 0 0 0 35 0 +LFQ 144 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 22 0 0 0 38 0 +LFQ 145 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 1 0 0 0 0 0 5 0 0 0 0 20 0 0 0 38 0 +LFQ 146 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 7 0 0 0 0 23 0 0 0 35 0 +LFQ 147 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 8 0 0 0 0 31 0 0 0 28 0 +LFQ 148 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 1 0 0 0 0 0 9 0 0 0 0 23 0 0 0 28 0 +LFQ 149 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 19 0 0 0 29 0 +LFQ 150 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 23 0 0 0 30 0 +LFQ 151 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 0 0 0 4 0 +# GC Content of first fragments. Use `grep ^GCF | cut -f 2-` to extract this part. +GCF 15.08 0 +GCF 30.40 1 +GCF 31.16 2 +GCF 32.16 0 +GCF 33.17 2 +GCF 33.92 5 +GCF 34.42 4 +GCF 34.92 2 +GCF 35.43 3 +GCF 35.93 7 +GCF 36.43 9 +GCF 36.93 4 +GCF 37.44 7 +GCF 37.94 8 +GCF 38.44 10 +GCF 38.94 7 +GCF 39.70 6 +GCF 40.45 8 +GCF 40.95 9 +GCF 41.71 4 +GCF 42.46 5 +GCF 42.96 7 +GCF 43.72 2 +GCF 44.72 1 +GCF 45.48 3 +GCF 46.48 2 +GCF 47.74 1 +GCF 48.74 2 +GCF 50.25 0 +GCF 52.01 1 +GCF 54.77 0 +GCF 57.54 1 +# GC Content of last fragments. Use `grep ^GCL | cut -f 2-` to extract this part. +GCL 15.08 0 +GCL 30.65 1 +GCL 31.66 0 +GCL 32.41 2 +GCL 32.91 1 +GCL 33.42 3 +GCL 33.92 4 +GCL 34.42 3 +GCL 34.92 4 +GCL 35.68 5 +GCL 36.43 10 +GCL 36.93 8 +GCL 37.44 7 +GCL 37.94 9 +GCL 38.44 10 +GCL 38.94 13 +GCL 39.45 8 +GCL 39.95 7 +GCL 40.45 2 +GCL 40.95 4 +GCL 41.46 3 +GCL 41.96 1 +GCL 42.46 4 +GCL 42.96 6 +GCL 43.47 4 +GCL 44.22 2 +GCL 44.97 4 +GCL 45.48 7 +GCL 45.98 3 +GCL 46.48 2 +GCL 46.98 3 +GCL 47.49 1 +GCL 48.49 0 +GCL 49.75 2 +# ACGT content per cycle. Use `grep ^GCC | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%]; and N and O counts as a percentage of all A/C/G/T bases [%] +GCC 1 19.50 26.50 31.50 22.50 0.00 0.00 +GCC 2 30.50 20.50 17.00 32.00 0.00 0.00 +GCC 3 32.00 15.00 16.50 36.50 0.00 0.00 +GCC 4 30.50 21.00 17.50 31.00 0.00 0.00 +GCC 5 39.50 9.50 12.50 38.50 0.00 0.00 +GCC 6 28.00 17.50 18.50 36.00 0.00 0.00 +GCC 7 29.50 19.50 21.00 30.00 0.00 0.00 +GCC 8 29.50 21.00 23.00 26.50 0.00 0.00 +GCC 9 22.00 32.50 27.00 18.50 0.00 0.00 +GCC 10 36.00 12.00 16.00 36.00 0.00 0.00 +GCC 11 28.00 18.50 20.50 33.00 0.00 0.00 +GCC 12 33.50 21.00 16.00 29.50 0.00 0.00 +GCC 13 28.00 19.00 27.50 25.50 0.00 0.00 +GCC 14 24.50 21.50 19.00 35.00 0.00 0.00 +GCC 15 29.50 16.50 20.00 34.00 0.00 0.00 +GCC 16 31.00 20.00 21.50 27.50 0.00 0.00 +GCC 17 27.50 16.50 19.50 36.50 0.00 0.00 +GCC 18 30.50 24.00 19.50 26.00 0.00 0.00 +GCC 19 23.50 21.50 17.50 37.50 0.00 0.00 +GCC 20 31.50 17.00 21.50 30.00 0.00 0.00 +GCC 21 26.00 22.00 17.50 34.50 0.00 0.00 +GCC 22 30.50 19.00 23.00 27.50 0.00 0.00 +GCC 23 31.50 15.50 22.50 30.50 0.00 0.00 +GCC 24 32.00 18.00 21.00 29.00 0.00 0.00 +GCC 25 27.50 16.50 22.00 34.00 0.00 0.00 +GCC 26 27.50 18.50 23.50 30.50 0.00 0.00 +GCC 27 28.50 19.00 19.50 33.00 0.00 0.00 +GCC 28 22.50 21.00 22.50 34.00 0.00 0.00 +GCC 29 27.00 18.50 22.00 32.50 0.00 0.00 +GCC 30 30.50 20.00 21.50 28.00 0.00 0.00 +GCC 31 24.50 21.00 24.00 30.50 0.00 0.00 +GCC 32 32.50 17.50 16.50 33.50 0.00 0.00 +GCC 33 28.50 16.00 25.00 30.50 0.00 0.00 +GCC 34 29.00 21.00 23.50 26.50 0.00 0.00 +GCC 35 32.50 18.50 21.00 28.00 0.00 0.00 +GCC 36 35.00 12.50 20.00 32.50 0.00 0.00 +GCC 37 26.50 20.00 18.50 35.00 0.00 0.00 +GCC 38 27.00 21.00 19.50 32.50 0.00 0.00 +GCC 39 31.00 20.00 19.00 30.00 0.00 0.00 +GCC 40 27.50 20.00 21.50 31.00 0.00 0.00 +GCC 41 37.00 16.50 19.00 27.50 0.00 0.00 +GCC 42 26.50 19.50 18.50 35.50 0.00 0.00 +GCC 43 33.50 20.00 17.50 29.00 0.00 0.00 +GCC 44 31.50 16.00 21.00 31.50 0.00 0.00 +GCC 45 28.50 19.00 20.00 32.50 0.00 0.00 +GCC 46 24.50 23.50 17.50 34.50 0.00 0.00 +GCC 47 22.50 24.50 19.50 33.50 0.00 0.00 +GCC 48 27.50 17.50 22.50 32.50 0.00 0.00 +GCC 49 28.50 17.00 20.00 34.50 0.00 0.00 +GCC 50 32.00 16.50 20.00 31.50 0.00 0.00 +GCC 51 27.50 20.50 21.00 31.00 0.00 0.00 +GCC 52 27.50 21.50 19.50 31.50 0.00 0.00 +GCC 53 26.00 19.00 25.50 29.50 0.00 0.00 +GCC 54 30.65 23.62 16.58 29.15 0.00 0.00 +GCC 55 29.65 21.61 20.10 28.64 0.00 0.00 +GCC 56 32.16 16.58 22.11 29.15 0.00 0.00 +GCC 57 28.64 20.60 21.11 29.65 0.00 0.00 +GCC 58 29.65 14.57 24.62 31.16 0.00 0.00 +GCC 59 31.16 21.61 17.59 29.65 0.00 0.00 +GCC 60 28.64 17.59 22.11 31.66 0.00 0.00 +GCC 61 25.13 21.61 22.61 30.65 0.00 0.00 +GCC 62 27.14 26.13 21.61 25.13 0.00 0.00 +GCC 63 29.15 14.57 18.59 37.69 0.00 0.00 +GCC 64 29.15 15.08 21.61 34.17 0.00 0.00 +GCC 65 28.64 20.10 19.10 32.16 0.00 0.00 +GCC 66 31.66 19.10 16.08 33.17 0.00 0.00 +GCC 67 24.75 20.20 24.24 30.81 0.00 0.00 +GCC 68 26.77 19.70 23.23 30.30 0.00 0.00 +GCC 69 30.96 17.26 22.84 28.93 0.00 0.00 +GCC 70 33.67 16.84 21.94 27.55 0.00 0.00 +GCC 71 35.20 20.41 18.88 25.51 0.00 0.00 +GCC 72 33.67 15.82 18.88 31.63 0.00 0.00 +GCC 73 32.31 18.46 18.46 30.77 0.00 0.00 +GCC 74 27.69 18.46 24.10 29.74 0.00 0.00 +GCC 75 32.31 14.87 21.54 31.28 0.00 0.00 +GCC 76 24.62 20.00 21.03 34.36 0.00 0.00 +GCC 77 29.74 17.44 17.95 34.87 0.00 0.00 +GCC 78 24.48 20.83 17.19 37.50 0.00 0.00 +GCC 79 33.33 20.83 19.79 26.04 0.00 0.00 +GCC 80 31.05 16.32 22.11 30.53 0.00 0.00 +GCC 81 33.33 15.87 15.34 35.45 0.00 0.00 +GCC 82 31.75 19.58 19.58 29.10 0.00 0.00 +GCC 83 30.32 21.81 18.62 29.26 0.00 0.00 +GCC 84 27.66 21.81 15.96 34.57 0.00 0.00 +GCC 85 26.06 15.43 22.34 36.17 0.00 0.00 +GCC 86 25.00 18.09 21.81 35.11 0.00 0.00 +GCC 87 30.85 18.09 15.43 35.64 0.00 0.00 +GCC 88 32.45 25.00 18.09 24.47 0.00 0.00 +GCC 89 24.47 15.43 19.68 40.43 0.00 0.00 +GCC 90 27.27 21.93 20.86 29.95 0.00 0.00 +GCC 91 28.34 14.97 20.86 35.83 0.00 0.00 +GCC 92 28.34 18.18 20.32 33.16 0.00 0.00 +GCC 93 28.65 18.38 18.38 34.59 0.00 0.00 +GCC 94 29.19 17.84 20.54 32.43 0.00 0.00 +GCC 95 27.72 23.91 21.20 27.17 0.00 0.00 +GCC 96 31.32 18.68 16.48 33.52 0.00 0.00 +GCC 97 21.98 17.58 21.43 39.01 0.00 0.00 +GCC 98 27.47 15.93 18.68 37.91 0.00 0.00 +GCC 99 27.53 20.22 17.98 34.27 0.00 0.00 +GCC 100 34.83 15.17 19.66 30.34 0.00 0.00 +GCC 101 36.52 16.85 20.22 26.40 0.00 0.00 +GCC 102 29.55 22.16 23.30 25.00 0.00 0.00 +GCC 103 27.84 18.75 19.32 34.09 0.00 0.00 +GCC 104 26.14 14.77 22.16 36.93 0.00 0.00 +GCC 105 33.52 11.36 19.89 35.23 0.00 0.00 +GCC 106 28.00 20.00 19.43 32.57 0.00 0.00 +GCC 107 25.88 16.47 24.12 33.53 0.00 0.00 +GCC 108 30.77 20.71 15.98 32.54 0.00 0.00 +GCC 109 26.63 30.18 16.57 26.63 0.00 0.00 +GCC 110 27.81 9.47 23.67 39.05 0.00 0.00 +GCC 111 30.18 16.57 23.67 29.59 0.00 0.00 +GCC 112 28.40 21.30 24.85 25.44 0.00 0.00 +GCC 113 28.57 19.64 22.02 29.76 0.00 0.00 +GCC 114 31.55 23.21 17.86 27.38 0.00 0.00 +GCC 115 35.12 19.64 15.48 29.76 0.00 0.00 +GCC 116 26.79 17.86 22.62 32.74 0.00 0.00 +GCC 117 34.73 22.75 14.37 28.14 0.00 0.00 +GCC 118 27.11 23.49 15.06 34.34 0.00 0.00 +GCC 119 31.93 19.28 20.48 28.31 0.00 0.00 +GCC 120 35.15 16.97 18.18 29.70 0.00 0.00 +GCC 121 26.67 24.85 18.18 30.30 0.00 0.00 +GCC 122 33.94 17.58 19.39 29.09 0.00 0.00 +GCC 123 29.45 19.63 18.40 32.52 0.00 0.00 +GCC 124 24.54 22.09 23.31 30.06 0.00 0.00 +GCC 125 28.22 17.18 20.86 33.74 0.00 0.00 +GCC 126 40.99 17.39 16.15 25.47 0.00 0.00 +GCC 127 28.75 18.12 19.38 33.75 0.00 0.00 +GCC 128 25.16 22.01 20.13 32.70 0.00 0.00 +GCC 129 23.27 16.98 23.27 36.48 0.00 0.00 +GCC 130 33.12 12.74 24.20 29.94 0.00 0.00 +GCC 131 25.48 16.56 21.66 36.31 0.00 0.00 +GCC 132 31.21 19.11 22.29 27.39 0.00 0.00 +GCC 133 30.97 19.35 19.35 30.32 0.00 0.00 +GCC 134 32.90 14.84 23.23 29.03 0.00 0.00 +GCC 135 32.26 18.71 18.06 30.97 0.00 0.00 +GCC 136 34.19 19.35 22.58 23.87 0.00 0.00 +GCC 137 27.27 18.18 20.13 34.42 0.00 0.00 +GCC 138 30.52 18.18 17.53 33.77 0.00 0.00 +GCC 139 26.62 22.08 19.48 31.82 0.00 0.00 +GCC 140 27.81 24.50 19.87 27.81 0.00 0.00 +GCC 141 28.00 23.33 21.33 27.33 0.00 0.00 +GCC 142 29.53 15.44 28.19 26.85 0.00 0.00 +GCC 143 24.66 15.07 23.97 36.30 0.00 0.00 +GCC 144 27.40 16.44 19.86 36.30 0.00 0.00 +GCC 145 29.45 13.70 19.86 36.99 0.00 0.00 +GCC 146 35.86 12.41 18.62 33.10 0.00 0.00 +GCC 147 32.87 20.98 16.08 30.07 0.00 0.00 +GCC 148 31.11 20.74 23.70 24.44 0.00 0.00 +GCC 149 33.07 14.96 19.69 32.28 0.00 0.00 +GCC 150 36.94 14.41 14.41 34.23 0.00 0.00 +GCC 151 40.82 18.37 14.29 26.53 0.00 0.00 +# ACGT content per cycle, read oriented. Use `grep ^GCT | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%] +GCT 1 22.50 26.00 32.00 19.50 +GCT 2 20.00 21.50 16.00 42.50 +GCT 3 30.00 16.50 15.00 38.50 +GCT 4 21.50 26.50 12.00 40.00 +GCT 5 44.50 10.00 12.00 33.50 +GCT 6 42.50 13.50 22.50 21.50 +GCT 7 34.50 17.00 23.50 25.00 +GCT 8 37.50 22.50 21.50 18.50 +GCT 9 17.00 39.00 20.50 23.50 +GCT 10 33.00 14.50 13.50 39.00 +GCT 11 34.50 12.50 26.50 26.50 +GCT 12 27.50 14.50 22.50 35.50 +GCT 13 21.50 22.00 24.50 32.00 +GCT 14 28.00 27.50 13.00 31.50 +GCT 15 35.00 15.50 21.00 28.50 +GCT 16 36.50 24.00 17.50 22.00 +GCT 17 36.50 18.00 18.00 27.50 +GCT 18 29.50 23.50 20.00 27.00 +GCT 19 30.00 17.50 21.50 31.00 +GCT 20 30.00 19.00 19.50 31.50 +GCT 21 25.50 20.00 19.50 35.00 +GCT 22 29.00 23.00 19.00 29.00 +GCT 23 30.50 21.00 17.00 31.50 +GCT 24 30.50 22.00 17.00 30.50 +GCT 25 28.50 19.00 19.50 33.00 +GCT 26 27.50 19.00 23.00 30.50 +GCT 27 33.50 21.50 17.00 28.00 +GCT 28 28.50 23.50 20.00 28.00 +GCT 29 32.00 21.00 19.50 27.50 +GCT 30 30.50 20.50 21.00 28.00 +GCT 31 25.00 24.00 21.00 30.00 +GCT 32 37.00 17.50 16.50 29.00 +GCT 33 27.00 19.00 22.00 32.00 +GCT 34 29.50 22.00 22.50 26.00 +GCT 35 29.00 19.50 20.00 31.50 +GCT 36 37.50 17.50 15.00 30.00 +GCT 37 32.50 21.50 17.00 29.00 +GCT 38 30.00 20.50 20.00 29.50 +GCT 39 34.00 20.50 18.50 27.00 +GCT 40 27.00 22.00 19.50 31.50 +GCT 41 32.00 20.00 15.50 32.50 +GCT 42 37.50 17.00 21.00 24.50 +GCT 43 25.50 19.50 18.00 37.00 +GCT 44 31.50 18.50 18.50 31.50 +GCT 45 27.00 20.00 19.00 34.00 +GCT 46 29.00 20.50 20.50 30.00 +GCT 47 29.00 20.50 23.50 27.00 +GCT 48 27.00 21.50 18.50 33.00 +GCT 49 27.00 17.00 20.00 36.00 +GCT 50 29.00 21.00 15.50 34.50 +GCT 51 33.00 21.50 20.00 25.50 +GCT 52 30.50 21.00 20.00 28.50 +GCT 53 24.50 23.00 21.50 31.00 +GCT 54 30.15 20.60 19.60 29.65 +GCT 55 25.13 20.60 21.11 33.17 +GCT 56 26.13 21.11 17.59 35.18 +GCT 57 27.14 20.60 21.11 31.16 +GCT 58 30.15 17.59 21.61 30.65 +GCT 59 32.66 20.60 18.59 28.14 +GCT 60 31.66 18.09 21.61 28.64 +GCT 61 25.13 23.12 21.11 30.65 +GCT 62 24.62 23.12 24.62 27.64 +GCT 63 36.68 17.59 15.58 30.15 +GCT 64 35.18 16.58 20.10 28.14 +GCT 65 30.65 18.59 20.60 30.15 +GCT 66 34.67 15.58 19.60 30.15 +GCT 67 29.29 24.75 19.70 26.26 +GCT 68 28.28 21.21 21.72 28.79 +GCT 69 29.44 22.84 17.26 30.46 +GCT 70 36.22 19.90 18.88 25.00 +GCT 71 34.18 20.92 18.37 26.53 +GCT 72 32.14 17.86 16.84 33.16 +GCT 73 32.82 14.36 22.56 30.26 +GCT 74 30.26 21.54 21.03 27.18 +GCT 75 33.33 18.46 17.95 30.26 +GCT 76 29.23 23.08 17.95 29.74 +GCT 77 29.74 17.95 17.44 34.87 +GCT 78 31.25 20.83 17.19 30.73 +GCT 79 29.17 23.44 17.19 30.21 +GCT 80 35.79 21.05 17.37 25.79 +GCT 81 39.68 20.11 11.11 29.10 +GCT 82 28.04 16.93 22.22 32.80 +GCT 83 29.26 20.21 20.21 30.32 +GCT 84 35.11 18.09 19.68 27.13 +GCT 85 28.72 20.74 17.02 33.51 +GCT 86 29.79 21.28 18.62 30.32 +GCT 87 31.38 18.09 15.43 35.11 +GCT 88 28.72 21.81 21.28 28.19 +GCT 89 30.32 18.62 16.49 34.57 +GCT 90 29.95 13.90 28.88 27.27 +GCT 91 32.09 15.51 20.32 32.09 +GCT 92 26.20 18.18 20.32 35.29 +GCT 93 31.35 18.38 18.38 31.89 +GCT 94 29.73 15.68 22.70 31.89 +GCT 95 28.80 19.57 25.54 26.09 +GCT 96 32.42 20.33 14.84 32.42 +GCT 97 31.87 21.43 17.58 29.12 +GCT 98 30.77 14.29 20.33 34.62 +GCT 99 28.65 17.42 20.79 33.15 +GCT 100 28.65 14.04 20.79 36.52 +GCT 101 27.53 23.03 14.04 35.39 +GCT 102 26.70 17.05 28.41 27.84 +GCT 103 29.55 20.45 17.61 32.39 +GCT 104 34.66 22.16 14.77 28.41 +GCT 105 40.91 13.07 18.18 27.84 +GCT 106 24.57 20.57 18.86 36.00 +GCT 107 26.47 18.24 22.35 32.94 +GCT 108 31.95 17.16 19.53 31.36 +GCT 109 26.04 24.85 21.89 27.22 +GCT 110 32.54 17.75 15.38 34.32 +GCT 111 26.63 17.75 22.49 33.14 +GCT 112 27.81 23.08 23.08 26.04 +GCT 113 35.12 16.67 25.00 23.21 +GCT 114 30.95 21.43 19.64 27.98 +GCT 115 29.17 18.45 16.67 35.71 +GCT 116 30.36 17.86 22.62 29.17 +GCT 117 27.54 21.56 15.57 35.33 +GCT 118 33.13 22.89 15.66 28.31 +GCT 119 33.73 16.87 22.89 26.51 +GCT 120 26.67 13.94 21.21 38.18 +GCT 121 29.09 18.18 24.85 27.88 +GCT 122 27.27 21.21 15.76 35.76 +GCT 123 30.06 17.79 20.25 31.90 +GCT 124 28.22 22.09 23.31 26.38 +GCT 125 27.61 20.25 17.79 34.36 +GCT 126 31.06 16.77 16.77 35.40 +GCT 127 32.50 15.00 22.50 30.00 +GCT 128 25.79 18.87 23.27 32.08 +GCT 129 28.30 20.75 19.50 31.45 +GCT 130 33.12 18.47 18.47 29.94 +GCT 131 31.85 19.75 18.47 29.94 +GCT 132 30.57 22.93 18.47 28.03 +GCT 133 29.68 18.06 20.65 31.61 +GCT 134 30.97 23.23 14.84 30.97 +GCT 135 32.90 16.77 20.00 30.32 +GCT 136 29.03 19.35 22.58 29.03 +GCT 137 27.92 24.68 13.64 33.77 +GCT 138 35.06 16.88 18.83 29.22 +GCT 139 33.12 22.73 18.83 25.32 +GCT 140 34.44 22.52 21.85 21.19 +GCT 141 25.33 22.67 22.00 30.00 +GCT 142 31.54 21.48 22.15 24.83 +GCT 143 35.62 20.55 18.49 25.34 +GCT 144 25.34 14.38 21.92 38.36 +GCT 145 35.62 15.75 17.81 30.82 +GCT 146 33.79 14.48 16.55 35.17 +GCT 147 32.17 20.98 16.08 30.77 +GCT 148 26.67 23.70 20.74 28.89 +GCT 149 40.16 16.54 18.11 25.20 +GCT 150 33.33 9.91 18.92 37.84 +GCT 151 24.49 0.00 32.65 42.86 +# ACGT content per cycle for first fragments. Use `grep ^FBC | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%]; and N and O counts as a percentage of all A/C/G/T bases [%] +FBC 1 20.00 26.00 32.00 22.00 0.00 0.00 +FBC 2 34.00 16.00 18.00 32.00 0.00 0.00 +FBC 3 35.00 17.00 16.00 32.00 0.00 0.00 +FBC 4 27.00 22.00 22.00 29.00 0.00 0.00 +FBC 5 33.00 10.00 14.00 43.00 0.00 0.00 +FBC 6 30.00 18.00 13.00 39.00 0.00 0.00 +FBC 7 27.00 22.00 21.00 30.00 0.00 0.00 +FBC 8 35.00 20.00 20.00 25.00 0.00 0.00 +FBC 9 23.00 34.00 23.00 20.00 0.00 0.00 +FBC 10 33.00 13.00 14.00 40.00 0.00 0.00 +FBC 11 33.00 17.00 21.00 29.00 0.00 0.00 +FBC 12 35.00 21.00 11.00 33.00 0.00 0.00 +FBC 13 31.00 20.00 21.00 28.00 0.00 0.00 +FBC 14 26.00 23.00 21.00 30.00 0.00 0.00 +FBC 15 25.00 24.00 18.00 33.00 0.00 0.00 +FBC 16 32.00 24.00 23.00 21.00 0.00 0.00 +FBC 17 27.00 13.00 21.00 39.00 0.00 0.00 +FBC 18 26.00 28.00 15.00 31.00 0.00 0.00 +FBC 19 24.00 18.00 19.00 39.00 0.00 0.00 +FBC 20 29.00 16.00 22.00 33.00 0.00 0.00 +FBC 21 21.00 20.00 13.00 46.00 0.00 0.00 +FBC 22 32.00 17.00 21.00 30.00 0.00 0.00 +FBC 23 33.00 13.00 24.00 30.00 0.00 0.00 +FBC 24 34.00 16.00 17.00 33.00 0.00 0.00 +FBC 25 27.00 18.00 22.00 33.00 0.00 0.00 +FBC 26 31.00 15.00 23.00 31.00 0.00 0.00 +FBC 27 29.00 18.00 20.00 33.00 0.00 0.00 +FBC 28 23.00 21.00 20.00 36.00 0.00 0.00 +FBC 29 26.00 14.00 24.00 36.00 0.00 0.00 +FBC 30 26.00 21.00 23.00 30.00 0.00 0.00 +FBC 31 25.00 19.00 22.00 34.00 0.00 0.00 +FBC 32 30.00 21.00 15.00 34.00 0.00 0.00 +FBC 33 31.00 16.00 22.00 31.00 0.00 0.00 +FBC 34 29.00 19.00 22.00 30.00 0.00 0.00 +FBC 35 38.00 13.00 27.00 22.00 0.00 0.00 +FBC 36 33.00 13.00 20.00 34.00 0.00 0.00 +FBC 37 32.00 14.00 18.00 36.00 0.00 0.00 +FBC 38 31.00 22.00 17.00 30.00 0.00 0.00 +FBC 39 32.00 18.00 16.00 34.00 0.00 0.00 +FBC 40 28.00 23.00 20.00 29.00 0.00 0.00 +FBC 41 41.00 14.00 16.00 29.00 0.00 0.00 +FBC 42 27.00 20.00 21.00 32.00 0.00 0.00 +FBC 43 35.00 23.00 14.00 28.00 0.00 0.00 +FBC 44 33.00 14.00 18.00 35.00 0.00 0.00 +FBC 45 30.00 18.00 19.00 33.00 0.00 0.00 +FBC 46 26.00 22.00 24.00 28.00 0.00 0.00 +FBC 47 25.00 26.00 22.00 27.00 0.00 0.00 +FBC 48 27.00 15.00 24.00 34.00 0.00 0.00 +FBC 49 23.00 20.00 21.00 36.00 0.00 0.00 +FBC 50 30.00 14.00 26.00 30.00 0.00 0.00 +FBC 51 32.00 15.00 15.00 38.00 0.00 0.00 +FBC 52 31.00 20.00 19.00 30.00 0.00 0.00 +FBC 53 28.00 17.00 28.00 27.00 0.00 0.00 +FBC 54 28.00 24.00 21.00 27.00 0.00 0.00 +FBC 55 23.00 25.00 20.00 32.00 0.00 0.00 +FBC 56 31.00 19.00 22.00 28.00 0.00 0.00 +FBC 57 33.00 19.00 18.00 30.00 0.00 0.00 +FBC 58 34.00 16.00 25.00 25.00 0.00 0.00 +FBC 59 35.00 22.00 17.00 26.00 0.00 0.00 +FBC 60 24.00 22.00 24.00 30.00 0.00 0.00 +FBC 61 22.00 25.00 27.00 26.00 0.00 0.00 +FBC 62 23.00 30.00 20.00 27.00 0.00 0.00 +FBC 63 30.00 10.00 22.00 38.00 0.00 0.00 +FBC 64 25.00 17.00 20.00 38.00 0.00 0.00 +FBC 65 25.00 24.00 21.00 30.00 0.00 0.00 +FBC 66 33.00 12.00 19.00 36.00 0.00 0.00 +FBC 67 23.00 22.00 19.00 36.00 0.00 0.00 +FBC 68 23.00 21.00 25.00 31.00 0.00 0.00 +FBC 69 31.00 17.00 24.00 28.00 0.00 0.00 +FBC 70 31.00 18.00 27.00 24.00 0.00 0.00 +FBC 71 42.00 17.00 15.00 26.00 0.00 0.00 +FBC 72 34.00 15.00 23.00 28.00 0.00 0.00 +FBC 73 31.31 23.23 19.19 26.26 0.00 0.00 +FBC 74 21.21 22.22 26.26 30.30 0.00 0.00 +FBC 75 32.32 15.15 20.20 32.32 0.00 0.00 +FBC 76 29.29 13.13 17.17 40.40 0.00 0.00 +FBC 77 26.26 18.18 21.21 34.34 0.00 0.00 +FBC 78 28.87 17.53 22.68 30.93 0.00 0.00 +FBC 79 32.99 20.62 20.62 25.77 0.00 0.00 +FBC 80 29.47 16.84 26.32 27.37 0.00 0.00 +FBC 81 32.98 12.77 12.77 41.49 0.00 0.00 +FBC 82 37.23 20.21 21.28 21.28 0.00 0.00 +FBC 83 31.91 23.40 18.09 26.60 0.00 0.00 +FBC 84 24.47 23.40 14.89 37.23 0.00 0.00 +FBC 85 36.17 18.09 20.21 25.53 0.00 0.00 +FBC 86 25.53 19.15 20.21 35.11 0.00 0.00 +FBC 87 29.79 18.09 13.83 38.30 0.00 0.00 +FBC 88 32.98 28.72 15.96 22.34 0.00 0.00 +FBC 89 24.47 20.21 15.96 39.36 0.00 0.00 +FBC 90 31.18 19.35 13.98 35.48 0.00 0.00 +FBC 91 25.81 19.35 18.28 36.56 0.00 0.00 +FBC 92 30.11 18.28 18.28 33.33 0.00 0.00 +FBC 93 28.26 13.04 20.65 38.04 0.00 0.00 +FBC 94 31.52 18.48 20.65 29.35 0.00 0.00 +FBC 95 26.37 21.98 21.98 29.67 0.00 0.00 +FBC 96 24.44 17.78 23.33 34.44 0.00 0.00 +FBC 97 17.78 17.78 21.11 43.33 0.00 0.00 +FBC 98 26.67 13.33 14.44 45.56 0.00 0.00 +FBC 99 27.27 20.45 19.32 32.95 0.00 0.00 +FBC 100 36.36 13.64 22.73 27.27 0.00 0.00 +FBC 101 40.91 15.91 17.05 26.14 0.00 0.00 +FBC 102 28.41 23.86 22.73 25.00 0.00 0.00 +FBC 103 30.68 19.32 18.18 31.82 0.00 0.00 +FBC 104 18.18 18.18 25.00 38.64 0.00 0.00 +FBC 105 30.68 10.23 19.32 39.77 0.00 0.00 +FBC 106 36.36 15.91 21.59 26.14 0.00 0.00 +FBC 107 25.58 15.12 19.77 39.53 0.00 0.00 +FBC 108 32.94 18.82 12.94 35.29 0.00 0.00 +FBC 109 28.24 29.41 17.65 24.71 0.00 0.00 +FBC 110 28.24 10.59 24.71 36.47 0.00 0.00 +FBC 111 34.12 14.12 25.88 25.88 0.00 0.00 +FBC 112 23.53 21.18 28.24 27.06 0.00 0.00 +FBC 113 21.18 21.18 23.53 34.12 0.00 0.00 +FBC 114 23.53 23.53 16.47 36.47 0.00 0.00 +FBC 115 30.59 27.06 12.94 29.41 0.00 0.00 +FBC 116 24.71 15.29 29.41 30.59 0.00 0.00 +FBC 117 29.41 27.06 12.94 30.59 0.00 0.00 +FBC 118 24.71 27.06 15.29 32.94 0.00 0.00 +FBC 119 27.06 22.35 22.35 28.24 0.00 0.00 +FBC 120 36.90 20.24 14.29 28.57 0.00 0.00 +FBC 121 33.33 20.24 15.48 30.95 0.00 0.00 +FBC 122 35.71 20.24 14.29 29.76 0.00 0.00 +FBC 123 24.10 25.30 16.87 33.73 0.00 0.00 +FBC 124 27.71 24.10 19.28 28.92 0.00 0.00 +FBC 125 26.51 16.87 19.28 37.35 0.00 0.00 +FBC 126 41.46 15.85 13.41 29.27 0.00 0.00 +FBC 127 28.05 18.29 24.39 29.27 0.00 0.00 +FBC 128 20.99 20.99 22.22 35.80 0.00 0.00 +FBC 129 22.22 13.58 22.22 41.98 0.00 0.00 +FBC 130 32.50 10.00 26.25 31.25 0.00 0.00 +FBC 131 26.25 15.00 26.25 32.50 0.00 0.00 +FBC 132 30.00 18.75 21.25 30.00 0.00 0.00 +FBC 133 32.91 20.25 17.72 29.11 0.00 0.00 +FBC 134 29.11 15.19 25.32 30.38 0.00 0.00 +FBC 135 31.65 18.99 18.99 30.38 0.00 0.00 +FBC 136 34.18 18.99 25.32 21.52 0.00 0.00 +FBC 137 29.11 10.13 25.32 35.44 0.00 0.00 +FBC 138 25.32 24.05 17.72 32.91 0.00 0.00 +FBC 139 25.32 25.32 18.99 30.38 0.00 0.00 +FBC 140 29.87 24.68 19.48 25.97 0.00 0.00 +FBC 141 29.87 22.08 18.18 29.87 0.00 0.00 +FBC 142 27.63 15.79 30.26 26.32 0.00 0.00 +FBC 143 27.03 18.92 24.32 29.73 0.00 0.00 +FBC 144 28.38 18.92 18.92 33.78 0.00 0.00 +FBC 145 32.43 16.22 14.86 36.49 0.00 0.00 +FBC 146 36.49 13.51 16.22 33.78 0.00 0.00 +FBC 147 34.72 22.22 13.89 29.17 0.00 0.00 +FBC 148 26.87 20.90 26.87 25.37 0.00 0.00 +FBC 149 31.25 12.50 25.00 31.25 0.00 0.00 +FBC 150 32.73 16.36 10.91 40.00 0.00 0.00 +FBC 151 48.28 17.24 13.79 20.69 0.00 0.00 +# ACGT raw counters for first fragments. Use `grep ^FTC | cut -f 2-` to extract this part. The columns are: A,C,G,T,N base counters +FTC 4077 2634 2796 4390 0 +# ACGT content per cycle for last fragments. Use `grep ^LBC | cut -f 2-` to extract this part. The columns are: cycle; A,C,G,T base counts as a percentage of all A/C/G/T bases [%]; and N and O counts as a percentage of all A/C/G/T bases [%] +LBC 1 19.00 27.00 31.00 23.00 0.00 0.00 +LBC 2 27.00 25.00 16.00 32.00 0.00 0.00 +LBC 3 29.00 13.00 17.00 41.00 0.00 0.00 +LBC 4 34.00 20.00 13.00 33.00 0.00 0.00 +LBC 5 46.00 9.00 11.00 34.00 0.00 0.00 +LBC 6 26.00 17.00 24.00 33.00 0.00 0.00 +LBC 7 32.00 17.00 21.00 30.00 0.00 0.00 +LBC 8 24.00 22.00 26.00 28.00 0.00 0.00 +LBC 9 21.00 31.00 31.00 17.00 0.00 0.00 +LBC 10 39.00 11.00 18.00 32.00 0.00 0.00 +LBC 11 23.00 20.00 20.00 37.00 0.00 0.00 +LBC 12 32.00 21.00 21.00 26.00 0.00 0.00 +LBC 13 25.00 18.00 34.00 23.00 0.00 0.00 +LBC 14 23.00 20.00 17.00 40.00 0.00 0.00 +LBC 15 34.00 9.00 22.00 35.00 0.00 0.00 +LBC 16 30.00 16.00 20.00 34.00 0.00 0.00 +LBC 17 28.00 20.00 18.00 34.00 0.00 0.00 +LBC 18 35.00 20.00 24.00 21.00 0.00 0.00 +LBC 19 23.00 25.00 16.00 36.00 0.00 0.00 +LBC 20 34.00 18.00 21.00 27.00 0.00 0.00 +LBC 21 31.00 24.00 22.00 23.00 0.00 0.00 +LBC 22 29.00 21.00 25.00 25.00 0.00 0.00 +LBC 23 30.00 18.00 21.00 31.00 0.00 0.00 +LBC 24 30.00 20.00 25.00 25.00 0.00 0.00 +LBC 25 28.00 15.00 22.00 35.00 0.00 0.00 +LBC 26 24.00 22.00 24.00 30.00 0.00 0.00 +LBC 27 28.00 20.00 19.00 33.00 0.00 0.00 +LBC 28 22.00 21.00 25.00 32.00 0.00 0.00 +LBC 29 28.00 23.00 20.00 29.00 0.00 0.00 +LBC 30 35.00 19.00 20.00 26.00 0.00 0.00 +LBC 31 24.00 23.00 26.00 27.00 0.00 0.00 +LBC 32 35.00 14.00 18.00 33.00 0.00 0.00 +LBC 33 26.00 16.00 28.00 30.00 0.00 0.00 +LBC 34 29.00 23.00 25.00 23.00 0.00 0.00 +LBC 35 27.00 24.00 15.00 34.00 0.00 0.00 +LBC 36 37.00 12.00 20.00 31.00 0.00 0.00 +LBC 37 21.00 26.00 19.00 34.00 0.00 0.00 +LBC 38 23.00 20.00 22.00 35.00 0.00 0.00 +LBC 39 30.00 22.00 22.00 26.00 0.00 0.00 +LBC 40 27.00 17.00 23.00 33.00 0.00 0.00 +LBC 41 33.00 19.00 22.00 26.00 0.00 0.00 +LBC 42 26.00 19.00 16.00 39.00 0.00 0.00 +LBC 43 32.00 17.00 21.00 30.00 0.00 0.00 +LBC 44 30.00 18.00 24.00 28.00 0.00 0.00 +LBC 45 27.00 20.00 21.00 32.00 0.00 0.00 +LBC 46 23.00 25.00 11.00 41.00 0.00 0.00 +LBC 47 20.00 23.00 17.00 40.00 0.00 0.00 +LBC 48 28.00 20.00 21.00 31.00 0.00 0.00 +LBC 49 34.00 14.00 19.00 33.00 0.00 0.00 +LBC 50 34.00 19.00 14.00 33.00 0.00 0.00 +LBC 51 23.00 26.00 27.00 24.00 0.00 0.00 +LBC 52 24.00 23.00 20.00 33.00 0.00 0.00 +LBC 53 24.00 21.00 23.00 32.00 0.00 0.00 +LBC 54 33.33 23.23 12.12 31.31 0.00 0.00 +LBC 55 36.36 18.18 20.20 25.25 0.00 0.00 +LBC 56 33.33 14.14 22.22 30.30 0.00 0.00 +LBC 57 24.24 22.22 24.24 29.29 0.00 0.00 +LBC 58 25.25 13.13 24.24 37.37 0.00 0.00 +LBC 59 27.27 21.21 18.18 33.33 0.00 0.00 +LBC 60 33.33 13.13 20.20 33.33 0.00 0.00 +LBC 61 28.28 18.18 18.18 35.35 0.00 0.00 +LBC 62 31.31 22.22 23.23 23.23 0.00 0.00 +LBC 63 28.28 19.19 15.15 37.37 0.00 0.00 +LBC 64 33.33 13.13 23.23 30.30 0.00 0.00 +LBC 65 32.32 16.16 17.17 34.34 0.00 0.00 +LBC 66 30.30 26.26 13.13 30.30 0.00 0.00 +LBC 67 26.53 18.37 29.59 25.51 0.00 0.00 +LBC 68 30.61 18.37 21.43 29.59 0.00 0.00 +LBC 69 30.93 17.53 21.65 29.90 0.00 0.00 +LBC 70 36.46 15.62 16.67 31.25 0.00 0.00 +LBC 71 28.12 23.96 22.92 25.00 0.00 0.00 +LBC 72 33.33 16.67 14.58 35.42 0.00 0.00 +LBC 73 33.33 13.54 17.71 35.42 0.00 0.00 +LBC 74 34.38 14.58 21.88 29.17 0.00 0.00 +LBC 75 32.29 14.58 22.92 30.21 0.00 0.00 +LBC 76 19.79 27.08 25.00 28.12 0.00 0.00 +LBC 77 33.33 16.67 14.58 35.42 0.00 0.00 +LBC 78 20.00 24.21 11.58 44.21 0.00 0.00 +LBC 79 33.68 21.05 18.95 26.32 0.00 0.00 +LBC 80 32.63 15.79 17.89 33.68 0.00 0.00 +LBC 81 33.68 18.95 17.89 29.47 0.00 0.00 +LBC 82 26.32 18.95 17.89 36.84 0.00 0.00 +LBC 83 28.72 20.21 19.15 31.91 0.00 0.00 +LBC 84 30.85 20.21 17.02 31.91 0.00 0.00 +LBC 85 15.96 12.77 24.47 46.81 0.00 0.00 +LBC 86 24.47 17.02 23.40 35.11 0.00 0.00 +LBC 87 31.91 18.09 17.02 32.98 0.00 0.00 +LBC 88 31.91 21.28 20.21 26.60 0.00 0.00 +LBC 89 24.47 10.64 23.40 41.49 0.00 0.00 +LBC 90 23.40 24.47 27.66 24.47 0.00 0.00 +LBC 91 30.85 10.64 23.40 35.11 0.00 0.00 +LBC 92 26.60 18.09 22.34 32.98 0.00 0.00 +LBC 93 29.03 23.66 16.13 31.18 0.00 0.00 +LBC 94 26.88 17.20 20.43 35.48 0.00 0.00 +LBC 95 29.03 25.81 20.43 24.73 0.00 0.00 +LBC 96 38.04 19.57 9.78 32.61 0.00 0.00 +LBC 97 26.09 17.39 21.74 34.78 0.00 0.00 +LBC 98 28.26 18.48 22.83 30.43 0.00 0.00 +LBC 99 27.78 20.00 16.67 35.56 0.00 0.00 +LBC 100 33.33 16.67 16.67 33.33 0.00 0.00 +LBC 101 32.22 17.78 23.33 26.67 0.00 0.00 +LBC 102 30.68 20.45 23.86 25.00 0.00 0.00 +LBC 103 25.00 18.18 20.45 36.36 0.00 0.00 +LBC 104 34.09 11.36 19.32 35.23 0.00 0.00 +LBC 105 36.36 12.50 20.45 30.68 0.00 0.00 +LBC 106 19.54 24.14 17.24 39.08 0.00 0.00 +LBC 107 26.19 17.86 28.57 27.38 0.00 0.00 +LBC 108 28.57 22.62 19.05 29.76 0.00 0.00 +LBC 109 25.00 30.95 15.48 28.57 0.00 0.00 +LBC 110 27.38 8.33 22.62 41.67 0.00 0.00 +LBC 111 26.19 19.05 21.43 33.33 0.00 0.00 +LBC 112 33.33 21.43 21.43 23.81 0.00 0.00 +LBC 113 36.14 18.07 20.48 25.30 0.00 0.00 +LBC 114 39.76 22.89 19.28 18.07 0.00 0.00 +LBC 115 39.76 12.05 18.07 30.12 0.00 0.00 +LBC 116 28.92 20.48 15.66 34.94 0.00 0.00 +LBC 117 40.24 18.29 15.85 25.61 0.00 0.00 +LBC 118 29.63 19.75 14.81 35.80 0.00 0.00 +LBC 119 37.04 16.05 18.52 28.40 0.00 0.00 +LBC 120 33.33 13.58 22.22 30.86 0.00 0.00 +LBC 121 19.75 29.63 20.99 29.63 0.00 0.00 +LBC 122 32.10 14.81 24.69 28.40 0.00 0.00 +LBC 123 35.00 13.75 20.00 31.25 0.00 0.00 +LBC 124 21.25 20.00 27.50 31.25 0.00 0.00 +LBC 125 30.00 17.50 22.50 30.00 0.00 0.00 +LBC 126 40.51 18.99 18.99 21.52 0.00 0.00 +LBC 127 29.49 17.95 14.10 38.46 0.00 0.00 +LBC 128 29.49 23.08 17.95 29.49 0.00 0.00 +LBC 129 24.36 20.51 24.36 30.77 0.00 0.00 +LBC 130 33.77 15.58 22.08 28.57 0.00 0.00 +LBC 131 24.68 18.18 16.88 40.26 0.00 0.00 +LBC 132 32.47 19.48 23.38 24.68 0.00 0.00 +LBC 133 28.95 18.42 21.05 31.58 0.00 0.00 +LBC 134 36.84 14.47 21.05 27.63 0.00 0.00 +LBC 135 32.89 18.42 17.11 31.58 0.00 0.00 +LBC 136 34.21 19.74 19.74 26.32 0.00 0.00 +LBC 137 25.33 26.67 14.67 33.33 0.00 0.00 +LBC 138 36.00 12.00 17.33 34.67 0.00 0.00 +LBC 139 28.00 18.67 20.00 33.33 0.00 0.00 +LBC 140 25.68 24.32 20.27 29.73 0.00 0.00 +LBC 141 26.03 24.66 24.66 24.66 0.00 0.00 +LBC 142 31.51 15.07 26.03 27.40 0.00 0.00 +LBC 143 22.22 11.11 23.61 43.06 0.00 0.00 +LBC 144 26.39 13.89 20.83 38.89 0.00 0.00 +LBC 145 26.39 11.11 25.00 37.50 0.00 0.00 +LBC 146 35.21 11.27 21.13 32.39 0.00 0.00 +LBC 147 30.99 19.72 18.31 30.99 0.00 0.00 +LBC 148 35.29 20.59 20.59 23.53 0.00 0.00 +LBC 149 34.92 17.46 14.29 33.33 0.00 0.00 +LBC 150 41.07 12.50 17.86 28.57 0.00 0.00 +LBC 151 30.00 20.00 15.00 35.00 0.00 0.00 +# ACGT raw counters for last fragments. Use `grep ^LTC | cut -f 2-` to extract this part. The columns are: A,C,G,T,N base counters +LTC 4051 2592 2808 4297 0 +# Insert sizes. Use `grep ^IS | cut -f 2-` to extract this part. The columns are: insert size, pairs total, inward oriented pairs, outward oriented pairs, other pairs +IS 0 0 0 0 0 +IS 1 0 0 0 0 +IS 2 0 0 0 0 +IS 3 0 0 0 0 +IS 4 0 0 0 0 +IS 5 0 0 0 0 +IS 6 0 0 0 0 +IS 7 0 0 0 0 +IS 8 0 0 0 0 +IS 9 0 0 0 0 +IS 10 0 0 0 0 +IS 11 0 0 0 0 +IS 12 0 0 0 0 +IS 13 0 0 0 0 +IS 14 0 0 0 0 +IS 15 0 0 0 0 +IS 16 0 0 0 0 +IS 17 0 0 0 0 +IS 18 0 0 0 0 +IS 19 0 0 0 0 +IS 20 0 0 0 0 +IS 21 0 0 0 0 +IS 22 0 0 0 0 +IS 23 0 0 0 0 +IS 24 0 0 0 0 +IS 25 0 0 0 0 +IS 26 0 0 0 0 +IS 27 0 0 0 0 +IS 28 0 0 0 0 +IS 29 0 0 0 0 +IS 30 0 0 0 0 +IS 31 0 0 0 0 +IS 32 0 0 0 0 +IS 33 0 0 0 0 +IS 34 0 0 0 0 +IS 35 0 0 0 0 +IS 36 0 0 0 0 +IS 37 0 0 0 0 +IS 38 0 0 0 0 +IS 39 0 0 0 0 +IS 40 0 0 0 0 +IS 41 0 0 0 0 +IS 42 0 0 0 0 +IS 43 0 0 0 0 +IS 44 0 0 0 0 +IS 45 0 0 0 0 +IS 46 0 0 0 0 +IS 47 0 0 0 0 +IS 48 0 0 0 0 +IS 49 0 0 0 0 +IS 50 0 0 0 0 +IS 51 0 0 0 0 +IS 52 0 0 0 0 +IS 53 0 0 0 0 +IS 54 0 0 0 0 +IS 55 0 0 0 0 +IS 56 0 0 0 0 +IS 57 0 0 0 0 +IS 58 0 0 0 0 +IS 59 0 0 0 0 +IS 60 0 0 0 0 +IS 61 0 0 0 0 +IS 62 0 0 0 0 +IS 63 0 0 0 0 +IS 64 0 0 0 0 +IS 65 0 0 0 0 +IS 66 0 0 0 0 +IS 67 0 0 0 0 +IS 68 0 0 0 0 +IS 69 0 0 0 0 +IS 70 0 0 0 0 +IS 71 0 0 0 0 +IS 72 0 0 0 0 +IS 73 0 0 0 0 +IS 74 0 0 0 0 +IS 75 0 0 0 0 +IS 76 0 0 0 0 +IS 77 1 0 1 0 +IS 78 0 0 0 0 +IS 79 0 0 0 0 +IS 80 0 0 0 0 +IS 81 0 0 0 0 +IS 82 1 1 0 0 +IS 83 0 0 0 0 +IS 84 0 0 0 0 +IS 85 0 0 0 0 +IS 86 1 1 0 0 +IS 87 0 0 0 0 +IS 88 0 0 0 0 +IS 89 0 0 0 0 +IS 90 0 0 0 0 +IS 91 0 0 0 0 +IS 92 1 1 0 0 +IS 93 0 0 0 0 +IS 94 0 0 0 0 +IS 95 0 0 0 0 +IS 96 0 0 0 0 +IS 97 0 0 0 0 +IS 98 2 1 1 0 +IS 99 0 0 0 0 +IS 100 0 0 0 0 +IS 101 0 0 0 0 +IS 102 0 0 0 0 +IS 103 0 0 0 0 +IS 104 0 0 0 0 +IS 105 0 0 0 0 +IS 106 2 1 1 0 +IS 107 1 1 0 0 +IS 108 0 0 0 0 +IS 109 0 0 0 0 +IS 110 0 0 0 0 +IS 111 0 0 0 0 +IS 112 1 1 0 0 +IS 113 0 0 0 0 +IS 114 0 0 0 0 +IS 115 0 0 0 0 +IS 116 0 0 0 0 +IS 117 0 0 0 0 +IS 118 1 1 0 0 +IS 119 0 0 0 0 +IS 120 0 0 0 0 +IS 121 0 0 0 0 +IS 122 1 0 1 0 +IS 123 0 0 0 0 +IS 124 0 0 0 0 +IS 125 1 0 1 0 +IS 126 0 0 0 0 +IS 127 1 0 1 0 +IS 128 0 0 0 0 +IS 129 1 0 1 0 +IS 130 0 0 0 0 +IS 131 0 0 0 0 +IS 132 1 1 0 0 +IS 133 0 0 0 0 +IS 134 0 0 0 0 +IS 135 0 0 0 0 +IS 136 0 0 0 0 +IS 137 0 0 0 0 +IS 138 0 0 0 0 +IS 139 1 1 0 0 +IS 140 1 1 0 0 +IS 141 0 0 0 0 +IS 142 1 0 1 0 +IS 143 0 0 0 0 +IS 144 0 0 0 0 +IS 145 0 0 0 0 +IS 146 0 0 0 0 +IS 147 1 1 0 0 +IS 148 1 0 1 0 +IS 149 0 0 0 0 +IS 150 1 1 0 0 +IS 151 0 0 0 0 +IS 152 0 0 0 0 +IS 153 0 0 0 0 +IS 154 0 0 0 0 +IS 155 0 0 0 0 +IS 156 0 0 0 0 +IS 157 0 0 0 0 +IS 158 1 1 0 0 +IS 159 3 3 0 0 +IS 160 0 0 0 0 +IS 161 0 0 0 0 +IS 162 0 0 0 0 +IS 163 0 0 0 0 +IS 164 0 0 0 0 +IS 165 0 0 0 0 +IS 166 2 2 0 0 +IS 167 0 0 0 0 +IS 168 2 2 0 0 +IS 169 0 0 0 0 +IS 170 0 0 0 0 +IS 171 1 1 0 0 +IS 172 1 1 0 0 +IS 173 0 0 0 0 +IS 174 1 1 0 0 +IS 175 0 0 0 0 +IS 176 0 0 0 0 +IS 177 1 1 0 0 +IS 178 1 1 0 0 +IS 179 0 0 0 0 +IS 180 2 2 0 0 +IS 181 0 0 0 0 +IS 182 0 0 0 0 +IS 183 0 0 0 0 +IS 184 0 0 0 0 +IS 185 1 1 0 0 +IS 186 0 0 0 0 +IS 187 1 1 0 0 +IS 188 0 0 0 0 +IS 189 1 1 0 0 +IS 190 0 0 0 0 +IS 191 1 1 0 0 +IS 192 0 0 0 0 +IS 193 0 0 0 0 +IS 194 0 0 0 0 +IS 195 1 1 0 0 +IS 196 0 0 0 0 +IS 197 1 1 0 0 +IS 198 1 1 0 0 +IS 199 0 0 0 0 +IS 200 0 0 0 0 +IS 201 2 2 0 0 +IS 202 1 1 0 0 +IS 203 0 0 0 0 +IS 204 1 1 0 0 +IS 205 0 0 0 0 +IS 206 0 0 0 0 +IS 207 0 0 0 0 +IS 208 0 0 0 0 +IS 209 1 1 0 0 +IS 210 0 0 0 0 +IS 211 0 0 0 0 +IS 212 0 0 0 0 +IS 213 0 0 0 0 +IS 214 1 1 0 0 +IS 215 0 0 0 0 +IS 216 0 0 0 0 +IS 217 0 0 0 0 +IS 218 1 1 0 0 +IS 219 1 1 0 0 +IS 220 0 0 0 0 +IS 221 0 0 0 0 +IS 222 1 1 0 0 +IS 223 0 0 0 0 +IS 224 0 0 0 0 +IS 225 0 0 0 0 +IS 226 0 0 0 0 +IS 227 1 1 0 0 +IS 228 0 0 0 0 +IS 229 0 0 0 0 +IS 230 0 0 0 0 +IS 231 1 1 0 0 +IS 232 1 1 0 0 +IS 233 1 1 0 0 +IS 234 2 2 0 0 +IS 235 3 3 0 0 +IS 236 1 1 0 0 +IS 237 0 0 0 0 +IS 238 2 2 0 0 +IS 239 0 0 0 0 +IS 240 1 1 0 0 +IS 241 0 0 0 0 +IS 242 0 0 0 0 +IS 243 0 0 0 0 +IS 244 1 1 0 0 +IS 245 1 1 0 0 +IS 246 1 1 0 0 +IS 247 2 2 0 0 +IS 248 0 0 0 0 +IS 249 1 1 0 0 +IS 250 0 0 0 0 +IS 251 1 1 0 0 +IS 252 0 0 0 0 +IS 253 0 0 0 0 +IS 254 1 1 0 0 +IS 255 1 1 0 0 +IS 256 0 0 0 0 +IS 257 0 0 0 0 +IS 258 0 0 0 0 +IS 259 1 1 0 0 +IS 260 0 0 0 0 +IS 261 0 0 0 0 +IS 262 0 0 0 0 +IS 263 0 0 0 0 +IS 264 0 0 0 0 +IS 265 0 0 0 0 +IS 266 1 1 0 0 +IS 267 1 1 0 0 +IS 268 1 1 0 0 +IS 269 0 0 0 0 +IS 270 0 0 0 0 +IS 271 0 0 0 0 +IS 272 2 2 0 0 +IS 273 0 0 0 0 +IS 274 0 0 0 0 +IS 275 0 0 0 0 +IS 276 1 1 0 0 +IS 277 0 0 0 0 +IS 278 1 1 0 0 +IS 279 0 0 0 0 +IS 280 0 0 0 0 +IS 281 1 1 0 0 +IS 282 1 1 0 0 +IS 283 0 0 0 0 +IS 284 1 1 0 0 +IS 285 0 0 0 0 +IS 286 0 0 0 0 +IS 287 0 0 0 0 +IS 288 0 0 0 0 +IS 289 0 0 0 0 +IS 290 0 0 0 0 +IS 291 1 1 0 0 +IS 292 0 0 0 0 +IS 293 0 0 0 0 +IS 294 1 1 0 0 +IS 295 0 0 0 0 +IS 296 0 0 0 0 +IS 297 0 0 0 0 +IS 298 0 0 0 0 +IS 299 0 0 0 0 +IS 300 0 0 0 0 +IS 301 0 0 0 0 +IS 302 0 0 0 0 +IS 303 0 0 0 0 +IS 304 1 1 0 0 +IS 305 1 1 0 0 +IS 306 0 0 0 0 +IS 307 0 0 0 0 +IS 308 0 0 0 0 +IS 309 0 0 0 0 +IS 310 1 1 0 0 +IS 311 0 0 0 0 +IS 312 0 0 0 0 +IS 313 0 0 0 0 +IS 314 1 1 0 0 +IS 315 0 0 0 0 +IS 316 0 0 0 0 +IS 317 0 0 0 0 +IS 318 1 1 0 0 +IS 319 0 0 0 0 +IS 320 1 1 0 0 +IS 321 0 0 0 0 +IS 322 0 0 0 0 +IS 323 0 0 0 0 +IS 324 0 0 0 0 +IS 325 0 0 0 0 +IS 326 0 0 0 0 +IS 327 0 0 0 0 +IS 328 0 0 0 0 +IS 329 0 0 0 0 +IS 330 0 0 0 0 +IS 331 0 0 0 0 +IS 332 0 0 0 0 +IS 333 0 0 0 0 +IS 334 0 0 0 0 +IS 335 0 0 0 0 +IS 336 0 0 0 0 +IS 337 0 0 0 0 +IS 338 0 0 0 0 +IS 339 1 1 0 0 +IS 340 0 0 0 0 +IS 341 0 0 0 0 +IS 342 0 0 0 0 +IS 343 1 1 0 0 +IS 344 0 0 0 0 +IS 345 0 0 0 0 +IS 346 0 0 0 0 +IS 347 0 0 0 0 +IS 348 0 0 0 0 +IS 349 0 0 0 0 +IS 350 0 0 0 0 +IS 351 0 0 0 0 +IS 352 0 0 0 0 +IS 353 0 0 0 0 +IS 354 0 0 0 0 +IS 355 0 0 0 0 +IS 356 0 0 0 0 +IS 357 0 0 0 0 +IS 358 0 0 0 0 +IS 359 0 0 0 0 +IS 360 0 0 0 0 +IS 361 0 0 0 0 +IS 362 0 0 0 0 +IS 363 0 0 0 0 +IS 364 1 1 0 0 +# Read lengths. Use `grep ^RL | cut -f 2-` to extract this part. The columns are: read length, count +RL 53 1 +RL 66 1 +RL 68 1 +RL 69 1 +RL 72 1 +RL 77 3 +RL 79 2 +RL 80 1 +RL 82 1 +RL 89 1 +RL 92 2 +RL 94 1 +RL 95 2 +RL 98 4 +RL 101 2 +RL 105 1 +RL 106 5 +RL 107 1 +RL 112 1 +RL 116 1 +RL 117 1 +RL 119 1 +RL 122 2 +RL 125 2 +RL 126 1 +RL 127 1 +RL 129 2 +RL 132 2 +RL 136 1 +RL 139 3 +RL 140 1 +RL 141 1 +RL 142 3 +RL 145 1 +RL 146 2 +RL 147 8 +RL 148 8 +RL 149 16 +RL 150 62 +RL 151 49 +# Read lengths - first fragments. Use `grep ^FRL | cut -f 2-` to extract this part. The columns are: read length, count +FRL 72 1 +FRL 77 2 +FRL 79 2 +FRL 80 1 +FRL 89 1 +FRL 92 1 +FRL 94 1 +FRL 95 1 +FRL 98 2 +FRL 106 2 +FRL 107 1 +FRL 119 1 +FRL 122 1 +FRL 125 1 +FRL 127 1 +FRL 129 1 +FRL 132 1 +FRL 139 2 +FRL 141 1 +FRL 142 2 +FRL 146 2 +FRL 147 5 +FRL 148 3 +FRL 149 9 +FRL 150 26 +FRL 151 29 +# Read lengths - last fragments. Use `grep ^LRL | cut -f 2-` to extract this part. The columns are: read length, count +LRL 53 1 +LRL 66 1 +LRL 68 1 +LRL 69 1 +LRL 77 1 +LRL 82 1 +LRL 92 1 +LRL 95 1 +LRL 98 2 +LRL 101 2 +LRL 105 1 +LRL 106 3 +LRL 112 1 +LRL 116 1 +LRL 117 1 +LRL 122 1 +LRL 125 1 +LRL 126 1 +LRL 129 1 +LRL 132 1 +LRL 136 1 +LRL 139 1 +LRL 140 1 +LRL 142 1 +LRL 145 1 +LRL 147 3 +LRL 148 5 +LRL 149 7 +LRL 150 36 +LRL 151 20 +# Mapping qualities for reads !(UNMAP|SECOND|SUPPL|QCFAIL|DUP). Use `grep ^MAPQ | cut -f 2-` to extract this part. The columns are: mapq, count +MAPQ 1 1 +MAPQ 36 1 +MAPQ 37 1 +MAPQ 38 2 +MAPQ 48 14 +MAPQ 49 1 +MAPQ 50 5 +MAPQ 51 1 +MAPQ 52 1 +MAPQ 55 2 +MAPQ 57 1 +MAPQ 59 1 +MAPQ 60 166 +# Indel distribution. Use `grep ^ID | cut -f 2-` to extract this part. The columns are: length, number of insertions, number of deletions +ID 1 0 8 +ID 2 0 1 +ID 32 0 1 +# Indels per cycle. Use `grep ^IC | cut -f 2-` to extract this part. The columns are: cycle, number of insertions (fwd), .. (rev) , number of deletions (fwd), .. (rev) +IC 5 0 0 1 0 +IC 7 0 0 1 1 +IC 72 0 0 1 0 +IC 85 0 0 1 0 +IC 97 0 0 1 0 +IC 107 0 0 0 1 +IC 121 0 0 0 1 +IC 135 0 0 0 1 +IC 137 0 0 1 0 +# Coverage distribution. Use `grep ^COV | cut -f 2-` to extract this part. +COV [1-1] 1 5542 +COV [2-2] 2 3794 +COV [3-3] 3 1571 +COV [4-4] 4 944 +COV [5-5] 5 491 +COV [6-6] 6 377 +COV [7-7] 7 50 +COV [8-8] 8 39 +COV [9-9] 9 27 +COV [10-10] 10 16 +# GC-depth. Use `grep ^GCD | cut -f 2-` to extract this part. The columns are: GC%, unique sequence percentiles, 10th, 25th, 50th, 75th and 90th depth percentile +GCD 0.0 66.667 0.000 0.000 0.000 0.000 0.000 +GCD 19.2 100.000 0.318 0.318 0.318 0.318 0.318 diff --git a/src/samtools/samtools_stats/test_data/script.sh b/src/samtools/samtools_stats/test_data/script.sh new file mode 100755 index 00000000..aed1fefb --- /dev/null +++ b/src/samtools/samtools_stats/test_data/script.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# dowload test data from nf-core module +wget https://github.com/nf-core/test-datasets/raw/modules/data/genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam +wget https://github.com/nf-core/test-datasets/raw/modules/data/genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai +# samtools stats test.paired_end.sorted.bam > ref.paired_end.sorted.txt \ No newline at end of file diff --git a/src/samtools/samtools_stats/test_data/test.paired_end.sorted.bam b/src/samtools/samtools_stats/test_data/test.paired_end.sorted.bam new file mode 100644 index 00000000..85cccf14 Binary files /dev/null and b/src/samtools/samtools_stats/test_data/test.paired_end.sorted.bam differ diff --git a/src/samtools/samtools_stats/test_data/test.paired_end.sorted.bam.bai b/src/samtools/samtools_stats/test_data/test.paired_end.sorted.bam.bai new file mode 100644 index 00000000..0c6d5a96 Binary files /dev/null and b/src/samtools/samtools_stats/test_data/test.paired_end.sorted.bam.bai differ diff --git a/src/samtools/samtools_view/config.vsh.yaml b/src/samtools/samtools_view/config.vsh.yaml new file mode 100644 index 00000000..206b87ac --- /dev/null +++ b/src/samtools/samtools_view/config.vsh.yaml @@ -0,0 +1,351 @@ +name: samtools_view +namespace: samtools +description: Views and converts SAM/BAM/CRAM files. +keywords: [view, convert, bam, sam, cram] +links: + homepage: https://www.htslib.org/ + documentation: https://www.htslib.org/doc/samtools-view.html + repository: https://github.com/samtools/samtools +references: + doi: [10.1093/bioinformatics/btp352, 10.1093/gigascience/giab008] +license: MIT/Expat + +argument_groups: + - name: Inputs + arguments: + - name: --input + type: file + description: Input SAM, BAM, or CRAM file. + required: true + must_exist: true + - name: --fai_reference + alternatives: -t + type: file + description: | + A tab-delimited FILE. Each line must contain the reference name in the first column + and the length of the reference in the second column, with one line for each distinct + reference. Any additional fields beyond the second column are ignored. This file also + defines the order of the reference sequences in sorting. If you run: `samtools faidx ', + the resulting index file .fai can be used as this FILE. + - name: --reference + alternatives: -T + type: file + description: | + A FASTA format reference FILE, optionally compressed by bgzip and ideally indexed by samtools faidx. + If an index is not present one will be generated for you, if the reference file is local. + If the reference file is not local, but is accessed instead via an https://, s3:// or other URL, + the index file will need to be supplied by the server alongside the reference. It is possible to + have the reference and index files in different locations by supplying both to this option separated + by the string "##idx##", for example: + --reference ftp://x.com/ref.fa##idx##ftp://y.com/index.fa.fai + However, note that only the location of the reference will be stored in the output file header. + If this method is used to make CRAM files, the cram reader may not be able to find the index, + and may not be able to decode the file unless it can get the references it needs using a different + method. + - name: --target_file + alternatives: -L + type: file + description: | + Only output alignments overlapping the input BED FILE [null]. + - name: --region_file + type: file + description: | + Use an index and multi-region iterator to only output alignments overlapping the input BED FILE. + Equivalent to --use_index --target_file FILE. + - name: --qname_file + alternatives: -N + type: file + description: | + Output only alignments with read names listed in FILE. If FILE starts with ^ then the operation is + negated and only outputs alignment with read groups not listed in FILE. It is not permissible to mix + both the filter-in and filter-out style syntax in the same command. + must_exist: true + - name: --read_group_file + alternatives: -R + type: file + description: | + Output alignments in read groups listed in FILE [null]. If FILE starts with ^ then the operation is + negated and only outputs alignment with read names not listed in FILE. It is not permissible to mix + both the filter-in and filter-out style syntax in the same command. Note that records with no RG tag + will also be output when using this option. This behaviour may change in a future release. + must_exist: true + - name: --use_index + alternatives: -M + type: boolean_true + description: | + Use the multi-region iterator on the union of a BED file and command-line region arguments. + This avoids re-reading the same regions of files so can sometimes be much faster. Note this also + removes duplicate sequences. Without this a sequence that overlaps multiple regions specified on + the command line will be reported multiple times. The usage of a BED file is optional and its path + has to be preceded by --target_file option. + + - name: Outputs + arguments: + - name: --output + alternatives: -o + type: file + description: Output to FILE instead of [stdout]. + required: true + direction: output + example: output.bam + - name: --bam + alternatives: -b + type: boolean_true + description: Output in the BAM format. + - name: --cram + alternatives: -C + type: boolean_true + description: | + Output in the CRAM format (requires --reference). + - name: --fast + type: boolean_true + description: | + Enable fast compression. This also changes the default output format to BAM, + but this can be overridden by the explicit format options or using a filename + with a known suffix. + - name: --uncompressed + alternatives: -u + type: boolean_true + description: | + Output uncompressed data. This also changes the default output format to BAM, + but this can be overridden by the explicit format options or using a filename + with a known suffix. + This option saves time spent on compression/decompression and is thus preferred + when the output is piped to another samtools command. + - name: --with_header + type: boolean_true + description: | + Include the header in the output. + - name: --header_only + alternatives: -H + type: boolean_true + description: | + Output the header only. + - name: --no_header + type: boolean_true + description: | + When producing SAM format, output alignment records but not headers. + This is the default; the option can be used to reset the effect of + --with_header/--header_only. + - name: --count + alternatives: -c + type: boolean_true + description: | + Instead of printing the alignments, only count them and print the total number. + All filter options, such as --require_flags, --excl_flags, and --min_MQ, are taken + into account. The --unmap option is ignored in this mode. + - name: --output_unselected + alternatives: -U + type: file + description: | + Write alignments that are not selected by the various filter options to FILE. + When this option is used, all alignments (or all alignments intersecting the regions + specified) are written to either the output file or this file, but never both. + - name: --unmap + alternatives: -p + type: boolean_true + description: | + Set the UNMAP flag on alignments that are not selected by the filter options. + These alignments are then written to the normal output. This is not compatible + with --output_unselected. + - name: --read_group + alternatives: -r + type: string + description: | + Output alignments in read group STR [null]. Note that records with no RG tag will also be output + when using this option. This behaviour may change in a future release. + - name: --tag + alternatives: -d + type: string + description: | + Only output alignments with tag STR1 and associated value STR2, which can be a string or an integer + [null]. + The value can be omitted, in which case only the tag is considered. + Note that this option does not specify a tag type. For example, use --tag XX:42 to select alignments + with an XX:i:42 field, not --tag XX:i:42. + - name: --tag_file + alternatives: -D + type: file + description: | + Only output alignments with tag STR and associated values listed in FILE. + must_exist: true + - name: --min_MQ + alternatives: -q + type: integer + description: | + Skip alignments with MAPQ smaller than INT. + default: 0 + - name: --library + alternatives: -l + type: string + description: | + Only output alignments in library STR. + - name: --min_qlen + alternatives: -m + type: integer + description: | + Only output alignments with number of CIGAR bases consuming query sequence >= INT. + default: 0 + - name: --expr + alternatives: -e + type: string + description: | + Only include alignments that match the filter expression STR. The syntax for these expressions is + described in the main samtools. + - name: --require_flags + alternatives: -f + type: string + description: | + Only output alignments with all bits set in FLAG present in the FLAG field. FLAG can be specified + in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0' (i.e. /^0[0-7]+/), + as a decimal number not beginning with '0' or as a comma-separated list of flag names. + - name: --excl_flags + alternatives: -F + type: string + description: | + Do not output alignments with any bits set in FLAG present in the FLAG field. FLAG can be specified + in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0' (i.e. /^0[0-7]+/), + as a decimal number not beginning with '0' or as a comma-separated list of flag names. + - name: --excl_all_flags + alternatives: -G + type: integer + description: | + Do not output alignments with all bits set in INT present in the FLAG field. This is the opposite of + --require_flags such that --require_flags 12 --exclude_all_flags 12 is the same as no filtering at all. + FLAG can be specified in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0' + (i.e. /^0[0-7]+/), as a decimal number not beginning with '0' or as a comma-separated list of flag names. + - name: --incl_flags + alternatives: --rf + type: string + description: | + Only output alignments with any bit set in FLAG present in the FLAG field. FLAG can be specified in hex + by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0' (i.e. /^0[0-7]+/), as a decimal + number not beginning with '0' or as a comma-separated list of flag names. + - name: --remove_tag + alternatives: -x + type: string + description: | + Read tag(s) to exclude from output (repeatable) [null]. This can be a single tag or a comma separated list. + Alternatively the option itself can be repeated multiple times. + If the list starts with a `^' then it is negated and treated as a request to remove all tags except those in STR. + The list may be empty, so --remove_tag ^ will remove all tags. + Note that tags will only be removed from reads that pass filtering. + - name: --keep_tag + type: string + description: | + This keeps only tags listed in STR and is directly equivalent to --remove_tag ^STR. Specifying an empty list + will remove all tags. If both --keep_tag and --remove_tag are specified then --keep_tag has precedence. + Note that tags will only be removed from reads that pass filtering. + - name: --remove_B + alternatives: -B + type: boolean_true + description: | + Collapse the backward CIGAR operation. + - name: --add_flags + type: string + description: | + Adds flag(s) to read. FLAG can be specified in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal + by beginning with `0' (i.e. /^0[0-7]+/), as a decimal number not beginning with '0' or as a comma-separated + list of flag names. + - name: --remove_flags + type: string + description: | + Remove flag(s) from read. FLAG is specified in the same way as with the --add_flags option. + - name: --subsample + type: double + description: | + Output only a proportion of the input alignments, as specified by 0.0 <= FLOAT <= 1.0, which gives the fraction + of templates/pairs to be kept. This subsampling acts in the same way on all of the alignment records in the same + template or read pair, so it never keeps a read but not its mate. + - name: --subsample_seed + type: integer + description: | + Subsampling seed used to influence which subset of reads is kept. When subsampling data that has previously + been subsampled, be sure to use a different seed value from those used previously; otherwise more reads will + be retained than expected. + default: 0 + - name: --fetch_pairs + alternatives: -P + type: boolean_true + description: | + Retrieve pairs even when the mate is outside of the requested region. Enabling this option also turns on the + multi-region iterator (-M). A region to search must be specified, either on the command-line, or using the + --target_file option. The input file must be an indexed regular file. + This option first scans the requested region, using the RNEXT and PNEXT fields of the records that have the + PAIRED flag set and pass other filtering options to find where paired reads are located. These locations are + used to build an expanded region list, and a set of QNAMEs to allow from the new regions. It will then make + a second pass, collecting all reads from the originally-specified region list together with reads from additional + locations that match the allowed set of QNAMEs. Any other filtering options used will be applied to all reads + found during this second pass. + As this option links reads using RNEXT and PNEXT, it is important that these fields are set accurately. Use + 'samtools fixmate' to correct them if necessary. + Note that this option does not work with the --count, --output-unselected or --unmap options. + - name: --customized_index + alternatives: -X + type: boolean_true + description: | + Include customized index file as a part of arguments. See EXAMPLES section for sample of usage. + - name: --sanitize + alternatives: -z + type: string + description: | + Perform some sanity checks on the state of SAM record fields, fixing up common mistakes made by aligners. + These include soft-clipping alignments when they extend beyond the end of the reference, marking records as + unmapped when they have reference * or position 0, and ensuring unmapped alignments have no CIGAR or mapping + quality for unmapped alignments and no MD, NM, CG or SM tags. + FLAGs is a comma-separated list of keywords chosen from the following list. + + unmap: The UNMAPPED BAM flag. This is set for reads with position <= 0, reference name "*" or reads starting + beyond the end of the reference. Note CIGAR "*" is permitted for mapped data so does not trigger this. + + pos: Position and reference name fields. These may be cleared when a sequence is unmapped due to the + coordinates being beyond the end of the reference. Selecting this may change the sort order of the file, + so it is not a part of the on compound argument. + mqual: Mapping quality. This is set to zero for unmapped reads. + cigar: Modifies CIGAR fields, either by adding soft-clips for reads that overlap the end of the reference or + by clearing it for unmapped reads. + aux: For unmapped data, some auxiliary fields are meaningless and will be removed. These include NM, MD, CG and SM. + off: Perform no sanity fixing. This is the default + on: Sanitize data in a way that guarantees the same sort order. This is everything except for pos. + all: All sanitizing options, including pos. + - name: --no_PG + type: boolean_true + description: | + Do not add a @PG line to the header of the output file. + - name: --input_fmt_option + type: string + description: | + Specify a single input file format option in the form of OPTION or OPTION=VALUE. + - name: --output_fmt + alternatives: -O + type: string + description: | + Specify output format (SAM, BAM, CRAM). + - name: --output_fmt_option + type: string + description: | + Specify a single output file format option in the form of OPTION or OPTION=VALUE. + - name: --write_index + type: boolean_true + description: | + Automatically index the output files. + +resources: + - type: bash_script + path: script.sh +test_resources: + - type: bash_script + path: test.sh + - type: file + path: test_data +engines: + - type: docker + image: quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 + setup: + - type: docker + run: | + samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ + sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt +runners: +- type: executable +- type: nextflow \ No newline at end of file diff --git a/src/samtools/samtools_view/help.txt b/src/samtools/samtools_view/help.txt new file mode 100644 index 00000000..753b1bc6 --- /dev/null +++ b/src/samtools/samtools_view/help.txt @@ -0,0 +1,80 @@ +``` +samtools view +``` + +Usage: samtools view [options] || [region ...] + +Output options: + -b, --bam Output BAM + -C, --cram Output CRAM (requires -T) + -1, --fast Use fast BAM compression (and default to --bam) + -u, --uncompressed Uncompressed BAM output (and default to --bam) + -h, --with-header Include header in SAM output + -H, --header-only Print SAM header only (no alignments) + --no-header Print SAM alignment records only [default] + -c, --count Print only the count of matching records + -o, --output FILE Write output to FILE [standard output] + -U, --unoutput FILE, --output-unselected FILE + Output reads not selected by filters to FILE + -p, --unmap Set flag to UNMAP on reads not selected + then write to output file. + -P, --fetch-pairs Retrieve complete pairs even when outside of region +Input options: + -t, --fai-reference FILE FILE listing reference names and lengths + -M, --use-index Use index and multi-region iterator for regions + --region[s]-file FILE Use index to include only reads overlapping FILE + -X, --customized-index Expect extra index file argument after + +Filtering options (Only include in output reads that...): + -L, --target[s]-file FILE ...overlap (BED) regions in FILE + -N, --qname-file [^]FILE ...whose read name is listed in FILE ("^" negates) + -r, --read-group STR ...are in read group STR + -R, --read-group-file [^]FILE + ...are in a read group listed in FILE + -d, --tag STR1[:STR2] ...have a tag STR1 (with associated value STR2) + -D, --tag-file STR:FILE ...have a tag STR whose value is listed in FILE + -q, --min-MQ INT ...have mapping quality >= INT + -l, --library STR ...are in library STR + -m, --min-qlen INT ...cover >= INT query bases (as measured via CIGAR) + -e, --expr STR ...match the filter expression STR + -f, --require-flags FLAG ...have all of the FLAGs present + -F, --excl[ude]-flags FLAG ...have none of the FLAGs present + --rf, --incl-flags, --include-flags FLAG + ...have some of the FLAGs present + -G FLAG EXCLUDE reads with all of the FLAGs present + --subsample FLOAT Keep only FLOAT fraction of templates/read pairs + --subsample-seed INT Influence WHICH reads are kept in subsampling [0] + -s INT.FRAC Same as --subsample 0.FRAC --subsample-seed INT + +Processing options: + --add-flags FLAG Add FLAGs to reads + --remove-flags FLAG Remove FLAGs from reads + -x, --remove-tag STR + Comma-separated read tags to strip (repeatable) [null] + --keep-tag STR + Comma-separated read tags to preserve (repeatable) [null]. + Equivalent to "-x ^STR" + -B, --remove-B Collapse the backward CIGAR operation + -z, --sanitize FLAGS Perform sanitity checking and fixing on records. + FLAGS is comma separated (see manual). [off] + +General options: + -?, --help Print long help, including note about region specification + -S Ignored (input format is auto-detected) + --no-PG Do not add a PG line + --input-fmt-option OPT[=VAL] + Specify a single input file format option in the form + of OPTION or OPTION=VALUE + -O, --output-fmt FORMAT[,OPT[=VAL]]... + Specify output format (SAM, BAM, CRAM) + --output-fmt-option OPT[=VAL] + Specify a single output file format option in the form + of OPTION or OPTION=VALUE + -T, --reference FILE + Reference sequence FASTA FILE [null] + -@, --threads INT + Number of additional threads to use [0] + --write-index + Automatically index the output files [off] + --verbosity INT + Set level of verbosity diff --git a/src/samtools/samtools_view/script.sh b/src/samtools/samtools_view/script.sh new file mode 100644 index 00000000..c3911b48 --- /dev/null +++ b/src/samtools/samtools_view/script.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +## VIASH START +## VIASH END + +set -e + +[[ "$par_bam" == "false" ]] && unset par_bam +[[ "$par_cram" == "false" ]] && unset par_cram +[[ "$par_fast" == "false" ]] && unset par_fast +[[ "$par_uncompressed" == "false" ]] && unset par_uncompressed +[[ "$par_with_header" == "false" ]] && unset par_with_header +[[ "$par_header_only" == "false" ]] && unset par_header_only +[[ "$par_no_header" == "false" ]] && unset par_no_header +[[ "$par_count" == "false" ]] && unset par_count +[[ "$par_unmap" == "false" ]] && unset par_unmap +[[ "$par_use_index" == "false" ]] && unset par_use_index +[[ "$par_fetch_pairs" == "false" ]] && unset par_fetch_pairs +[[ "$par_customized_index" == "false" ]] && unset par_customized_index +[[ "$par_no_PG" == "false" ]] && unset par_no_PG +[[ "$par_write_index" == "false" ]] && unset par_write_index +[[ "$par_remove_B" == "false" ]] && unset par_remove_B + +samtools view \ + ${par_bam:+-b} \ + ${par_cram:+-C} \ + ${par_fast:+--fast} \ + ${par_uncompressed:+-u} \ + ${par_with_header:+--with-header} \ + ${par_header_only:+-H} \ + ${par_no_header:+--no-header} \ + ${par_count:+-c} \ + ${par_output:+-o "$par_output"} \ + ${par_output_unselected:+-U "$par_output_unselected"} \ + ${par_unmap:+-p "$par_unmap"} \ + ${par_fetch_pairs:+-P "$par_fetch_pairs"} \ + ${par_fai_reference:+-t "$par_fai_reference"} \ + ${par_use_index:+-M "$par_use_index"} \ + ${par_region_file:+--region-file "$par_region_file"} \ + ${par_customized_index:+-X} \ + ${par_target_file:+-L "$par_target_file"} \ + ${par_qname_file:+-N "$par_qname_file"} \ + ${par_read_group:+-r "$par_read_group"} \ + ${par_read_group_file:+-R "$par_read_group_file"} \ + ${par_tag:+-d "$par_tag"} \ + ${par_tag_file:+-D "$par_tag_file"} \ + ${par_min_MQ:+-q "$par_min_MQ"} \ + ${par_library:+-l "$par_library"} \ + ${par_min_qlen:+-m "$par_min_qlen"} \ + ${par_expr:+-e "$par_expr"} \ + ${par_require_flags:+-f "$par_require_flags"} \ + ${par_excl_flags:+-F "$par_excl_flags"} \ + ${par_incl_flags:+--rf "$par_incl_flags"} \ + ${par_excl_all_flags:+-G "$par_excl_all_flags"} \ + ${par_subsample:+--subsample "$par_subsample"} \ + ${par_subsample_seed:+--subsample-seed "$par_subsample_seed"} \ + ${par_add_flags:+--add-flags "$par_add_flags"} \ + ${par_remove_flags:+--remove-flags "$par_remove_flags"} \ + ${par_remove_tag:+-x "$par_remove_tag"} \ + ${par_keep_tag:+--keep-tag "$par_keep_tag"} \ + ${par_remove_B:+-B} \ + ${par_sanitize:+-z "$par_sanitize"} \ + ${par_input_fmt_option:+--input-fmt-option "$par_input_fmt_option"} \ + ${par_output_fmt:+-O "$par_output_fmt"} \ + ${par_output_fmt_option:+--output-fmt-option "$par_output_fmt_option"} \ + ${par_reference:+-T "$par_reference"} \ + ${par_write_index:+--write-index} \ + ${par_no_PG:+--no-PG} \ + "$par_input" + +exit 0 diff --git a/src/samtools/samtools_view/test.sh b/src/samtools/samtools_view/test.sh new file mode 100644 index 00000000..1de29a7c --- /dev/null +++ b/src/samtools/samtools_view/test.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +test_dir="${meta_resources_dir}/test_data" +temp_dir="${meta_resources_dir}/out" + +############################################################################################ + +echo ">>> Test 1: Import SAM to BAM when @SQ lines are present in the header" +"$meta_executable" \ + --bam \ + --output "$temp_dir/a.bam" \ + --input "$test_dir/a.sam" + +echo ">>> Checking whether output exists" +[ ! -f "$temp_dir/a.bam" ] && echo "File 'a.bam' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$temp_dir/a.bam" ] && echo "File 'a.bam' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +# compare output of "samtools view" for both files +diff <(samtools view "$temp_dir/a.bam") <(samtools view "$test_dir/a.bam") || \ + (echo "Output file a.bam does not match expected output" && exit 1) + +############################################################################################ + +echo ">>> Test 2: ${meta_functionality_name} with CRAM format output" + +"$meta_executable" \ + --cram \ + --output "$temp_dir/a.cram" \ + --input "$test_dir/a.sam" + +echo ">>> Checking whether output exists" +[ ! -f "$temp_dir/a.cram" ] && echo "File 'a.cram' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$temp_dir/a.cram" ] && echo "File 'a.cram' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +# compare output of "samtools view" for both files +diff <(samtools view "$temp_dir/a.cram") <(samtools view "$test_dir/a.cram") || \ + (echo "Output file a.cram does not match expected output" && exit 1) + +############################################################################################ + +echo ">>> Test 3: ${meta_functionality_name} with --count option" + +"$meta_executable" \ + --count \ + --output "$temp_dir/a.count" \ + --input "$test_dir/a.sam" + +echo ">>> Checking whether output exists" +[ ! -f "$temp_dir/a.count" ] && echo "File 'a.count' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$temp_dir/a.count" ] && echo "File 'a.count' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff "$temp_dir/a.count" "$test_dir/a.count" || \ + (echo "Output file a.count does not match expected output" && exit 1) + +############################################################################################ + +echo ">>> Test 4: ${meta_functionality_name} including only the forward reads from read pairs" + +"$meta_executable" \ + --output "$temp_dir/a.forward" \ + --excl_flags "0x80" \ + --input "$test_dir/a.sam" + +echo ">>> Checking whether output exists" +[ ! -f "$temp_dir/a.forward" ] && echo "File 'a.forward' does not exist!" && exit 1 + +echo ">>> Checking whether output is non-empty" +[ ! -s "$temp_dir/a.forward" ] && echo "File 'a.forward' is empty!" && exit 1 + +echo ">>> Checking whether output is correct" +diff "$temp_dir/a.forward" "$test_dir/a.forward" || \ + (echo "Output file a.forward does not match expected output" && exit 1) + +############################################################################################ + +echo ">>> All test passed successfully" +rm -rf "${temp_dir}" +exit 0 \ No newline at end of file diff --git a/src/samtools/samtools_view/test_data/a.bam b/src/samtools/samtools_view/test_data/a.bam new file mode 100644 index 00000000..95b85b72 Binary files /dev/null and b/src/samtools/samtools_view/test_data/a.bam differ diff --git a/src/samtools/samtools_view/test_data/a.count b/src/samtools/samtools_view/test_data/a.count new file mode 100644 index 00000000..1e8b3149 --- /dev/null +++ b/src/samtools/samtools_view/test_data/a.count @@ -0,0 +1 @@ +6 diff --git a/src/samtools/samtools_view/test_data/a.cram b/src/samtools/samtools_view/test_data/a.cram new file mode 100644 index 00000000..57fb3269 Binary files /dev/null and b/src/samtools/samtools_view/test_data/a.cram differ diff --git a/src/samtools/samtools_view/test_data/a.forward b/src/samtools/samtools_view/test_data/a.forward new file mode 100644 index 00000000..766d4f20 --- /dev/null +++ b/src/samtools/samtools_view/test_data/a.forward @@ -0,0 +1,3 @@ +a1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +b1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +c1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** diff --git a/src/samtools/samtools_view/test_data/a.sam b/src/samtools/samtools_view/test_data/a.sam new file mode 100644 index 00000000..aa8c77b3 --- /dev/null +++ b/src/samtools/samtools_view/test_data/a.sam @@ -0,0 +1,7 @@ +@SQ SN:xx LN:20 +a1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +b1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +c1 99 xx 1 1 10M = 11 20 AAAAAAAAAA ********** +a1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** +b1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** +c1 147 xx 11 1 10M = 1 -20 TTTTTTTTTT ********** diff --git a/src/samtools/samtools_view/test_data/script.sh b/src/samtools/samtools_view/test_data/script.sh new file mode 100755 index 00000000..90918e44 --- /dev/null +++ b/src/samtools/samtools_view/test_data/script.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# dowload test data from snakemake wrapper +if [ ! -d /tmp/view_source ]; then + git clone --depth 1 --single-branch --branch master https://github.com/snakemake/snakemake-wrappers.git /tmp/view_source +fi + +cp -r /tmp/idxstats_source/bio/samtools/view/test/*.sam src/samtools/samtools_view/test_data \ No newline at end of file diff --git a/src/star/star_align_reads/argument_groups.yaml b/src/star/star_align_reads/argument_groups.yaml new file mode 100644 index 00000000..e6a1c874 --- /dev/null +++ b/src/star/star_align_reads/argument_groups.yaml @@ -0,0 +1,1088 @@ +argument_groups: +- name: Run Parameters + arguments: + - name: --runRNGseed + type: integer + description: random number generator seed. + example: 777 +- name: Genome Parameters + arguments: + - name: --genomeDir + type: file + description: path to the directory where genome files are stored (for --runMode + alignReads) or will be generated (for --runMode generateGenome) + example: ./GenomeDir/ + required: yes + - name: --genomeLoad + type: string + description: |- + mode of shared memory usage for the genome files. Only used with --runMode alignReads. + + - LoadAndKeep ... load genome into shared and keep it in memory after run + - LoadAndRemove ... load genome into shared but remove it after run + - LoadAndExit ... load genome into shared memory and exit, keeping the genome in memory for future runs + - Remove ... do not map anything, just remove loaded genome from memory + - NoSharedMemory ... do not use shared memory, each job will have its own private copy of the genome + example: NoSharedMemory + - name: --genomeFastaFiles + type: file + description: |- + path(s) to the fasta files with the genome sequences, separated by spaces. These files should be plain text FASTA files, they *cannot* be zipped. + + Required for the genome generation (--runMode genomeGenerate). Can also be used in the mapping (--runMode alignReads) to add extra (new) sequences to the genome (e.g. spike-ins). + multiple: yes + multiple_sep: ; + - name: --genomeFileSizes + type: integer + description: genome files exact sizes in bytes. Typically, this should not be + defined by the user. + example: 0 + multiple: yes + multiple_sep: ; + - name: --genomeTransformOutput + type: string + description: |- + which output to transform back to original genome + + - SAM ... SAM/BAM alignments + - SJ ... splice junctions (SJ.out.tab) + - Quant ... quantifications (from --quantMode option) + - None ... no transformation of the output + multiple: yes + multiple_sep: ; + - name: --genomeChrSetMitochondrial + type: string + description: names of the mitochondrial chromosomes. Presently only used for STARsolo + statistics output/ + example: + - chrM + - M + - MT + multiple: yes + multiple_sep: ; +- name: Splice Junctions Database + arguments: + - name: --sjdbFileChrStartEnd + type: string + description: path to the files with genomic coordinates (chr start + end strand) for the splice junction introns. Multiple files can be supplied + and will be concatenated. + multiple: yes + multiple_sep: ; + - name: --sjdbGTFfile + type: file + description: path to the GTF file with annotations + - name: --sjdbGTFchrPrefix + type: string + description: prefix for chromosome names in a GTF file (e.g. 'chr' for using ENSMEBL + annotations with UCSC genomes) + - name: --sjdbGTFfeatureExon + type: string + description: feature type in GTF file to be used as exons for building transcripts + example: exon + - name: --sjdbGTFtagExonParentTranscript + type: string + description: GTF attribute name for parent transcript ID (default "transcript_id" + works for GTF files) + example: transcript_id + - name: --sjdbGTFtagExonParentGene + type: string + description: GTF attribute name for parent gene ID (default "gene_id" works for + GTF files) + example: gene_id + - name: --sjdbGTFtagExonParentGeneName + type: string + description: GTF attribute name for parent gene name + example: gene_name + multiple: yes + multiple_sep: ; + - name: --sjdbGTFtagExonParentGeneType + type: string + description: GTF attribute name for parent gene type + example: + - gene_type + - gene_biotype + multiple: yes + multiple_sep: ; + - name: --sjdbOverhang + type: integer + description: length of the donor/acceptor sequence on each side of the junctions, + ideally = (mate_length - 1) + example: 100 + - name: --sjdbScore + type: integer + description: extra alignment score for alignments that cross database junctions + example: 2 + - name: --sjdbInsertSave + type: string + description: |- + which files to save when sjdb junctions are inserted on the fly at the mapping step + + - Basic ... only small junction / transcript files + - All ... all files including big Genome, SA and SAindex - this will create a complete genome directory + example: Basic +- name: Variation parameters + arguments: + - name: --varVCFfile + type: string + description: path to the VCF file that contains variation data. The 10th column + should contain the genotype information, e.g. 0/1 +- name: Read Parameters + arguments: + - name: --readFilesType + type: string + description: |- + format of input read files + + - Fastx ... FASTA or FASTQ + - SAM SE ... SAM or BAM single-end reads; for BAM use --readFilesCommand samtools view + - SAM PE ... SAM or BAM paired-end reads; for BAM use --readFilesCommand samtools view + example: Fastx + - name: --readFilesSAMattrKeep + type: string + description: |- + for --readFilesType SAM SE/PE, which SAM tags to keep in the output BAM, e.g.: --readFilesSAMtagsKeep RG PL + + - All ... keep all tags + - None ... do not keep any tags + example: All + multiple: yes + multiple_sep: ; + - name: --readFilesManifest + type: file + description: |- + path to the "manifest" file with the names of read files. The manifest file should contain 3 tab-separated columns: + + paired-end reads: read1_file_name $tab$ read2_file_name $tab$ read_group_line. + single-end reads: read1_file_name $tab$ - $tab$ read_group_line. + Spaces, but not tabs are allowed in file names. + If read_group_line does not start with ID:, it can only contain one ID field, and ID: will be added to it. + If read_group_line starts with ID:, it can contain several fields separated by $tab$, and all fields will be be copied verbatim into SAM @RG header line. + - name: --readFilesPrefix + type: string + description: prefix for the read files names, i.e. it will be added in front of + the strings in --readFilesIn + - name: --readFilesCommand + type: string + description: |- + command line to execute for each of the input file. This command should generate FASTA or FASTQ text and send it to stdout + + For example: zcat - to uncompress .gz files, bzcat - to uncompress .bz2 files, etc. + multiple: yes + multiple_sep: ; + - name: --readMapNumber + type: integer + description: |- + number of reads to map from the beginning of the file + + -1: map all reads + example: -1 + - name: --readMatesLengthsIn + type: string + description: Equal/NotEqual - lengths of names,sequences,qualities for both mates + are the same / not the same. NotEqual is safe in all situations. + example: NotEqual + - name: --readNameSeparator + type: string + description: character(s) separating the part of the read names that will be trimmed + in output (read name after space is always trimmed) + example: / + multiple: yes + multiple_sep: ; + - name: --readQualityScoreBase + type: integer + description: number to be subtracted from the ASCII code to get Phred quality + score + example: 33 +- name: Read Clipping + arguments: + - name: --clipAdapterType + type: string + description: |- + adapter clipping type + + - Hamming ... adapter clipping based on Hamming distance, with the number of mismatches controlled by --clip5pAdapterMMp + - CellRanger4 ... 5p and 3p adapter clipping similar to CellRanger4. Utilizes Opal package by Martin Sosic: https://github.com/Martinsos/opal + - None ... no adapter clipping, all other clip* parameters are disregarded + example: Hamming + - name: --clip3pNbases + type: integer + description: number(s) of bases to clip from 3p of each mate. If one value is + given, it will be assumed the same for both mates. + example: 0 + multiple: yes + multiple_sep: ; + - name: --clip3pAdapterSeq + type: string + description: |- + adapter sequences to clip from 3p of each mate. If one value is given, it will be assumed the same for both mates. + + - polyA ... polyA sequence with the length equal to read length + multiple: yes + multiple_sep: ; + - name: --clip3pAdapterMMp + type: double + description: max proportion of mismatches for 3p adapter clipping for each mate. If + one value is given, it will be assumed the same for both mates. + example: 0.1 + multiple: yes + multiple_sep: ; + - name: --clip3pAfterAdapterNbases + type: integer + description: number of bases to clip from 3p of each mate after the adapter clipping. + If one value is given, it will be assumed the same for both mates. + example: 0 + multiple: yes + multiple_sep: ; + - name: --clip5pNbases + type: integer + description: number(s) of bases to clip from 5p of each mate. If one value is + given, it will be assumed the same for both mates. + example: 0 + multiple: yes + multiple_sep: ; +- name: Limits + arguments: + - name: --limitGenomeGenerateRAM + type: long + description: maximum available RAM (bytes) for genome generation + example: '31000000000' + - name: --limitIObufferSize + type: long + description: max available buffers size (bytes) for input/output, per thread + example: + - 30000000 + - 50000000 + multiple: yes + multiple_sep: ; + - name: --limitOutSAMoneReadBytes + type: long + description: 'max size of the SAM record (bytes) for one read. Recommended value: + >(2*(LengthMate1+LengthMate2+100)*outFilterMultimapNmax' + example: 100000 + - name: --limitOutSJoneRead + type: integer + description: max number of junctions for one read (including all multi-mappers) + example: 1000 + - name: --limitOutSJcollapsed + type: integer + description: max number of collapsed junctions + example: 1000000 + - name: --limitBAMsortRAM + type: long + description: maximum available RAM (bytes) for sorting BAM. If =0, it will be + set to the genome index size. 0 value can only be used with --genomeLoad NoSharedMemory + option. + example: 0 + - name: --limitSjdbInsertNsj + type: integer + description: maximum number of junctions to be inserted to the genome on the fly + at the mapping stage, including those from annotations and those detected in + the 1st step of the 2-pass run + example: 1000000 + - name: --limitNreadsSoft + type: integer + description: soft limit on the number of reads + example: -1 +- name: 'Output: general' + arguments: + - name: --outTmpKeep + type: string + description: |- + whether to keep the temporary files after STAR runs is finished + + - None ... remove all temporary files + - All ... keep all files + - name: --outStd + type: string + description: |- + which output will be directed to stdout (standard out) + + - Log ... log messages + - SAM ... alignments in SAM format (which normally are output to Aligned.out.sam file), normal standard output will go into Log.std.out + - BAM_Unsorted ... alignments in BAM format, unsorted. Requires --outSAMtype BAM Unsorted + - BAM_SortedByCoordinate ... alignments in BAM format, sorted by coordinate. Requires --outSAMtype BAM SortedByCoordinate + - BAM_Quant ... alignments to transcriptome in BAM format, unsorted. Requires --quantMode TranscriptomeSAM + example: Log + - name: --outReadsUnmapped + type: string + description: |- + output of unmapped and partially mapped (i.e. mapped only one mate of a paired end read) reads in separate file(s). + + - None ... no output + - Fastx ... output in separate fasta/fastq files, Unmapped.out.mate1/2 + - name: --outQSconversionAdd + type: integer + description: add this number to the quality score (e.g. to convert from Illumina + to Sanger, use -31) + example: 0 + - name: --outMultimapperOrder + type: string + description: |- + order of multimapping alignments in the output files + + - Old_2.4 ... quasi-random order used before 2.5.0 + - Random ... random order of alignments for each multi-mapper. Read mates (pairs) are always adjacent, all alignment for each read stay together. This option will become default in the future releases. + example: Old_2.4 +- name: 'Output: SAM and BAM' + arguments: + - name: --outSAMtype + type: string + description: |- + type of SAM/BAM output + + 1st word: + - BAM ... output BAM without sorting + - SAM ... output SAM without sorting + - None ... no SAM/BAM output + 2nd, 3rd: + - Unsorted ... standard unsorted + - SortedByCoordinate ... sorted by coordinate. This option will allocate extra memory for sorting which can be specified by --limitBAMsortRAM. + example: SAM + multiple: yes + multiple_sep: ; + - name: --outSAMmode + type: string + description: |- + mode of SAM output + + - None ... no SAM output + - Full ... full SAM output + - NoQS ... full SAM but without quality scores + example: Full + - name: --outSAMstrandField + type: string + description: |- + Cufflinks-like strand field flag + + - None ... not used + - intronMotif ... strand derived from the intron motif. This option changes the output alignments: reads with inconsistent and/or non-canonical introns are filtered out. + - name: --outSAMattributes + type: string + description: |- + a string of desired SAM attributes, in the order desired for the output SAM. Tags can be listed in any combination/order. + + ***Presets: + - None ... no attributes + - Standard ... NH HI AS nM + - All ... NH HI AS nM NM MD jM jI MC ch + ***Alignment: + - NH ... number of loci the reads maps to: =1 for unique mappers, >1 for multimappers. Standard SAM tag. + - HI ... multiple alignment index, starts with --outSAMattrIHstart (=1 by default). Standard SAM tag. + - AS ... local alignment score, +1/-1 for matches/mismateches, score* penalties for indels and gaps. For PE reads, total score for two mates. Stadnard SAM tag. + - nM ... number of mismatches. For PE reads, sum over two mates. + - NM ... edit distance to the reference (number of mismatched + inserted + deleted bases) for each mate. Standard SAM tag. + - MD ... string encoding mismatched and deleted reference bases (see standard SAM specifications). Standard SAM tag. + - jM ... intron motifs for all junctions (i.e. N in CIGAR): 0: non-canonical; 1: GT/AG, 2: CT/AC, 3: GC/AG, 4: CT/GC, 5: AT/AC, 6: GT/AT. If splice junctions database is used, and a junction is annotated, 20 is added to its motif value. + - jI ... start and end of introns for all junctions (1-based). + - XS ... alignment strand according to --outSAMstrandField. + - MC ... mate's CIGAR string. Standard SAM tag. + - ch ... marks all segment of all chimeric alingments for --chimOutType WithinBAM output. + - cN ... number of bases clipped from the read ends: 5' and 3' + ***Variation: + - vA ... variant allele + - vG ... genomic coordinate of the variant overlapped by the read. + - vW ... 1 - alignment passes WASP filtering; 2,3,4,5,6,7 - alignment does not pass WASP filtering. Requires --waspOutputMode SAMtag. + - ha ... haplotype (1/2) when mapping to the diploid genome. Requires genome generated with --genomeTransformType Diploid . + ***STARsolo: + - CR CY UR UY ... sequences and quality scores of cell barcodes and UMIs for the solo* demultiplexing. + - GX GN ... gene ID and gene name for unique-gene reads. + - gx gn ... gene IDs and gene names for unique- and multi-gene reads. + - CB UB ... error-corrected cell barcodes and UMIs for solo* demultiplexing. Requires --outSAMtype BAM SortedByCoordinate. + - sM ... assessment of CB and UMI. + - sS ... sequence of the entire barcode (CB,UMI,adapter). + - sQ ... quality of the entire barcode. + - sF ... type of feature overlap and number of features for each alignment + ***Unsupported/undocumented: + - rB ... alignment block read/genomic coordinates. + - vR ... read coordinate of the variant. + example: Standard + multiple: yes + multiple_sep: ; + - name: --outSAMattrIHstart + type: integer + description: start value for the IH attribute. 0 may be required by some downstream + software, such as Cufflinks or StringTie. + example: 1 + - name: --outSAMunmapped + type: string + description: |- + output of unmapped reads in the SAM format + + 1st word: + - None ... no output + - Within ... output unmapped reads within the main SAM file (i.e. Aligned.out.sam) + 2nd word: + - KeepPairs ... record unmapped mate for each alignment, and, in case of unsorted output, keep it adjacent to its mapped mate. Only affects multi-mapping reads. + multiple: yes + multiple_sep: ; + - name: --outSAMorder + type: string + description: |- + type of sorting for the SAM output + + Paired: one mate after the other for all paired alignments + PairedKeepInputOrder: one mate after the other for all paired alignments, the order is kept the same as in the input FASTQ files + example: Paired + - name: --outSAMprimaryFlag + type: string + description: |- + which alignments are considered primary - all others will be marked with 0x100 bit in the FLAG + + - OneBestScore ... only one alignment with the best score is primary + - AllBestScore ... all alignments with the best score are primary + example: OneBestScore + - name: --outSAMreadID + type: string + description: |- + read ID record type + + - Standard ... first word (until space) from the FASTx read ID line, removing /1,/2 from the end + - Number ... read number (index) in the FASTx file + example: Standard + - name: --outSAMmapqUnique + type: integer + description: '0 to 255: the MAPQ value for unique mappers' + example: 255 + - name: --outSAMflagOR + type: integer + description: '0 to 65535: sam FLAG will be bitwise OR''d with this value, i.e. + FLAG=FLAG | outSAMflagOR. This is applied after all flags have been set by STAR, + and after outSAMflagAND. Can be used to set specific bits that are not set otherwise.' + example: 0 + - name: --outSAMflagAND + type: integer + description: '0 to 65535: sam FLAG will be bitwise AND''d with this value, i.e. + FLAG=FLAG & outSAMflagOR. This is applied after all flags have been set by STAR, + but before outSAMflagOR. Can be used to unset specific bits that are not set + otherwise.' + example: 65535 + - name: --outSAMattrRGline + type: string + description: |- + SAM/BAM read group line. The first word contains the read group identifier and must start with "ID:", e.g. --outSAMattrRGline ID:xxx CN:yy "DS:z z z". + + xxx will be added as RG tag to each output alignment. Any spaces in the tag values have to be double quoted. + Comma separated RG lines correspons to different (comma separated) input files in --readFilesIn. Commas have to be surrounded by spaces, e.g. + --outSAMattrRGline ID:xxx , ID:zzz "DS:z z" , ID:yyy DS:yyyy + multiple: yes + multiple_sep: ; + - name: --outSAMheaderHD + type: string + description: '@HD (header) line of the SAM header' + multiple: yes + multiple_sep: ; + - name: --outSAMheaderPG + type: string + description: extra @PG (software) line of the SAM header (in addition to STAR) + multiple: yes + multiple_sep: ; + - name: --outSAMheaderCommentFile + type: string + description: path to the file with @CO (comment) lines of the SAM header + - name: --outSAMfilter + type: string + description: |- + filter the output into main SAM/BAM files + + - KeepOnlyAddedReferences ... only keep the reads for which all alignments are to the extra reference sequences added with --genomeFastaFiles at the mapping stage. + - KeepAllAddedReferences ... keep all alignments to the extra reference sequences added with --genomeFastaFiles at the mapping stage. + multiple: yes + multiple_sep: ; + - name: --outSAMmultNmax + type: integer + description: |- + max number of multiple alignments for a read that will be output to the SAM/BAM files. Note that if this value is not equal to -1, the top scoring alignment will be output first + + - -1 ... all alignments (up to --outFilterMultimapNmax) will be output + example: -1 + - name: --outSAMtlen + type: integer + description: |- + calculation method for the TLEN field in the SAM/BAM files + + - 1 ... leftmost base of the (+)strand mate to rightmost base of the (-)mate. (+)sign for the (+)strand mate + - 2 ... leftmost base of any mate to rightmost base of any mate. (+)sign for the mate with the leftmost base. This is different from 1 for overlapping mates with protruding ends + example: 1 + - name: --outBAMcompression + type: integer + description: -1 to 10 BAM compression level, -1=default compression (6?), 0=no + compression, 10=maximum compression + example: 1 + - name: --outBAMsortingThreadN + type: integer + description: '>=0: number of threads for BAM sorting. 0 will default to min(6,--runThreadN).' + example: 0 + - name: --outBAMsortingBinsN + type: integer + description: '>0: number of genome bins for coordinate-sorting' + example: 50 +- name: BAM processing + arguments: + - name: --bamRemoveDuplicatesType + type: string + description: |- + mark duplicates in the BAM file, for now only works with (i) sorted BAM fed with inputBAMfile, and (ii) for paired-end alignments only + + - - ... no duplicate removal/marking + - UniqueIdentical ... mark all multimappers, and duplicate unique mappers. The coordinates, FLAG, CIGAR must be identical + - UniqueIdenticalNotMulti ... mark duplicate unique mappers but not multimappers. + - name: --bamRemoveDuplicatesMate2basesN + type: integer + description: number of bases from the 5' of mate 2 to use in collapsing (e.g. + for RAMPAGE) + example: 0 +- name: Output Wiggle + arguments: + - name: --outWigType + type: string + description: |- + type of signal output, e.g. "bedGraph" OR "bedGraph read1_5p". Requires sorted BAM: --outSAMtype BAM SortedByCoordinate . + + 1st word: + - None ... no signal output + - bedGraph ... bedGraph format + - wiggle ... wiggle format + 2nd word: + - read1_5p ... signal from only 5' of the 1st read, useful for CAGE/RAMPAGE etc + - read2 ... signal from only 2nd read + multiple: yes + multiple_sep: ; + - name: --outWigStrand + type: string + description: |- + strandedness of wiggle/bedGraph output + + - Stranded ... separate strands, str1 and str2 + - Unstranded ... collapsed strands + example: Stranded + - name: --outWigReferencesPrefix + type: string + description: prefix matching reference names to include in the output wiggle file, + e.g. "chr", default "-" - include all references + - name: --outWigNorm + type: string + description: |- + type of normalization for the signal + + - RPM ... reads per million of mapped reads + - None ... no normalization, "raw" counts + example: RPM +- name: Output Filtering + arguments: + - name: --outFilterType + type: string + description: |- + type of filtering + + - Normal ... standard filtering using only current alignment + - BySJout ... keep only those reads that contain junctions that passed filtering into SJ.out.tab + example: Normal + - name: --outFilterMultimapScoreRange + type: integer + description: the score range below the maximum score for multimapping alignments + example: 1 + - name: --outFilterMultimapNmax + type: integer + description: |- + maximum number of loci the read is allowed to map to. Alignments (all of them) will be output only if the read maps to no more loci than this value. + + Otherwise no alignments will be output, and the read will be counted as "mapped to too many loci" in the Log.final.out . + example: 10 + - name: --outFilterMismatchNmax + type: integer + description: alignment will be output only if it has no more mismatches than this + value. + example: 10 + - name: --outFilterMismatchNoverLmax + type: double + description: alignment will be output only if its ratio of mismatches to *mapped* + length is less than or equal to this value. + example: 0.3 + - name: --outFilterMismatchNoverReadLmax + type: double + description: alignment will be output only if its ratio of mismatches to *read* + length is less than or equal to this value. + example: 1.0 + - name: --outFilterScoreMin + type: integer + description: alignment will be output only if its score is higher than or equal + to this value. + example: 0 + - name: --outFilterScoreMinOverLread + type: double + description: same as outFilterScoreMin, but normalized to read length (sum of + mates' lengths for paired-end reads) + example: 0.66 + - name: --outFilterMatchNmin + type: integer + description: alignment will be output only if the number of matched bases is higher + than or equal to this value. + example: 0 + - name: --outFilterMatchNminOverLread + type: double + description: sam as outFilterMatchNmin, but normalized to the read length (sum + of mates' lengths for paired-end reads). + example: 0.66 + - name: --outFilterIntronMotifs + type: string + description: |- + filter alignment using their motifs + + - None ... no filtering + - RemoveNoncanonical ... filter out alignments that contain non-canonical junctions + - RemoveNoncanonicalUnannotated ... filter out alignments that contain non-canonical unannotated junctions when using annotated splice junctions database. The annotated non-canonical junctions will be kept. + - name: --outFilterIntronStrands + type: string + description: |- + filter alignments + + - RemoveInconsistentStrands ... remove alignments that have junctions with inconsistent strands + - None ... no filtering + example: RemoveInconsistentStrands +- name: Output splice junctions (SJ.out.tab) + arguments: + - name: --outSJtype + type: string + description: |- + type of splice junction output + + - Standard ... standard SJ.out.tab output + - None ... no splice junction output + example: Standard +- name: 'Output Filtering: Splice Junctions' + arguments: + - name: --outSJfilterReads + type: string + description: |- + which reads to consider for collapsed splice junctions output + + - All ... all reads, unique- and multi-mappers + - Unique ... uniquely mapping reads only + example: All + - name: --outSJfilterOverhangMin + type: integer + description: |- + minimum overhang length for splice junctions on both sides for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif + + does not apply to annotated junctions + example: + - 30 + - 12 + - 12 + - 12 + multiple: yes + multiple_sep: ; + - name: --outSJfilterCountUniqueMin + type: integer + description: |- + minimum uniquely mapping read count per junction for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif + + Junctions are output if one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin conditions are satisfied + does not apply to annotated junctions + example: + - 3 + - 1 + - 1 + - 1 + multiple: yes + multiple_sep: ; + - name: --outSJfilterCountTotalMin + type: integer + description: |- + minimum total (multi-mapping+unique) read count per junction for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif + + Junctions are output if one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin conditions are satisfied + does not apply to annotated junctions + example: + - 3 + - 1 + - 1 + - 1 + multiple: yes + multiple_sep: ; + - name: --outSJfilterDistToOtherSJmin + type: integer + description: |- + minimum allowed distance to other junctions' donor/acceptor + + does not apply to annotated junctions + example: + - 10 + - 0 + - 5 + - 10 + multiple: yes + multiple_sep: ; + - name: --outSJfilterIntronMaxVsReadN + type: integer + description: |- + maximum gap allowed for junctions supported by 1,2,3,,,N reads + + i.e. by default junctions supported by 1 read can have gaps <=50000b, by 2 reads: <=100000b, by 3 reads: <=200000. by >=4 reads any gap <=alignIntronMax + does not apply to annotated junctions + example: + - 50000 + - 100000 + - 200000 + multiple: yes + multiple_sep: ; +- name: Scoring + arguments: + - name: --scoreGap + type: integer + description: splice junction penalty (independent on intron motif) + example: 0 + - name: --scoreGapNoncan + type: integer + description: non-canonical junction penalty (in addition to scoreGap) + example: -8 + - name: --scoreGapGCAG + type: integer + description: GC/AG and CT/GC junction penalty (in addition to scoreGap) + example: -4 + - name: --scoreGapATAC + type: integer + description: AT/AC and GT/AT junction penalty (in addition to scoreGap) + example: -8 + - name: --scoreGenomicLengthLog2scale + type: integer + description: 'extra score logarithmically scaled with genomic length of the alignment: + scoreGenomicLengthLog2scale*log2(genomicLength)' + example: 0 + - name: --scoreDelOpen + type: integer + description: deletion open penalty + example: -2 + - name: --scoreDelBase + type: integer + description: deletion extension penalty per base (in addition to scoreDelOpen) + example: -2 + - name: --scoreInsOpen + type: integer + description: insertion open penalty + example: -2 + - name: --scoreInsBase + type: integer + description: insertion extension penalty per base (in addition to scoreInsOpen) + example: -2 + - name: --scoreStitchSJshift + type: integer + description: maximum score reduction while searching for SJ boundaries in the + stitching step + example: 1 +- name: Alignments and Seeding + arguments: + - name: --seedSearchStartLmax + type: integer + description: defines the search start point through the read - the read is split + into pieces no longer than this value + example: 50 + - name: --seedSearchStartLmaxOverLread + type: double + description: seedSearchStartLmax normalized to read length (sum of mates' lengths + for paired-end reads) + example: 1.0 + - name: --seedSearchLmax + type: integer + description: defines the maximum length of the seeds, if =0 seed length is not + limited + example: 0 + - name: --seedMultimapNmax + type: integer + description: only pieces that map fewer than this value are utilized in the stitching + procedure + example: 10000 + - name: --seedPerReadNmax + type: integer + description: max number of seeds per read + example: 1000 + - name: --seedPerWindowNmax + type: integer + description: max number of seeds per window + example: 50 + - name: --seedNoneLociPerWindow + type: integer + description: max number of one seed loci per window + example: 10 + - name: --seedSplitMin + type: integer + description: min length of the seed sequences split by Ns or mate gap + example: 12 + - name: --seedMapMin + type: integer + description: min length of seeds to be mapped + example: 5 + - name: --alignIntronMin + type: integer + description: minimum intron size, genomic gap is considered intron if its length>=alignIntronMin, + otherwise it is considered Deletion + example: 21 + - name: --alignIntronMax + type: integer + description: maximum intron size, if 0, max intron size will be determined by + (2^winBinNbits)*winAnchorDistNbins + example: 0 + - name: --alignMatesGapMax + type: integer + description: maximum gap between two mates, if 0, max intron gap will be determined + by (2^winBinNbits)*winAnchorDistNbins + example: 0 + - name: --alignSJoverhangMin + type: integer + description: minimum overhang (i.e. block size) for spliced alignments + example: 5 + - name: --alignSJstitchMismatchNmax + type: integer + description: |- + maximum number of mismatches for stitching of the splice junctions (-1: no limit). + + (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. + example: + - 0 + - -1 + - 0 + - 0 + multiple: yes + multiple_sep: ; + - name: --alignSJDBoverhangMin + type: integer + description: minimum overhang (i.e. block size) for annotated (sjdb) spliced alignments + example: 3 + - name: --alignSplicedMateMapLmin + type: integer + description: minimum mapped length for a read mate that is spliced + example: 0 + - name: --alignSplicedMateMapLminOverLmate + type: double + description: alignSplicedMateMapLmin normalized to mate length + example: 0.66 + - name: --alignWindowsPerReadNmax + type: integer + description: max number of windows per read + example: 10000 + - name: --alignTranscriptsPerWindowNmax + type: integer + description: max number of transcripts per window + example: 100 + - name: --alignTranscriptsPerReadNmax + type: integer + description: max number of different alignments per read to consider + example: 10000 + - name: --alignEndsType + type: string + description: |- + type of read ends alignment + + - Local ... standard local alignment with soft-clipping allowed + - EndToEnd ... force end-to-end read alignment, do not soft-clip + - Extend5pOfRead1 ... fully extend only the 5p of the read1, all other ends: local alignment + - Extend5pOfReads12 ... fully extend only the 5p of the both read1 and read2, all other ends: local alignment + example: Local + - name: --alignEndsProtrude + type: string + description: |- + allow protrusion of alignment ends, i.e. start (end) of the +strand mate downstream of the start (end) of the -strand mate + + 1st word: int: maximum number of protrusion bases allowed + 2nd word: string: + - ConcordantPair ... report alignments with non-zero protrusion as concordant pairs + - DiscordantPair ... report alignments with non-zero protrusion as discordant pairs + example: 0 ConcordantPair + - name: --alignSoftClipAtReferenceEnds + type: string + description: |- + allow the soft-clipping of the alignments past the end of the chromosomes + + - Yes ... allow + - No ... prohibit, useful for compatibility with Cufflinks + example: 'Yes' + - name: --alignInsertionFlush + type: string + description: |- + how to flush ambiguous insertion positions + + - None ... insertions are not flushed + - Right ... insertions are flushed to the right +- name: Paired-End reads + arguments: + - name: --peOverlapNbasesMin + type: integer + description: minimum number of overlapping bases to trigger mates merging and + realignment. Specify >0 value to switch on the "merginf of overlapping mates" + algorithm. + example: 0 + - name: --peOverlapMMp + type: double + description: maximum proportion of mismatched bases in the overlap area + example: 0.01 +- name: Windows, Anchors, Binning + arguments: + - name: --winAnchorMultimapNmax + type: integer + description: max number of loci anchors are allowed to map to + example: 50 + - name: --winBinNbits + type: integer + description: =log2(winBin), where winBin is the size of the bin for the windows/clustering, + each window will occupy an integer number of bins. + example: 16 + - name: --winAnchorDistNbins + type: integer + description: max number of bins between two anchors that allows aggregation of + anchors into one window + example: 9 + - name: --winFlankNbins + type: integer + description: log2(winFlank), where win Flank is the size of the left and right + flanking regions for each window + example: 4 + - name: --winReadCoverageRelativeMin + type: double + description: minimum relative coverage of the read sequence by the seeds in a + window, for STARlong algorithm only. + example: 0.5 + - name: --winReadCoverageBasesMin + type: integer + description: minimum number of bases covered by the seeds in a window , for STARlong + algorithm only. + example: 0 +- name: Chimeric Alignments + arguments: + - name: --chimOutType + type: string + description: |- + type of chimeric output + + - Junctions ... Chimeric.out.junction + - SeparateSAMold ... output old SAM into separate Chimeric.out.sam file + - WithinBAM ... output into main aligned BAM files (Aligned.*.bam) + - WithinBAM HardClip ... (default) hard-clipping in the CIGAR for supplemental chimeric alignments (default if no 2nd word is present) + - WithinBAM SoftClip ... soft-clipping in the CIGAR for supplemental chimeric alignments + example: Junctions + multiple: yes + multiple_sep: ; + - name: --chimSegmentMin + type: integer + description: minimum length of chimeric segment length, if ==0, no chimeric output + example: 0 + - name: --chimScoreMin + type: integer + description: minimum total (summed) score of the chimeric segments + example: 0 + - name: --chimScoreDropMax + type: integer + description: max drop (difference) of chimeric score (the sum of scores of all + chimeric segments) from the read length + example: 20 + - name: --chimScoreSeparation + type: integer + description: minimum difference (separation) between the best chimeric score and + the next one + example: 10 + - name: --chimScoreJunctionNonGTAG + type: integer + description: penalty for a non-GT/AG chimeric junction + example: -1 + - name: --chimJunctionOverhangMin + type: integer + description: minimum overhang for a chimeric junction + example: 20 + - name: --chimSegmentReadGapMax + type: integer + description: maximum gap in the read sequence between chimeric segments + example: 0 + - name: --chimFilter + type: string + description: |- + different filters for chimeric alignments + + - None ... no filtering + - banGenomicN ... Ns are not allowed in the genome sequence around the chimeric junction + example: banGenomicN + multiple: yes + multiple_sep: ; + - name: --chimMainSegmentMultNmax + type: integer + description: maximum number of multi-alignments for the main chimeric segment. + =1 will prohibit multimapping main segments. + example: 10 + - name: --chimMultimapNmax + type: integer + description: |- + maximum number of chimeric multi-alignments + + - 0 ... use the old scheme for chimeric detection which only considered unique alignments + example: 0 + - name: --chimMultimapScoreRange + type: integer + description: the score range for multi-mapping chimeras below the best chimeric + score. Only works with --chimMultimapNmax > 1 + example: 1 + - name: --chimNonchimScoreDropMin + type: integer + description: to trigger chimeric detection, the drop in the best non-chimeric + alignment score with respect to the read length has to be greater than this + value + example: 20 + - name: --chimOutJunctionFormat + type: integer + description: |- + formatting type for the Chimeric.out.junction file + + - 0 ... no comment lines/headers + - 1 ... comment lines at the end of the file: command line and Nreads: total, unique/multi-mapping + example: 0 +- name: Quantification of Annotations + arguments: + - name: --quantMode + type: string + description: |- + types of quantification requested + + - - ... none + - TranscriptomeSAM ... output SAM/BAM alignments to transcriptome into a separate file + - GeneCounts ... count reads per gene + multiple: yes + multiple_sep: ; + - name: --quantTranscriptomeBAMcompression + type: integer + description: |- + -2 to 10 transcriptome BAM compression level + + - -2 ... no BAM output + - -1 ... default compression (6?) + - 0 ... no compression + - 10 ... maximum compression + example: 1 + - name: --quantTranscriptomeSAMoutput + type: string + description: |- + alignment filtering for TranscriptomeSAM output + + - BanSingleEnd_BanIndels_ExtendSoftclip ... prohibit indels and single-end alignments, extend softclips - compatible with RSEM + - BanSingleEnd ... prohibit single-end alignments, allow indels and softclips + - BanSingleEnd_ExtendSoftclip ... prohibit single-end alignments, extend softclips, allow indels + example: BanSingleEnd_BanIndels_ExtendSoftclip +- name: 2-pass Mapping + arguments: + - name: --twopassMode + type: string + description: |- + 2-pass mapping mode. + + - None ... 1-pass mapping + - Basic ... basic 2-pass mapping, with all 1st pass junctions inserted into the genome indices on the fly + - name: --twopass1readsN + type: integer + description: number of reads to process for the 1st step. Use very large number + (or default -1) to map all reads in the first step. + example: -1 +- name: WASP parameters + arguments: + - name: --waspOutputMode + type: string + description: |- + WASP allele-specific output type. This is re-implementation of the original WASP mappability filtering by Bryce van de Geijn, Graham McVicker, Yoav Gilad & Jonathan K Pritchard. Please cite the original WASP paper: Nature Methods 12, 1061-1063 (2015), https://www.nature.com/articles/nmeth.3582 . + + - SAMtag ... add WASP tags to the alignments that pass WASP filtering diff --git a/src/star/star_align_reads/config.vsh.yaml b/src/star/star_align_reads/config.vsh.yaml new file mode 100644 index 00000000..8fdd5256 --- /dev/null +++ b/src/star/star_align_reads/config.vsh.yaml @@ -0,0 +1,115 @@ +name: star_align_reads +namespace: star +description: | + Aligns reads to a reference genome using STAR. +keywords: [align, fasta, genome] +links: + repository: https://github.com/alexdobin/STAR + documentation: https://github.com/alexdobin/STAR/blob/master/doc/STARmanual.pdf +references: + doi: 10.1093/bioinformatics/bts635 +license: MIT +requirements: + commands: [ STAR, python, ps, zcat, bzcat ] +# manually taking care of the main input and output arguments +argument_groups: + - name: Inputs + arguments: + - type: file + name: --input + alternatives: --readFilesIn + required: true + description: The single-end or paired-end R1 FastQ files to be processed. + example: [ mysample_S1_L001_R1_001.fastq.gz ] + multiple: true + - type: file + name: --input_r2 + required: false + description: The paired-end R2 FastQ files to be processed. Only required if --input is a paired-end R1 file. + example: [ mysample_S1_L001_R2_001.fastq.gz ] + multiple: true + - name: Outputs + arguments: + - type: file + name: --aligned_reads + required: true + description: The output file containing the aligned reads. + direction: output + example: aligned_reads.bam + - type: file + name: --reads_per_gene + required: false + description: The output file containing the number of reads per gene. + direction: output + example: reads_per_gene.tsv + - type: file + name: --unmapped + required: false + description: The output file containing the unmapped reads. + direction: output + example: unmapped.fastq + - type: file + name: --unmapped_r2 + required: false + description: The output file containing the unmapped R2 reads. + direction: output + example: unmapped_r2.fastq + - type: file + name: --chimeric_junctions + required: false + description: The output file containing the chimeric junctions. + direction: output + example: chimeric_junctions.tsv + - type: file + name: --log + required: false + description: The output file containing the log of the alignment process. + direction: output + example: log.txt + - type: file + name: --splice_junctions + required: false + description: The output file containing the splice junctions. + direction: output + example: splice_junctions.tsv +# other arguments are defined in a separate file +__merge__: argument_groups.yaml +resources: + - type: python_script + path: script.py +test_resources: + - type: bash_script + path: test.sh +engines: + - type: docker + image: python:3.12-slim + setup: + - type: apt + packages: + - procps + - gzip + - bzip2 + # setup derived from https://github.com/alexdobin/STAR/blob/master/extras/docker/Dockerfile + - type: docker + env: + - STAR_VERSION 2.7.11b + - PACKAGES gcc g++ make wget zlib1g-dev unzip xxd + run: | + apt-get update && \ + apt-get install -y --no-install-recommends ${PACKAGES} && \ + cd /tmp && \ + wget --no-check-certificate https://github.com/alexdobin/STAR/archive/refs/tags/${STAR_VERSION}.zip && \ + unzip ${STAR_VERSION}.zip && \ + cd STAR-${STAR_VERSION}/source && \ + make STARstatic CXXFLAGS_SIMD=-std=c++11 && \ + cp STAR /usr/local/bin && \ + cd / && \ + rm -rf /tmp/STAR-${STAR_VERSION} /tmp/${STAR_VERSION}.zip && \ + apt-get --purge autoremove -y ${PACKAGES} && \ + apt-get clean + - type: docker + run: | + STAR --version | sed 's#\(.*\)#star: "\1"#' > /var/software_versions.txt +runners: + - type: executable + - type: nextflow diff --git a/src/star/star_align_reads/help.txt b/src/star/star_align_reads/help.txt new file mode 100644 index 00000000..940f639d --- /dev/null +++ b/src/star/star_align_reads/help.txt @@ -0,0 +1,927 @@ +Usage: STAR [options]... --genomeDir /path/to/genome/index/ --readFilesIn R1.fq R2.fq +Spliced Transcripts Alignment to a Reference (c) Alexander Dobin, 2009-2022 + +STAR version=2.7.11b +STAR compilation time,server,dir=2024-02-11T19:36:26+00:00 :/tmp/STAR-2.7.11b/source +For more details see: + + +### versions +versionGenome 2.7.4a + string: earliest genome index version compatible with this STAR release. Please do not change this value! + +### Parameter Files +parametersFiles - + string: name of a user-defined parameters file, "-": none. Can only be defined on the command line. + +### System +sysShell - + string: path to the shell binary, preferably bash, e.g. /bin/bash. + - ... the default shell is executed, typically /bin/sh. This was reported to fail on some Ubuntu systems - then you need to specify path to bash. + +### Run Parameters +runMode alignReads + string: type of the run. + alignReads ... map reads + genomeGenerate ... generate genome files + inputAlignmentsFromBAM ... input alignments from BAM. Presently only works with --outWigType and --bamRemoveDuplicates options. + liftOver ... lift-over of GTF files (--sjdbGTFfile) between genome assemblies using chain file(s) from --genomeChainFiles. + soloCellFiltering ... STARsolo cell filtering ("calling") without remapping, followed by the path to raw count directory and output (filtered) prefix + +runThreadN 1 + int: number of threads to run STAR + +runDirPerm User_RWX + string: permissions for the directories created at the run-time. + User_RWX ... user-read/write/execute + All_RWX ... all-read/write/execute (same as chmod 777) + +runRNGseed 777 + int: random number generator seed. + + +### Genome Parameters +genomeDir ./GenomeDir/ + string: path to the directory where genome files are stored (for --runMode alignReads) or will be generated (for --runMode generateGenome) + +genomeLoad NoSharedMemory + string: mode of shared memory usage for the genome files. Only used with --runMode alignReads. + LoadAndKeep ... load genome into shared and keep it in memory after run + LoadAndRemove ... load genome into shared but remove it after run + LoadAndExit ... load genome into shared memory and exit, keeping the genome in memory for future runs + Remove ... do not map anything, just remove loaded genome from memory + NoSharedMemory ... do not use shared memory, each job will have its own private copy of the genome + +genomeFastaFiles - + string(s): path(s) to the fasta files with the genome sequences, separated by spaces. These files should be plain text FASTA files, they *cannot* be zipped. + Required for the genome generation (--runMode genomeGenerate). Can also be used in the mapping (--runMode alignReads) to add extra (new) sequences to the genome (e.g. spike-ins). + +genomeChainFiles - + string: chain files for genomic liftover. Only used with --runMode liftOver . + +genomeFileSizes 0 + uint(s)>0: genome files exact sizes in bytes. Typically, this should not be defined by the user. + +genomeTransformOutput None + string(s): which output to transform back to original genome + SAM ... SAM/BAM alignments + SJ ... splice junctions (SJ.out.tab) + Quant ... quantifications (from --quantMode option) + None ... no transformation of the output + +genomeChrSetMitochondrial chrM M MT + string(s): names of the mitochondrial chromosomes. Presently only used for STARsolo statistics output/ + +### Genome Indexing Parameters - only used with --runMode genomeGenerate +genomeChrBinNbits 18 + int: =log2(chrBin), where chrBin is the size of the bins for genome storage: each chromosome will occupy an integer number of bins. For a genome with large number of contigs, it is recommended to scale this parameter as min(18, log2[max(GenomeLength/NumberOfReferences,ReadLength)]). + +genomeSAindexNbases 14 + int: length (bases) of the SA pre-indexing string. Typically between 10 and 15. Longer strings will use much more memory, but allow faster searches. For small genomes, the parameter --genomeSAindexNbases must be scaled down to min(14, log2(GenomeLength)/2 - 1). + +genomeSAsparseD 1 + int>0: suffux array sparsity, i.e. distance between indices: use bigger numbers to decrease needed RAM at the cost of mapping speed reduction + +genomeSuffixLengthMax -1 + int: maximum length of the suffixes, has to be longer than read length. -1 = infinite. + +genomeTransformType None + string: type of genome transformation + None ... no transformation + Haploid ... replace reference alleles with alternative alleles from VCF file (e.g. consensus allele) + Diploid ... create two haplotypes for each chromosome listed in VCF file, for genotypes 1|2, assumes perfect phasing (e.g. personal genome) + +genomeTransformVCF - + string: path to VCF file for genome transformation + + + +#####UnderDevelopment_begin : not supported - do not use +genomeType Full + string: type of genome to generate + Full ... full (normal) genome + Transcriptome ... genome consists of transcript sequences + SuperTransriptome ... genome consists of superTranscript sequences +#####UnderDevelopment_end + +# DEPRECATED: please use --genomeTransformVCF and --genomeTransformType options instead. +#genomeConsensusFile - +# string: VCF file with consensus SNPs (i.e. alternative allele is the major (AF>0.5) allele) +# DEPRECATED + + + +### Splice Junctions Database +sjdbFileChrStartEnd - + string(s): path to the files with genomic coordinates (chr start end strand) for the splice junction introns. Multiple files can be supplied and will be concatenated. + +sjdbGTFfile - + string: path to the GTF file with annotations + +sjdbGTFchrPrefix - + string: prefix for chromosome names in a GTF file (e.g. 'chr' for using ENSMEBL annotations with UCSC genomes) + +sjdbGTFfeatureExon exon + string: feature type in GTF file to be used as exons for building transcripts + +sjdbGTFtagExonParentTranscript transcript_id + string: GTF attribute name for parent transcript ID (default "transcript_id" works for GTF files) + +sjdbGTFtagExonParentGene gene_id + string: GTF attribute name for parent gene ID (default "gene_id" works for GTF files) + +sjdbGTFtagExonParentGeneName gene_name + string(s): GTF attribute name for parent gene name + +sjdbGTFtagExonParentGeneType gene_type gene_biotype + string(s): GTF attribute name for parent gene type + +sjdbOverhang 100 + int>0: length of the donor/acceptor sequence on each side of the junctions, ideally = (mate_length - 1) + +sjdbScore 2 + int: extra alignment score for alignments that cross database junctions + +sjdbInsertSave Basic + string: which files to save when sjdb junctions are inserted on the fly at the mapping step + Basic ... only small junction / transcript files + All ... all files including big Genome, SA and SAindex - this will create a complete genome directory + +### Variation parameters +varVCFfile - + string: path to the VCF file that contains variation data. The 10th column should contain the genotype information, e.g. 0/1 + +### Input Files +inputBAMfile - + string: path to BAM input file, to be used with --runMode inputAlignmentsFromBAM + +### Read Parameters +readFilesType Fastx + string: format of input read files + Fastx ... FASTA or FASTQ + SAM SE ... SAM or BAM single-end reads; for BAM use --readFilesCommand samtools view + SAM PE ... SAM or BAM paired-end reads; for BAM use --readFilesCommand samtools view + +readFilesSAMattrKeep All + string(s): for --readFilesType SAM SE/PE, which SAM tags to keep in the output BAM, e.g.: --readFilesSAMtagsKeep RG PL + All ... keep all tags + None ... do not keep any tags + +readFilesIn Read1 Read2 + string(s): paths to files that contain input read1 (and, if needed, read2) + +readFilesManifest - + string: path to the "manifest" file with the names of read files. The manifest file should contain 3 tab-separated columns: + paired-end reads: read1_file_name $tab$ read2_file_name $tab$ read_group_line. + single-end reads: read1_file_name $tab$ - $tab$ read_group_line. + Spaces, but not tabs are allowed in file names. + If read_group_line does not start with ID:, it can only contain one ID field, and ID: will be added to it. + If read_group_line starts with ID:, it can contain several fields separated by $tab$, and all fields will be be copied verbatim into SAM @RG header line. + +readFilesPrefix - + string: prefix for the read files names, i.e. it will be added in front of the strings in --readFilesIn + +readFilesCommand - + string(s): command line to execute for each of the input file. This command should generate FASTA or FASTQ text and send it to stdout + For example: zcat - to uncompress .gz files, bzcat - to uncompress .bz2 files, etc. + +readMapNumber -1 + int: number of reads to map from the beginning of the file + -1: map all reads + +readMatesLengthsIn NotEqual + string: Equal/NotEqual - lengths of names,sequences,qualities for both mates are the same / not the same. NotEqual is safe in all situations. + +readNameSeparator / + string(s): character(s) separating the part of the read names that will be trimmed in output (read name after space is always trimmed) + +readQualityScoreBase 33 + int>=0: number to be subtracted from the ASCII code to get Phred quality score + +### Read Clipping + +clipAdapterType Hamming + string: adapter clipping type + Hamming ... adapter clipping based on Hamming distance, with the number of mismatches controlled by --clip5pAdapterMMp + CellRanger4 ... 5p and 3p adapter clipping similar to CellRanger4. Utilizes Opal package by Martin Šošić: https://github.com/Martinsos/opal + None ... no adapter clipping, all other clip* parameters are disregarded + +clip3pNbases 0 + int(s): number(s) of bases to clip from 3p of each mate. If one value is given, it will be assumed the same for both mates. + +clip3pAdapterSeq - + string(s): adapter sequences to clip from 3p of each mate. If one value is given, it will be assumed the same for both mates. + polyA ... polyA sequence with the length equal to read length + +clip3pAdapterMMp 0.1 + double(s): max proportion of mismatches for 3p adapter clipping for each mate. If one value is given, it will be assumed the same for both mates. + +clip3pAfterAdapterNbases 0 + int(s): number of bases to clip from 3p of each mate after the adapter clipping. If one value is given, it will be assumed the same for both mates. + +clip5pNbases 0 + int(s): number(s) of bases to clip from 5p of each mate. If one value is given, it will be assumed the same for both mates. + +#####UnderDevelopment_begin : not supported - do not use +clip5pAdapterSeq - + string(s): adapter sequences to clip from 5p of each mate, separated by space. + +clip5pAdapterMMp 0.1 + double(s): max proportion of mismatches for 5p adapter clipping for each mate, separated by space + +clip5pAfterAdapterNbases 0 + int(s): number of bases to clip from 5p of each mate after the adapter clipping, separated by space. +#####UnderDevelopment_end + +### Limits +limitGenomeGenerateRAM 31000000000 + int>0: maximum available RAM (bytes) for genome generation + +limitIObufferSize 30000000 50000000 + int(s)>0: max available buffers size (bytes) for input/output, per thread + +limitOutSAMoneReadBytes 100000 + int>0: max size of the SAM record (bytes) for one read. Recommended value: >(2*(LengthMate1+LengthMate2+100)*outFilterMultimapNmax + +limitOutSJoneRead 1000 + int>0: max number of junctions for one read (including all multi-mappers) + +limitOutSJcollapsed 1000000 + int>0: max number of collapsed junctions + +limitBAMsortRAM 0 + int>=0: maximum available RAM (bytes) for sorting BAM. If =0, it will be set to the genome index size. 0 value can only be used with --genomeLoad NoSharedMemory option. + +limitSjdbInsertNsj 1000000 + int>=0: maximum number of junctions to be inserted to the genome on the fly at the mapping stage, including those from annotations and those detected in the 1st step of the 2-pass run + +limitNreadsSoft -1 + int: soft limit on the number of reads + +### Output: general +outFileNamePrefix ./ + string: output files name prefix (including full or relative path). Can only be defined on the command line. + +outTmpDir - + string: path to a directory that will be used as temporary by STAR. All contents of this directory will be removed! + - ... the temp directory will default to outFileNamePrefix_STARtmp + +outTmpKeep None + string: whether to keep the temporary files after STAR runs is finished + None ... remove all temporary files + All ... keep all files + +outStd Log + string: which output will be directed to stdout (standard out) + Log ... log messages + SAM ... alignments in SAM format (which normally are output to Aligned.out.sam file), normal standard output will go into Log.std.out + BAM_Unsorted ... alignments in BAM format, unsorted. Requires --outSAMtype BAM Unsorted + BAM_SortedByCoordinate ... alignments in BAM format, sorted by coordinate. Requires --outSAMtype BAM SortedByCoordinate + BAM_Quant ... alignments to transcriptome in BAM format, unsorted. Requires --quantMode TranscriptomeSAM + +outReadsUnmapped None + string: output of unmapped and partially mapped (i.e. mapped only one mate of a paired end read) reads in separate file(s). + None ... no output + Fastx ... output in separate fasta/fastq files, Unmapped.out.mate1/2 + +outQSconversionAdd 0 + int: add this number to the quality score (e.g. to convert from Illumina to Sanger, use -31) + +outMultimapperOrder Old_2.4 + string: order of multimapping alignments in the output files + Old_2.4 ... quasi-random order used before 2.5.0 + Random ... random order of alignments for each multi-mapper. Read mates (pairs) are always adjacent, all alignment for each read stay together. This option will become default in the future releases. + +### Output: SAM and BAM +outSAMtype SAM + strings: type of SAM/BAM output + 1st word: + BAM ... output BAM without sorting + SAM ... output SAM without sorting + None ... no SAM/BAM output + 2nd, 3rd: + Unsorted ... standard unsorted + SortedByCoordinate ... sorted by coordinate. This option will allocate extra memory for sorting which can be specified by --limitBAMsortRAM. + +outSAMmode Full + string: mode of SAM output + None ... no SAM output + Full ... full SAM output + NoQS ... full SAM but without quality scores + +outSAMstrandField None + string: Cufflinks-like strand field flag + None ... not used + intronMotif ... strand derived from the intron motif. This option changes the output alignments: reads with inconsistent and/or non-canonical introns are filtered out. + +outSAMattributes Standard + string(s): a string of desired SAM attributes, in the order desired for the output SAM. Tags can be listed in any combination/order. + ***Presets: + None ... no attributes + Standard ... NH HI AS nM + All ... NH HI AS nM NM MD jM jI MC ch + ***Alignment: + NH ... number of loci the reads maps to: =1 for unique mappers, >1 for multimappers. Standard SAM tag. + HI ... multiple alignment index, starts with --outSAMattrIHstart (=1 by default). Standard SAM tag. + AS ... local alignment score, +1/-1 for matches/mismateches, score* penalties for indels and gaps. For PE reads, total score for two mates. Stadnard SAM tag. + nM ... number of mismatches. For PE reads, sum over two mates. + NM ... edit distance to the reference (number of mismatched + inserted + deleted bases) for each mate. Standard SAM tag. + MD ... string encoding mismatched and deleted reference bases (see standard SAM specifications). Standard SAM tag. + jM ... intron motifs for all junctions (i.e. N in CIGAR): 0: non-canonical; 1: GT/AG, 2: CT/AC, 3: GC/AG, 4: CT/GC, 5: AT/AC, 6: GT/AT. If splice junctions database is used, and a junction is annotated, 20 is added to its motif value. + jI ... start and end of introns for all junctions (1-based). + XS ... alignment strand according to --outSAMstrandField. + MC ... mate's CIGAR string. Standard SAM tag. + ch ... marks all segment of all chimeric alingments for --chimOutType WithinBAM output. + cN ... number of bases clipped from the read ends: 5' and 3' + ***Variation: + vA ... variant allele + vG ... genomic coordinate of the variant overlapped by the read. + vW ... 1 - alignment passes WASP filtering; 2,3,4,5,6,7 - alignment does not pass WASP filtering. Requires --waspOutputMode SAMtag. + ha ... haplotype (1/2) when mapping to the diploid genome. Requires genome generated with --genomeTransformType Diploid . + ***STARsolo: + CR CY UR UY ... sequences and quality scores of cell barcodes and UMIs for the solo* demultiplexing. + GX GN ... gene ID and gene name for unique-gene reads. + gx gn ... gene IDs and gene names for unique- and multi-gene reads. + CB UB ... error-corrected cell barcodes and UMIs for solo* demultiplexing. Requires --outSAMtype BAM SortedByCoordinate. + sM ... assessment of CB and UMI. + sS ... sequence of the entire barcode (CB,UMI,adapter). + sQ ... quality of the entire barcode. + sF ... type of feature overlap and number of features for each alignment + ***Unsupported/undocumented: + rB ... alignment block read/genomic coordinates. + vR ... read coordinate of the variant. + +outSAMattrIHstart 1 + int>=0: start value for the IH attribute. 0 may be required by some downstream software, such as Cufflinks or StringTie. + +outSAMunmapped None + string(s): output of unmapped reads in the SAM format + 1st word: + None ... no output + Within ... output unmapped reads within the main SAM file (i.e. Aligned.out.sam) + 2nd word: + KeepPairs ... record unmapped mate for each alignment, and, in case of unsorted output, keep it adjacent to its mapped mate. Only affects multi-mapping reads. + +outSAMorder Paired + string: type of sorting for the SAM output + Paired: one mate after the other for all paired alignments + PairedKeepInputOrder: one mate after the other for all paired alignments, the order is kept the same as in the input FASTQ files + +outSAMprimaryFlag OneBestScore + string: which alignments are considered primary - all others will be marked with 0x100 bit in the FLAG + OneBestScore ... only one alignment with the best score is primary + AllBestScore ... all alignments with the best score are primary + +outSAMreadID Standard + string: read ID record type + Standard ... first word (until space) from the FASTx read ID line, removing /1,/2 from the end + Number ... read number (index) in the FASTx file + +outSAMmapqUnique 255 + int: 0 to 255: the MAPQ value for unique mappers + +outSAMflagOR 0 + int: 0 to 65535: sam FLAG will be bitwise OR'd with this value, i.e. FLAG=FLAG | outSAMflagOR. This is applied after all flags have been set by STAR, and after outSAMflagAND. Can be used to set specific bits that are not set otherwise. + +outSAMflagAND 65535 + int: 0 to 65535: sam FLAG will be bitwise AND'd with this value, i.e. FLAG=FLAG & outSAMflagOR. This is applied after all flags have been set by STAR, but before outSAMflagOR. Can be used to unset specific bits that are not set otherwise. + +outSAMattrRGline - + string(s): SAM/BAM read group line. The first word contains the read group identifier and must start with "ID:", e.g. --outSAMattrRGline ID:xxx CN:yy "DS:z z z". + xxx will be added as RG tag to each output alignment. Any spaces in the tag values have to be double quoted. + Comma separated RG lines correspons to different (comma separated) input files in --readFilesIn. Commas have to be surrounded by spaces, e.g. + --outSAMattrRGline ID:xxx , ID:zzz "DS:z z" , ID:yyy DS:yyyy + +outSAMheaderHD - + strings: @HD (header) line of the SAM header + +outSAMheaderPG - + strings: extra @PG (software) line of the SAM header (in addition to STAR) + +outSAMheaderCommentFile - + string: path to the file with @CO (comment) lines of the SAM header + +outSAMfilter None + string(s): filter the output into main SAM/BAM files + KeepOnlyAddedReferences ... only keep the reads for which all alignments are to the extra reference sequences added with --genomeFastaFiles at the mapping stage. + KeepAllAddedReferences ... keep all alignments to the extra reference sequences added with --genomeFastaFiles at the mapping stage. + + +outSAMmultNmax -1 + int: max number of multiple alignments for a read that will be output to the SAM/BAM files. Note that if this value is not equal to -1, the top scoring alignment will be output first + -1 ... all alignments (up to --outFilterMultimapNmax) will be output + +outSAMtlen 1 + int: calculation method for the TLEN field in the SAM/BAM files + 1 ... leftmost base of the (+)strand mate to rightmost base of the (-)mate. (+)sign for the (+)strand mate + 2 ... leftmost base of any mate to rightmost base of any mate. (+)sign for the mate with the leftmost base. This is different from 1 for overlapping mates with protruding ends + +outBAMcompression 1 + int: -1 to 10 BAM compression level, -1=default compression (6?), 0=no compression, 10=maximum compression + +outBAMsortingThreadN 0 + int: >=0: number of threads for BAM sorting. 0 will default to min(6,--runThreadN). + +outBAMsortingBinsN 50 + int: >0: number of genome bins for coordinate-sorting + +### BAM processing +bamRemoveDuplicatesType - + string: mark duplicates in the BAM file, for now only works with (i) sorted BAM fed with inputBAMfile, and (ii) for paired-end alignments only + - ... no duplicate removal/marking + UniqueIdentical ... mark all multimappers, and duplicate unique mappers. The coordinates, FLAG, CIGAR must be identical + UniqueIdenticalNotMulti ... mark duplicate unique mappers but not multimappers. + +bamRemoveDuplicatesMate2basesN 0 + int>0: number of bases from the 5' of mate 2 to use in collapsing (e.g. for RAMPAGE) + +### Output Wiggle +outWigType None + string(s): type of signal output, e.g. "bedGraph" OR "bedGraph read1_5p". Requires sorted BAM: --outSAMtype BAM SortedByCoordinate . + 1st word: + None ... no signal output + bedGraph ... bedGraph format + wiggle ... wiggle format + 2nd word: + read1_5p ... signal from only 5' of the 1st read, useful for CAGE/RAMPAGE etc + read2 ... signal from only 2nd read + +outWigStrand Stranded + string: strandedness of wiggle/bedGraph output + Stranded ... separate strands, str1 and str2 + Unstranded ... collapsed strands + +outWigReferencesPrefix - + string: prefix matching reference names to include in the output wiggle file, e.g. "chr", default "-" - include all references + +outWigNorm RPM + string: type of normalization for the signal + RPM ... reads per million of mapped reads + None ... no normalization, "raw" counts + +### Output Filtering +outFilterType Normal + string: type of filtering + Normal ... standard filtering using only current alignment + BySJout ... keep only those reads that contain junctions that passed filtering into SJ.out.tab + +outFilterMultimapScoreRange 1 + int: the score range below the maximum score for multimapping alignments + +outFilterMultimapNmax 10 + int: maximum number of loci the read is allowed to map to. Alignments (all of them) will be output only if the read maps to no more loci than this value. + Otherwise no alignments will be output, and the read will be counted as "mapped to too many loci" in the Log.final.out . + +outFilterMismatchNmax 10 + int: alignment will be output only if it has no more mismatches than this value. + +outFilterMismatchNoverLmax 0.3 + real: alignment will be output only if its ratio of mismatches to *mapped* length is less than or equal to this value. + +outFilterMismatchNoverReadLmax 1.0 + real: alignment will be output only if its ratio of mismatches to *read* length is less than or equal to this value. + + +outFilterScoreMin 0 + int: alignment will be output only if its score is higher than or equal to this value. + +outFilterScoreMinOverLread 0.66 + real: same as outFilterScoreMin, but normalized to read length (sum of mates' lengths for paired-end reads) + +outFilterMatchNmin 0 + int: alignment will be output only if the number of matched bases is higher than or equal to this value. + +outFilterMatchNminOverLread 0.66 + real: sam as outFilterMatchNmin, but normalized to the read length (sum of mates' lengths for paired-end reads). + +outFilterIntronMotifs None + string: filter alignment using their motifs + None ... no filtering + RemoveNoncanonical ... filter out alignments that contain non-canonical junctions + RemoveNoncanonicalUnannotated ... filter out alignments that contain non-canonical unannotated junctions when using annotated splice junctions database. The annotated non-canonical junctions will be kept. + +outFilterIntronStrands RemoveInconsistentStrands + string: filter alignments + RemoveInconsistentStrands ... remove alignments that have junctions with inconsistent strands + None ... no filtering + +### Output splice junctions (SJ.out.tab) +outSJtype Standard + string: type of splice junction output + Standard ... standard SJ.out.tab output + None ... no splice junction output + +### Output Filtering: Splice Junctions +outSJfilterReads All + string: which reads to consider for collapsed splice junctions output + All ... all reads, unique- and multi-mappers + Unique ... uniquely mapping reads only + +outSJfilterOverhangMin 30 12 12 12 + 4 integers: minimum overhang length for splice junctions on both sides for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif + does not apply to annotated junctions + +outSJfilterCountUniqueMin 3 1 1 1 + 4 integers: minimum uniquely mapping read count per junction for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif + Junctions are output if one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin conditions are satisfied + does not apply to annotated junctions + +outSJfilterCountTotalMin 3 1 1 1 + 4 integers: minimum total (multi-mapping+unique) read count per junction for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif + Junctions are output if one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin conditions are satisfied + does not apply to annotated junctions + +outSJfilterDistToOtherSJmin 10 0 5 10 + 4 integers>=0: minimum allowed distance to other junctions' donor/acceptor + does not apply to annotated junctions + +outSJfilterIntronMaxVsReadN 50000 100000 200000 + N integers>=0: maximum gap allowed for junctions supported by 1,2,3,,,N reads + i.e. by default junctions supported by 1 read can have gaps <=50000b, by 2 reads: <=100000b, by 3 reads: <=200000. by >=4 reads any gap <=alignIntronMax + does not apply to annotated junctions + +### Scoring +scoreGap 0 + int: splice junction penalty (independent on intron motif) + +scoreGapNoncan -8 + int: non-canonical junction penalty (in addition to scoreGap) + +scoreGapGCAG -4 + int: GC/AG and CT/GC junction penalty (in addition to scoreGap) + +scoreGapATAC -8 + int: AT/AC and GT/AT junction penalty (in addition to scoreGap) + +scoreGenomicLengthLog2scale -0.25 + int: extra score logarithmically scaled with genomic length of the alignment: scoreGenomicLengthLog2scale*log2(genomicLength) + +scoreDelOpen -2 + int: deletion open penalty + +scoreDelBase -2 + int: deletion extension penalty per base (in addition to scoreDelOpen) + +scoreInsOpen -2 + int: insertion open penalty + +scoreInsBase -2 + int: insertion extension penalty per base (in addition to scoreInsOpen) + +scoreStitchSJshift 1 + int: maximum score reduction while searching for SJ boundaries in the stitching step + + +### Alignments and Seeding + +seedSearchStartLmax 50 + int>0: defines the search start point through the read - the read is split into pieces no longer than this value + +seedSearchStartLmaxOverLread 1.0 + real: seedSearchStartLmax normalized to read length (sum of mates' lengths for paired-end reads) + +seedSearchLmax 0 + int>=0: defines the maximum length of the seeds, if =0 seed length is not limited + +seedMultimapNmax 10000 + int>0: only pieces that map fewer than this value are utilized in the stitching procedure + +seedPerReadNmax 1000 + int>0: max number of seeds per read + +seedPerWindowNmax 50 + int>0: max number of seeds per window + +seedNoneLociPerWindow 10 + int>0: max number of one seed loci per window + +seedSplitMin 12 + int>0: min length of the seed sequences split by Ns or mate gap + +seedMapMin 5 + int>0: min length of seeds to be mapped + +alignIntronMin 21 + int: minimum intron size, genomic gap is considered intron if its length>=alignIntronMin, otherwise it is considered Deletion + +alignIntronMax 0 + int: maximum intron size, if 0, max intron size will be determined by (2^winBinNbits)*winAnchorDistNbins + +alignMatesGapMax 0 + int: maximum gap between two mates, if 0, max intron gap will be determined by (2^winBinNbits)*winAnchorDistNbins + +alignSJoverhangMin 5 + int>0: minimum overhang (i.e. block size) for spliced alignments + +alignSJstitchMismatchNmax 0 -1 0 0 + 4*int>=0: maximum number of mismatches for stitching of the splice junctions (-1: no limit). + (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. + +alignSJDBoverhangMin 3 + int>0: minimum overhang (i.e. block size) for annotated (sjdb) spliced alignments + +alignSplicedMateMapLmin 0 + int>0: minimum mapped length for a read mate that is spliced + +alignSplicedMateMapLminOverLmate 0.66 + real>0: alignSplicedMateMapLmin normalized to mate length + +alignWindowsPerReadNmax 10000 + int>0: max number of windows per read + +alignTranscriptsPerWindowNmax 100 + int>0: max number of transcripts per window + +alignTranscriptsPerReadNmax 10000 + int>0: max number of different alignments per read to consider + +alignEndsType Local + string: type of read ends alignment + Local ... standard local alignment with soft-clipping allowed + EndToEnd ... force end-to-end read alignment, do not soft-clip + Extend5pOfRead1 ... fully extend only the 5p of the read1, all other ends: local alignment + Extend5pOfReads12 ... fully extend only the 5p of the both read1 and read2, all other ends: local alignment + +alignEndsProtrude 0 ConcordantPair + int, string: allow protrusion of alignment ends, i.e. start (end) of the +strand mate downstream of the start (end) of the -strand mate + 1st word: int: maximum number of protrusion bases allowed + 2nd word: string: + ConcordantPair ... report alignments with non-zero protrusion as concordant pairs + DiscordantPair ... report alignments with non-zero protrusion as discordant pairs + +alignSoftClipAtReferenceEnds Yes + string: allow the soft-clipping of the alignments past the end of the chromosomes + Yes ... allow + No ... prohibit, useful for compatibility with Cufflinks + +alignInsertionFlush None + string: how to flush ambiguous insertion positions + None ... insertions are not flushed + Right ... insertions are flushed to the right + +### Paired-End reads +peOverlapNbasesMin 0 + int>=0: minimum number of overlapping bases to trigger mates merging and realignment. Specify >0 value to switch on the "merginf of overlapping mates" algorithm. + +peOverlapMMp 0.01 + real, >=0 & <1: maximum proportion of mismatched bases in the overlap area + +### Windows, Anchors, Binning + +winAnchorMultimapNmax 50 + int>0: max number of loci anchors are allowed to map to + +winBinNbits 16 + int>0: =log2(winBin), where winBin is the size of the bin for the windows/clustering, each window will occupy an integer number of bins. + +winAnchorDistNbins 9 + int>0: max number of bins between two anchors that allows aggregation of anchors into one window + +winFlankNbins 4 + int>0: log2(winFlank), where win Flank is the size of the left and right flanking regions for each window + +winReadCoverageRelativeMin 0.5 + real>=0: minimum relative coverage of the read sequence by the seeds in a window, for STARlong algorithm only. + +winReadCoverageBasesMin 0 + int>0: minimum number of bases covered by the seeds in a window , for STARlong algorithm only. + +### Chimeric Alignments +chimOutType Junctions + string(s): type of chimeric output + Junctions ... Chimeric.out.junction + SeparateSAMold ... output old SAM into separate Chimeric.out.sam file + WithinBAM ... output into main aligned BAM files (Aligned.*.bam) + WithinBAM HardClip ... (default) hard-clipping in the CIGAR for supplemental chimeric alignments (default if no 2nd word is present) + WithinBAM SoftClip ... soft-clipping in the CIGAR for supplemental chimeric alignments + +chimSegmentMin 0 + int>=0: minimum length of chimeric segment length, if ==0, no chimeric output + +chimScoreMin 0 + int>=0: minimum total (summed) score of the chimeric segments + +chimScoreDropMax 20 + int>=0: max drop (difference) of chimeric score (the sum of scores of all chimeric segments) from the read length + +chimScoreSeparation 10 + int>=0: minimum difference (separation) between the best chimeric score and the next one + +chimScoreJunctionNonGTAG -1 + int: penalty for a non-GT/AG chimeric junction + +chimJunctionOverhangMin 20 + int>=0: minimum overhang for a chimeric junction + +chimSegmentReadGapMax 0 + int>=0: maximum gap in the read sequence between chimeric segments + +chimFilter banGenomicN + string(s): different filters for chimeric alignments + None ... no filtering + banGenomicN ... Ns are not allowed in the genome sequence around the chimeric junction + +chimMainSegmentMultNmax 10 + int>=1: maximum number of multi-alignments for the main chimeric segment. =1 will prohibit multimapping main segments. + +chimMultimapNmax 0 + int>=0: maximum number of chimeric multi-alignments + 0 ... use the old scheme for chimeric detection which only considered unique alignments + +chimMultimapScoreRange 1 + int>=0: the score range for multi-mapping chimeras below the best chimeric score. Only works with --chimMultimapNmax > 1 + +chimNonchimScoreDropMin 20 + int>=0: to trigger chimeric detection, the drop in the best non-chimeric alignment score with respect to the read length has to be greater than this value + +chimOutJunctionFormat 0 + int: formatting type for the Chimeric.out.junction file + 0 ... no comment lines/headers + 1 ... comment lines at the end of the file: command line and Nreads: total, unique/multi-mapping + +### Quantification of Annotations +quantMode - + string(s): types of quantification requested + - ... none + TranscriptomeSAM ... output SAM/BAM alignments to transcriptome into a separate file + GeneCounts ... count reads per gene + +quantTranscriptomeBAMcompression 1 + int: -2 to 10 transcriptome BAM compression level + -2 ... no BAM output + -1 ... default compression (6?) + 0 ... no compression + 10 ... maximum compression + +quantTranscriptomeSAMoutput BanSingleEnd_BanIndels_ExtendSoftclip + string: alignment filtering for TranscriptomeSAM output + BanSingleEnd_BanIndels_ExtendSoftclip ... prohibit indels and single-end alignments, extend softclips - compatible with RSEM + BanSingleEnd ... prohibit single-end alignments, allow indels and softclips + BanSingleEnd_ExtendSoftclip ... prohibit single-end alignments, extend softclips, allow indels + + +### 2-pass Mapping +twopassMode None + string: 2-pass mapping mode. + None ... 1-pass mapping + Basic ... basic 2-pass mapping, with all 1st pass junctions inserted into the genome indices on the fly + +twopass1readsN -1 + int: number of reads to process for the 1st step. Use very large number (or default -1) to map all reads in the first step. + + +### WASP parameters +waspOutputMode None + string: WASP allele-specific output type. This is re-implementation of the original WASP mappability filtering by Bryce van de Geijn, Graham McVicker, Yoav Gilad & Jonathan K Pritchard. Please cite the original WASP paper: Nature Methods 12, 1061–1063 (2015), https://www.nature.com/articles/nmeth.3582 . + SAMtag ... add WASP tags to the alignments that pass WASP filtering + +### STARsolo (single cell RNA-seq) parameters +soloType None + string(s): type of single-cell RNA-seq + CB_UMI_Simple ... (a.k.a. Droplet) one UMI and one Cell Barcode of fixed length in read2, e.g. Drop-seq and 10X Chromium. + CB_UMI_Complex ... multiple Cell Barcodes of varying length, one UMI of fixed length and one adapter sequence of fixed length are allowed in read2 only (e.g. inDrop, ddSeq). + CB_samTagOut ... output Cell Barcode as CR and/or CB SAm tag. No UMI counting. --readFilesIn cDNA_read1 [cDNA_read2 if paired-end] CellBarcode_read . Requires --outSAMtype BAM Unsorted [and/or SortedByCoordinate] + SmartSeq ... Smart-seq: each cell in a separate FASTQ (paired- or single-end), barcodes are corresponding read-groups, no UMI sequences, alignments deduplicated according to alignment start and end (after extending soft-clipped bases) + +soloCBtype Sequence + string: cell barcode type + Sequence: cell barcode is a sequence (standard option) + String: cell barcode is an arbitrary string + +soloCBwhitelist - + string(s): file(s) with whitelist(s) of cell barcodes. Only --soloType CB_UMI_Complex allows more than one whitelist file. + None ... no whitelist: all cell barcodes are allowed + +soloCBstart 1 + int>0: cell barcode start base + +soloCBlen 16 + int>0: cell barcode length + +soloUMIstart 17 + int>0: UMI start base + +soloUMIlen 10 + int>0: UMI length + +soloBarcodeReadLength 1 + int: length of the barcode read + 1 ... equal to sum of soloCBlen+soloUMIlen + 0 ... not defined, do not check + +soloBarcodeMate 0 + int: identifies which read mate contains the barcode (CB+UMI) sequence + 0 ... barcode sequence is on separate read, which should always be the last file in the --readFilesIn listed + 1 ... barcode sequence is a part of mate 1 + 2 ... barcode sequence is a part of mate 2 + +soloCBposition - + strings(s): position of Cell Barcode(s) on the barcode read. + Presently only works with --soloType CB_UMI_Complex, and barcodes are assumed to be on Read2. + Format for each barcode: startAnchor_startPosition_endAnchor_endPosition + start(end)Anchor defines the Anchor Base for the CB: 0: read start; 1: read end; 2: adapter start; 3: adapter end + start(end)Position is the 0-based position with of the CB start(end) with respect to the Anchor Base + String for different barcodes are separated by space. + Example: inDrop (Zilionis et al, Nat. Protocols, 2017): + --soloCBposition 0_0_2_-1 3_1_3_8 + +soloUMIposition - + string: position of the UMI on the barcode read, same as soloCBposition + Example: inDrop (Zilionis et al, Nat. Protocols, 2017): + --soloCBposition 3_9_3_14 + +soloAdapterSequence - + string: adapter sequence to anchor barcodes. Only one adapter sequence is allowed. + +soloAdapterMismatchesNmax 1 + int>0: maximum number of mismatches allowed in adapter sequence. + +soloCBmatchWLtype 1MM_multi + string: matching the Cell Barcodes to the WhiteList + Exact ... only exact matches allowed + 1MM ... only one match in whitelist with 1 mismatched base allowed. Allowed CBs have to have at least one read with exact match. + 1MM_multi ... multiple matches in whitelist with 1 mismatched base allowed, posterior probability calculation is used choose one of the matches. + Allowed CBs have to have at least one read with exact match. This option matches best with CellRanger 2.2.0 + 1MM_multi_pseudocounts ... same as 1MM_Multi, but pseudocounts of 1 are added to all whitelist barcodes. + 1MM_multi_Nbase_pseudocounts ... same as 1MM_multi_pseudocounts, multimatching to WL is allowed for CBs with N-bases. This option matches best with CellRanger >= 3.0.0 + EditDist_2 ... allow up to edit distance of 3 fpr each of the barcodes. May include one deletion + one insertion. Only works with --soloType CB_UMI_Complex. Matches to multiple passlist barcdoes are not allowed. Similar to ParseBio Split-seq pipeline. + +soloInputSAMattrBarcodeSeq - + string(s): when inputting reads from a SAM file (--readsFileType SAM SE/PE), these SAM attributes mark the barcode sequence (in proper order). + For instance, for 10X CellRanger or STARsolo BAMs, use --soloInputSAMattrBarcodeSeq CR UR . + This parameter is required when running STARsolo with input from SAM. + +soloInputSAMattrBarcodeQual - + string(s): when inputting reads from a SAM file (--readsFileType SAM SE/PE), these SAM attributes mark the barcode qualities (in proper order). + For instance, for 10X CellRanger or STARsolo BAMs, use --soloInputSAMattrBarcodeQual CY UY . + If this parameter is '-' (default), the quality 'H' will be assigned to all bases. + +soloStrand Forward + string: strandedness of the solo libraries: + Unstranded ... no strand information + Forward ... read strand same as the original RNA molecule + Reverse ... read strand opposite to the original RNA molecule + +soloFeatures Gene + string(s): genomic features for which the UMI counts per Cell Barcode are collected + Gene ... genes: reads match the gene transcript + SJ ... splice junctions: reported in SJ.out.tab + GeneFull ... full gene (pre-mRNA): count all reads overlapping genes' exons and introns + GeneFull_ExonOverIntron ... full gene (pre-mRNA): count all reads overlapping genes' exons and introns: prioritize 100% overlap with exons + GeneFull_Ex50pAS ... full gene (pre-RNA): count all reads overlapping genes' exons and introns: prioritize >50% overlap with exons. Do not count reads with 100% exonic overlap in the antisense direction. + +#####UnderDevelopment_begin : not supported - do not use + Transcript3p ... quantification of transcript for 3' protocols +#####UnderDevelopment_end + +soloMultiMappers Unique + string(s): counting method for reads mapping to multiple genes + Unique ... count only reads that map to unique genes + Uniform ... uniformly distribute multi-genic UMIs to all genes + Rescue ... distribute UMIs proportionally to unique+uniform counts (~ first iteration of EM) + PropUnique ... distribute UMIs proportionally to unique mappers, if present, and uniformly if not. + EM ... multi-gene UMIs are distributed using Expectation Maximization algorithm + +soloUMIdedup 1MM_All + string(s): type of UMI deduplication (collapsing) algorithm + 1MM_All ... all UMIs with 1 mismatch distance to each other are collapsed (i.e. counted once). + 1MM_Directional_UMItools ... follows the "directional" method from the UMI-tools by Smith, Heger and Sudbery (Genome Research 2017). + 1MM_Directional ... same as 1MM_Directional_UMItools, but with more stringent criteria for duplicate UMIs + Exact ... only exactly matching UMIs are collapsed. + NoDedup ... no deduplication of UMIs, count all reads. + 1MM_CR ... CellRanger2-4 algorithm for 1MM UMI collapsing. + +soloUMIfiltering - + string(s): type of UMI filtering (for reads uniquely mapping to genes) + - ... basic filtering: remove UMIs with N and homopolymers (similar to CellRanger 2.2.0). + MultiGeneUMI ... basic + remove lower-count UMIs that map to more than one gene. + MultiGeneUMI_All ... basic + remove all UMIs that map to more than one gene. + MultiGeneUMI_CR ... basic + remove lower-count UMIs that map to more than one gene, matching CellRanger > 3.0.0 . + Only works with --soloUMIdedup 1MM_CR + +soloOutFileNames Solo.out/ features.tsv barcodes.tsv matrix.mtx + string(s): file names for STARsolo output: + file_name_prefix gene_names barcode_sequences cell_feature_count_matrix + +soloCellFilter CellRanger2.2 3000 0.99 10 + string(s): cell filtering type and parameters + None ... do not output filtered cells + TopCells ... only report top cells by UMI count, followed by the exact number of cells + CellRanger2.2 ... simple filtering of CellRanger 2.2. + Can be followed by numbers: number of expected cells, robust maximum percentile for UMI count, maximum to minimum ratio for UMI count + The harcoded values are from CellRanger: nExpectedCells=3000; maxPercentile=0.99; maxMinRatio=10 + EmptyDrops_CR ... EmptyDrops filtering in CellRanger flavor. Please cite the original EmptyDrops paper: A.T.L Lun et al, Genome Biology, 20, 63 (2019): https://genomebiology.biomedcentral.com/articles/10.1186/s13059-019-1662-y + Can be followed by 10 numeric parameters: nExpectedCells maxPercentile maxMinRatio indMin indMax umiMin umiMinFracMedian candMaxN FDR simN + The harcoded values are from CellRanger: 3000 0.99 10 45000 90000 500 0.01 20000 0.01 10000 + +soloOutFormatFeaturesGeneField3 "Gene Expression" + string(s): field 3 in the Gene features.tsv file. If "-", then no 3rd field is output. + +soloCellReadStats None + string: Output reads statistics for each CB + Standard ... standard output + +#####UnderDevelopment_begin : not supported - do not use +soloClusterCBfile - + string: file containing the cluster information for cell barcodes, two columns: CB cluster_index. Only used with --soloFeatures Transcript3p +#####UnderDevelopment_end diff --git a/src/star/star_align_reads/script.py b/src/star/star_align_reads/script.py new file mode 100644 index 00000000..2bde8798 --- /dev/null +++ b/src/star/star_align_reads/script.py @@ -0,0 +1,109 @@ +import tempfile +import subprocess +import shutil +from pathlib import Path + +## VIASH START +par = { + "input": [ + "src/star/star_align_reads/test_data/a_R1.1.fastq", + "src/star/star_align_reads/test_data/a_R1.2.fastq", + ], + "input_r2": [ + "src/star/star_align_reads/test_data/a_R2.1.fastq", + "src/star/star_align_reads/test_data/a_R2.2.fastq", + ], + "genomeDir": "src/star/star_align_reads/test_data/genome.fasta", + "aligned_reads": "aligned_reads.sam" +} +meta = { + "cpus": 8, + "temp_dir": "/tmp" +} +## VIASH END + +################################################## +# check and process SE / PE R1 input files +input_r1 = par["input"] +readFilesIn = ",".join(par["input"]) +par["input"] = None + +# check and process PE R2 input files +input_r2 = par["input_r2"] +if input_r2 is not None: + if len(input_r1) != len(input_r2): + raise ValueError("The number of R1 and R2 files do not match.") + readFilesIn = [readFilesIn, ",".join(par["input_r2"])] + par["input_r2"] = None + +# store readFilesIn +par["readFilesIn"] = readFilesIn + +################################################## + +# determine readFilesCommand +if input_r1[0].endswith(".gz"): + print(">> Input files are gzipped, setting readFilesCommand to zcat", flush=True) + par["readFilesCommand"] = "zcat" +elif input_r1[0].endswith(".bz2"): + print(">> Input files are bzipped, setting readFilesCommand to bzcat", flush=True) + par["readFilesCommand"] = "bzcat" + +################################################## +# store output paths +expected_outputs = { + "aligned_reads": ["Aligned.out.sam", "Aligned.out.bam"], + "reads_per_gene": "ReadsPerGene.out.tab", + "chimeric_junctions": "Chimeric.out.junction", + "log": "Log.final.out", + "splice_junctions": "SJ.out.tab", + "unmapped": "Unmapped.out.mate1", + "unmapped_r2": "Unmapped.out.mate2" +} +output_paths = {name: par[name] for name in expected_outputs.keys()} +for name in expected_outputs.keys(): + par[name] = None + +################################################## +# process other args +par["runMode"] = "alignReads" + +if "cpus" in meta and meta["cpus"]: + par["runThreadN"] = meta["cpus"] + +################################################## +# run STAR and move output to final destination +with tempfile.TemporaryDirectory(prefix="star-", dir=meta["temp_dir"], ignore_cleanup_errors=True) as temp_dir: + print(">> Constructing command", flush=True) + + # set output paths + temp_dir = Path(temp_dir) + par["outTmpDir"] = temp_dir / "tempdir" + out_dir = temp_dir / "out" + par["outFileNamePrefix"] = f"{out_dir}/" # star needs this slash + + # construct command + cmd_args = [ "STAR" ] + for name, value in par.items(): + if value is not None: + val_to_add = value if isinstance(value, list) else [value] + cmd_args.extend([f"--{name}"] + [str(x) for x in val_to_add]) + print("", flush=True) + + # run command + print(">> Running STAR with command:", flush=True) + print(f"+ {' '.join(cmd_args)}", end="\n\n", flush=True) + subprocess.run( + cmd_args, + check=True + ) + print(">> STAR finished successfully", end="\n\n", flush=True) + + # move output to final destination + print(">> Moving output to final destination", flush=True) + for name, paths in expected_outputs.items(): + for expected_path in [paths] if isinstance(paths, str) else paths: + expected_full_path = out_dir / expected_path + if output_paths[name] and expected_full_path.is_file(): + print(f">> Moving {expected_path} to {output_paths[name]}", flush=True) + shutil.move(expected_full_path, output_paths[name]) diff --git a/src/star/star_align_reads/test.sh b/src/star/star_align_reads/test.sh new file mode 100644 index 00000000..374b9014 --- /dev/null +++ b/src/star/star_align_reads/test.sh @@ -0,0 +1,173 @@ +#!/bin/bash + +set -e + +## VIASH START +meta_executable="target/docker/star/star_align_reads/star_align_reads" +meta_resources_dir="src/star/star_align_reads" +## VIASH END + +######################################################################################### + +# helper functions +assert_file_exists() { + [ -f "$1" ] || (echo "File '$1' does not exist" && exit 1) +} +assert_file_doesnt_exist() { + [ ! -f "$1" ] || (echo "File '$1' exists but shouldn't" && exit 1) +} +assert_file_empty() { + [ ! -s "$1" ] || (echo "File '$1' is not empty but should be" && exit 1) +} +assert_file_not_empty() { + [ -s "$1" ] || (echo "File '$1' is empty but shouldn't be" && exit 1) +} +assert_file_contains() { + grep -q "$2" "$1" || (echo "File '$1' does not contain '$2'" && exit 1) +} +assert_file_not_contains() { + grep -q "$2" "$1" && (echo "File '$1' contains '$2' but shouldn't" && exit 1) +} +assert_file_contains_regex() { + grep -q -E "$2" "$1" || (echo "File '$1' does not contain '$2'" && exit 1) +} +assert_file_not_contains_regex() { + grep -q -E "$2" "$1" && (echo "File '$1' contains '$2' but shouldn't" && exit 1) +} + +######################################################################################### +echo "> Prepare test data" + +cat > reads_R1.fastq <<'EOF' +@SEQ_ID1 +ACGCTGCCTCATAAGCCTCACACAT ++ +IIIIIIIIIIIIIIIIIIIIIIIII +@SEQ_ID2 +ACCCGCAAGATTAGGCTCCGTACAC ++ +!!!!!!!!!!!!!!!!!!!!!!!!! +EOF + +cat > reads_R2.fastq <<'EOF' +@SEQ_ID1 +ATGTGTGAGGCTTATGAGGCAGCGT ++ +IIIIIIIIIIIIIIIIIIIIIIIII +@SEQ_ID2 +GTGTACGGAGCCTAATCTTGCAGGG ++ +!!!!!!!!!!!!!!!!!!!!!!!!! +EOF + +cat > genome.fasta <<'EOF' +>chr1 +TGGCATGAGCCAACGAACGCTGCCTCATAAGCCTCACACATCCGCGCCTATGTTGTGACTCTCTGTGAGCGTTCGTGGG +GCTCGTCACCACTATGGTTGGCCGGTTAGTAGTGTGACTCCTGGTTTTCTGGAGCTTCTTTAAACCGTAGTCCAGTCAA +TGCGAATGGCACTTCACGACGGACTGTCCTTAGCTCAGGGGA +EOF + +cat > genes.gtf <<'EOF' +chr1 example_source gene 0 50 . + . gene_id "gene1"; transcript_id "transcript1"; +chr1 example_source exon 20 40 . + . gene_id "gene1"; transcript_id "transcript1"; +EOF + +echo "> Generate index" +STAR \ + ${meta_cpus:+--runThreadN $meta_cpus} \ + --runMode genomeGenerate \ + --genomeDir "index/" \ + --genomeFastaFiles "genome.fasta" \ + --sjdbGTFfile "genes.gtf" \ + --genomeSAindexNbases 2 + +######################################################################################### + +mkdir star_align_reads_se +cd star_align_reads_se + +echo "> Run star_align_reads on SE" +"$meta_executable" \ + --input "../reads_R1.fastq" \ + --genomeDir "../index/" \ + --aligned_reads "output.sam" \ + --log "log.txt" \ + --outReadsUnmapped "Fastx" \ + --unmapped "unmapped.sam" \ + --quantMode "TranscriptomeSAM;GeneCounts" \ + --reads_per_gene "reads_per_gene.tsv" \ + --outSJtype Standard \ + --splice_junctions "splice_junctions.tsv" \ + ${meta_cpus:+---cpus $meta_cpus} + +# TODO: Test data doesn't contain any chimeric reads yet +# --chimOutType "Junctions" \ +# --chimeric_junctions "chimeric_junctions.tsv" \ + +echo ">> Check if output exists" +assert_file_exists "output.sam" +assert_file_exists "log.txt" +assert_file_exists "reads_per_gene.tsv" +# assert_file_exists "chimeric_junctions.tsv" +assert_file_exists "splice_junctions.tsv" +assert_file_exists "unmapped.sam" + +echo ">> Check if output contents are not empty" +assert_file_not_empty "output.sam" +assert_file_not_empty "log.txt" +assert_file_not_empty "reads_per_gene.tsv" +# assert_file_not_empty "chimeric_junctions.tsv" +# assert_file_not_empty "splice_junctions.tsv" # TODO: test data doesn't contain any splice junctions yet +assert_file_not_empty "unmapped.sam" + +echo ">> Check if output contents are correct" +assert_file_contains "log.txt" "Number of input reads \\| 2" +assert_file_contains "log.txt" "Number of reads unmapped: too short \\| 1" +assert_file_contains "log.txt" "Uniquely mapped reads number \\| 1" +assert_file_contains "reads_per_gene.tsv" "gene1 1 1 0" +assert_file_contains "reads_per_gene.tsv" "N_unmapped 1 1 1" +assert_file_contains "output.sam" "SEQ_ID1 0 chr1 17 255 25M \\* 0 0 ACGCTGCCTCATAAGCCTCACACAT IIIIIIIIIIIIIIIIIIIIIIIII NH:i:1 HI:i:1 AS:i:24 nM:i:0" +assert_file_contains "unmapped.sam" "@SEQ_ID2 0:N:" +assert_file_contains "unmapped.sam" "ACCCGCAAGATTAGGCTCCGTACAC" + +cd .. + +######################################################################################### + +mkdir star_align_reads_pe_minimal +cd star_align_reads_pe_minimal + +echo ">> Run star_align_reads on PE" +"$meta_executable" \ + --input ../reads_R1.fastq \ + --input_r2 ../reads_R2.fastq \ + --genomeDir ../index/ \ + --aligned_reads output.bam \ + --log log.txt \ + --outReadsUnmapped Fastx \ + --unmapped unmapped_r1.bam \ + --unmapped_r2 unmapped_r2.bam \ + ${meta_cpus:+---cpus $meta_cpus} + +echo ">> Check if output exists" +assert_file_exists "output.bam" +assert_file_exists "log.txt" +assert_file_exists "unmapped_r1.bam" +assert_file_exists "unmapped_r2.bam" + +echo ">> Check if output contents are not empty" +assert_file_not_empty "output.bam" +assert_file_not_empty "log.txt" +assert_file_not_empty "unmapped_r1.bam" +assert_file_not_empty "unmapped_r2.bam" + +echo ">> Check if output contents are correct" +assert_file_contains "log.txt" "Number of input reads \\| 2" +assert_file_contains "log.txt" "Number of reads unmapped: too short \\| 1" +assert_file_contains "log.txt" "Uniquely mapped reads number \\| 1" + +cd .. + +######################################################################################### + +echo "> Test successful" diff --git a/src/star/star_align_reads/utils/process_params.R b/src/star/star_align_reads/utils/process_params.R new file mode 100644 index 00000000..ccdc50b3 --- /dev/null +++ b/src/star/star_align_reads/utils/process_params.R @@ -0,0 +1,189 @@ +library(tidyverse) + +# This script processes the STAR aligner's help file +# to create a viash argument_groups.yaml file. + +local_file <- "src/star/star_align_reads/help.txt" +yaml_file <- "src/star/star_align_reads/argument_groups.yaml" + +param_txt <- readr::read_lines(local_file) + +# replace non-ascii characters with their ascii approximations +param_txt <- iconv(param_txt, "UTF-8", "ASCII//TRANSLIT") + +dev_begin <- grep("#####UnderDevelopment_begin", param_txt) +dev_end <- grep("#####UnderDevelopment_end", param_txt) + +# strip development sections +nondev_ix <- unlist(map2(c(1, dev_end + 1), c(dev_begin - 1, length(param_txt)), function(i, j) { + if (i >= 1 && i < j) { + seq(i, j, 1) + } else { + NULL + } +})) + +param_txt2 <- param_txt[nondev_ix] + +# strip comments +param_txt3 <- param_txt2[-grep("^#[^#]", param_txt2)] + +# detect groups +group_ix <- grep("^### ", param_txt3) + +out <- map2_dfr( + group_ix, + c(group_ix[-1] - 1, length(param_txt3)), + function(group_start, group_end) { + # cat("group_start <- ", group_start, "; group_end <- ", group_end, "\n", sep = "") + group_name <- gsub("^### ", "", param_txt3[[group_start]]) + + group_txt <- param_txt3[seq(group_start + 1, group_end)] + + arg_ix <- grep("^[^ ]", group_txt) + + arguments <- map2_dfr( + arg_ix, + c(arg_ix[-1] - 1, length(group_txt)), + function(arg_start, arg_end) { + # cat("arg_start <- ", arg_start, "; arg_end <- ", arg_end, "\n", sep = "") + + # process name and default + first_txt <- group_txt[[arg_start]] + first_regex <- "^([^ ]*) +(.*) *$" + if (!grepl(first_regex, first_txt)) { + stop("Line '", first_txt, "' did not match regex '", first_regex, "'") + } + name <- gsub(first_regex, "\\1", first_txt) + default <- gsub(first_regex, "\\2", first_txt) + + # process type and first description + second_txt <- group_txt[[arg_start + 1]] + second_regex <- "^ +([^:]*):[ ]+(.*)$" + if (!grepl(second_regex, second_txt)) { + stop("Line '", second_txt, "' did not match regex '", second_regex, "'") + } + type <- gsub(second_regex, "\\1", second_txt) + desc_start <- str_trim(gsub(second_regex, "\\2", second_txt)) + + # process more description + desc_cont1 <- group_txt[seq(arg_start + 2, arg_end)] + + desc <- + if (sum(str_length(desc_cont1)) == 0) { + desc_start + } else { + # detect margin + margins <- str_extract(desc_cont1, "^( +)") %>% na.omit + margin <- margins[[which.min(str_length(margins))]] + desc_cont2 <- gsub(paste0("^", margin), "", desc_cont1) + desc_cont3 <- ifelse(grepl("\\.\\.\\.", desc_cont2), paste0("- ", desc_cont2), desc_cont2) + desc_cont4 <- str_trim(desc_cont3) + + # construct desc + str_trim(paste0(c(desc_start, "", desc_cont4), "\n", collapse = "")) + } + + tibble( + group_name, + name, + default, + type, + description = desc + ) + } + ) + + arguments + } +) + +# todo: manually fix alignEndsProtrude? +# assigning types +type_map <- c("string" = "string", "int" = "integer", "real" = "double", "double" = "double", "int, string" = "string") +file_args <- c("genomeDir", "readFilesIn", "sjdbGTFfile", "genomeFastaFiles", "genomeChainFiles", "readFilesManifest") +long_args <- c("limitGenomeGenerateRAM", "limitIObufferSize", "limitOutSAMoneReadBytes", "limitBAMsortRAM") +required_args <- c("genomeDir", "readFilesIn") + +# converting examples +as_safe_int <- function(x) tryCatch({as.integer(x)}, warning = function(e) { bit64::as.integer64(x) }) +safe_split <- function(x) strsplit(x, "'[^']*'(*SKIP)(*F)|\"[^\"]*\"(*SKIP)(*F)|\\s+", perl = TRUE)[[1]] %>% gsub("^[\"']|[\"']$", "", .) +trafos <- list( + string = function(x) x, + integer = as_safe_int, + double = as.numeric, + strings = function(x) safe_split(x), + integers = function(x) sapply(safe_split(x), as_safe_int), + doubles = function(x) as.numeric(safe_split(x)) +) +# remove arguments that are not relevant for viash +removed_args <- c("versionGenome", "parametersFiles", "sysShell", "runDirPerm") +# these settings are defined by the viash component +manual_args <- c("runThreadN", "outTmpDir", "runMode", "outFileNamePrefix", "readFilesIn") + +# make viash-like values +out2 <- out %>% + # remove arguments that are not relevant for viash + filter(!name %in% c(removed_args, manual_args)) %>% + # remove arguments that are related to a different runmode + filter(!grepl("--runMode", description) | grepl("--runMode alignReads", description)) %>% + filter(!grepl("--runMode", group_name) | grepl("--runMode alignReads", group_name)) %>% + filter(!grepl("STARsolo", group_name)) %>% + mutate( + viash_arg = paste0("--", name), + type_step1 = type %>% + str_replace_all(".*(int, string|string|int|real|double)\\(?(s?).*", "\\1\\2"), + viash_type = type_map[gsub("(int, string|string|int|real|double).*", "\\1", type_step1)], + multiple = type_step1 == "int, string" | grepl("s$", type_step1) | grepl("^[4N][\\* ]", type), + default_step1 = default %>% + {ifelse(. %in% c("-", "None"), NA_character_, .)}, + viash_default = + mapply( + default_step1, + paste0(viash_type, ifelse(multiple, "s", "")), + FUN = function(str, typ) trafos[[typ]](str) + ), + # viash_type = ifelse(sapply(viash_default, bit64::is.integer64), "long", viash_type), + # update type + viash_type = case_when( + name %in% long_args ~ "long", + name %in% file_args ~ "file", + TRUE ~ viash_type + ), + # turn longs into character because yaml::write_yaml doesn't handle longs well + viash_default = ifelse(sapply(viash_default, bit64::is.integer64), map(viash_default, as.character), viash_default), + group_name = gsub(" - .*", "", group_name), + required = ifelse(name %in% required_args, TRUE, NA) + ) +print(out2, n = 200) +out2 %>% mutate(i = row_number()) %>% + # filter(is.na(default_step1) != is.na(viash_default)) %>% + select(-group_name, -description) + +out2 %>% filter(!grepl("--runMode", description) | grepl("--runMode alignReads", description)) + +argument_groups <- map(unique(out2$group_name), function(group_name) { + args <- out2 %>% + filter(group_name == !!group_name) %>% + pmap(function(viash_arg, viash_type, multiple, viash_default, description, required, ...) { + li <- lst( + name = viash_arg, + type = viash_type, + description = description + ) + if (all(!is.na(viash_default))) { + li$example <- viash_default + } + if (!is.na(multiple) && multiple) { + li$multiple <- multiple + li$multiple_sep <- ";" + } + if (!is.na(required) && required) { + li$required <- required + } + li + }) + list(name = group_name, arguments = args) +}) + +yaml::write_yaml(list(argument_groups = argument_groups), yaml_file) diff --git a/src/star/star_genome_generate/config.vsh.yaml b/src/star/star_genome_generate/config.vsh.yaml new file mode 100644 index 00000000..3adaf7a2 --- /dev/null +++ b/src/star/star_genome_generate/config.vsh.yaml @@ -0,0 +1,139 @@ +name: star_genome_generate +namespace: star +description: | + Create index for STAR +keywords: [genome, index, align] +links: + repository: https://github.com/alexdobin/STAR + documentation: https://github.com/alexdobin/STAR/blob/master/doc/STARmanual.pdf +references: + doi: 10.1093/bioinformatics/bts635 +license: MIT +requirements: + commands: [ STAR ] + +argument_groups: +- name: "Input" + arguments: + - name: "--genomeFastaFiles" + type: file + description: | + Path(s) to the fasta files with the genome sequences, separated by spaces. These files should be plain text FASTA files, they *cannot* be zipped. + required: true + multiple: yes + multiple_sep: ; + - name: "--sjdbGTFfile" + type: file + description: Path to the GTF file with annotations + - name: --sjdbOverhang + type: integer + description: Length of the donor/acceptor sequence on each side of the junctions, ideally = (mate_length - 1) + example: 100 + - name: --sjdbGTFchrPrefix + type: string + description: Prefix for chromosome names in a GTF file (e.g. 'chr' for using ENSMEBL annotations with UCSC genomes) + - name: --sjdbGTFfeatureExon + type: string + description: Feature type in GTF file to be used as exons for building transcripts + example: exon + - name: --sjdbGTFtagExonParentTranscript + type: string + description: GTF attribute name for parent transcript ID (default "transcript_id" works for GTF files) + example: transcript_id + - name: --sjdbGTFtagExonParentGene + type: string + description: GTF attribute name for parent gene ID (default "gene_id" works for GTF files) + example: gene_id + - name: --sjdbGTFtagExonParentGeneName + type: string + description: GTF attribute name for parent gene name + example: gene_name + multiple: yes + multiple_sep: ; + - name: --sjdbGTFtagExonParentGeneType + type: string + description: GTF attribute name for parent gene type + example: + - gene_type + - gene_biotype + multiple: yes + multiple_sep: ; + - name: --limitGenomeGenerateRAM + type: long + description: Maximum available RAM (bytes) for genome generation + example: '31000000000' + - name: --genomeSAindexNbases + type: integer + description: Length (bases) of the SA pre-indexing string. Typically between 10 and 15. Longer strings will use much more memory, but allow faster searches. For small genomes, this parameter must be scaled down to min(14, log2(GenomeLength)/2 - 1). + example: 14 + - name: --genomeChrBinNbits + type: integer + description: Defined as log2(chrBin), where chrBin is the size of the bins for genome storage. Each chromosome will occupy an integer number of bins. For a genome with large number of contigs, it is recommended to scale this parameter as min(18, log2[max(GenomeLength/NumberOfReferences,ReadLength)]). + example: 18 + - name: --genomeSAsparseD + type: integer + min: 0 + example: 1 + description: Suffux array sparsity, i.e. distance between indices. Use bigger numbers to decrease needed RAM at the cost of mapping speed reduction. + - name: --genomeSuffixLengthMax + type: integer + description: Maximum length of the suffixes, has to be longer than read length. Use -1 for infinite length. + example: -1 + - name: --genomeTransformType + type: string + description: | + Type of genome transformation + None ... no transformation + Haploid ... replace reference alleles with alternative alleles from VCF file (e.g. consensus allele) + Diploid ... create two haplotypes for each chromosome listed in VCF file, for genotypes 1|2, assumes perfect phasing (e.g. personal genome) + example: None + - name: --genomeTransformVCF + type: file + description: path to VCF file for genome transformation + +- name: "Output" + arguments: + - name: "--index" + type: file + direction: output + description: STAR index directory. + default: STAR_index + required: true + +resources: + - type: bash_script + path: script.sh + +test_resources: + - type: bash_script + path: test.sh + +engines: +- type: docker + image: ubuntu:22.04 + setup: + # setup derived from https://github.com/alexdobin/STAR/blob/master/extras/docker/Dockerfile + - type: docker + env: + - STAR_VERSION 2.7.11b + - PACKAGES gcc g++ make wget zlib1g-dev unzip xxd + run: | + apt-get update && \ + apt-get install -y --no-install-recommends ${PACKAGES} && \ + cd /tmp && \ + wget --no-check-certificate https://github.com/alexdobin/STAR/archive/refs/tags/${STAR_VERSION}.zip && \ + unzip ${STAR_VERSION}.zip && \ + cd STAR-${STAR_VERSION}/source && \ + make STARstatic CXXFLAGS_SIMD=-std=c++11 && \ + cp STAR /usr/local/bin && \ + cd / && \ + rm -rf /tmp/STAR-${STAR_VERSION} /tmp/${STAR_VERSION}.zip && \ + apt-get --purge autoremove -y ${PACKAGES} && \ + apt-get clean + - type: docker + run: | + STAR --version | sed 's#\(.*\)#star: "\1"#' > /var/software_versions.txt + +runners: + - type: executable + - type: nextflow diff --git a/src/star/star_genome_generate/help.txt b/src/star/star_genome_generate/help.txt new file mode 100644 index 00000000..940f639d --- /dev/null +++ b/src/star/star_genome_generate/help.txt @@ -0,0 +1,927 @@ +Usage: STAR [options]... --genomeDir /path/to/genome/index/ --readFilesIn R1.fq R2.fq +Spliced Transcripts Alignment to a Reference (c) Alexander Dobin, 2009-2022 + +STAR version=2.7.11b +STAR compilation time,server,dir=2024-02-11T19:36:26+00:00 :/tmp/STAR-2.7.11b/source +For more details see: + + +### versions +versionGenome 2.7.4a + string: earliest genome index version compatible with this STAR release. Please do not change this value! + +### Parameter Files +parametersFiles - + string: name of a user-defined parameters file, "-": none. Can only be defined on the command line. + +### System +sysShell - + string: path to the shell binary, preferably bash, e.g. /bin/bash. + - ... the default shell is executed, typically /bin/sh. This was reported to fail on some Ubuntu systems - then you need to specify path to bash. + +### Run Parameters +runMode alignReads + string: type of the run. + alignReads ... map reads + genomeGenerate ... generate genome files + inputAlignmentsFromBAM ... input alignments from BAM. Presently only works with --outWigType and --bamRemoveDuplicates options. + liftOver ... lift-over of GTF files (--sjdbGTFfile) between genome assemblies using chain file(s) from --genomeChainFiles. + soloCellFiltering ... STARsolo cell filtering ("calling") without remapping, followed by the path to raw count directory and output (filtered) prefix + +runThreadN 1 + int: number of threads to run STAR + +runDirPerm User_RWX + string: permissions for the directories created at the run-time. + User_RWX ... user-read/write/execute + All_RWX ... all-read/write/execute (same as chmod 777) + +runRNGseed 777 + int: random number generator seed. + + +### Genome Parameters +genomeDir ./GenomeDir/ + string: path to the directory where genome files are stored (for --runMode alignReads) or will be generated (for --runMode generateGenome) + +genomeLoad NoSharedMemory + string: mode of shared memory usage for the genome files. Only used with --runMode alignReads. + LoadAndKeep ... load genome into shared and keep it in memory after run + LoadAndRemove ... load genome into shared but remove it after run + LoadAndExit ... load genome into shared memory and exit, keeping the genome in memory for future runs + Remove ... do not map anything, just remove loaded genome from memory + NoSharedMemory ... do not use shared memory, each job will have its own private copy of the genome + +genomeFastaFiles - + string(s): path(s) to the fasta files with the genome sequences, separated by spaces. These files should be plain text FASTA files, they *cannot* be zipped. + Required for the genome generation (--runMode genomeGenerate). Can also be used in the mapping (--runMode alignReads) to add extra (new) sequences to the genome (e.g. spike-ins). + +genomeChainFiles - + string: chain files for genomic liftover. Only used with --runMode liftOver . + +genomeFileSizes 0 + uint(s)>0: genome files exact sizes in bytes. Typically, this should not be defined by the user. + +genomeTransformOutput None + string(s): which output to transform back to original genome + SAM ... SAM/BAM alignments + SJ ... splice junctions (SJ.out.tab) + Quant ... quantifications (from --quantMode option) + None ... no transformation of the output + +genomeChrSetMitochondrial chrM M MT + string(s): names of the mitochondrial chromosomes. Presently only used for STARsolo statistics output/ + +### Genome Indexing Parameters - only used with --runMode genomeGenerate +genomeChrBinNbits 18 + int: =log2(chrBin), where chrBin is the size of the bins for genome storage: each chromosome will occupy an integer number of bins. For a genome with large number of contigs, it is recommended to scale this parameter as min(18, log2[max(GenomeLength/NumberOfReferences,ReadLength)]). + +genomeSAindexNbases 14 + int: length (bases) of the SA pre-indexing string. Typically between 10 and 15. Longer strings will use much more memory, but allow faster searches. For small genomes, the parameter --genomeSAindexNbases must be scaled down to min(14, log2(GenomeLength)/2 - 1). + +genomeSAsparseD 1 + int>0: suffux array sparsity, i.e. distance between indices: use bigger numbers to decrease needed RAM at the cost of mapping speed reduction + +genomeSuffixLengthMax -1 + int: maximum length of the suffixes, has to be longer than read length. -1 = infinite. + +genomeTransformType None + string: type of genome transformation + None ... no transformation + Haploid ... replace reference alleles with alternative alleles from VCF file (e.g. consensus allele) + Diploid ... create two haplotypes for each chromosome listed in VCF file, for genotypes 1|2, assumes perfect phasing (e.g. personal genome) + +genomeTransformVCF - + string: path to VCF file for genome transformation + + + +#####UnderDevelopment_begin : not supported - do not use +genomeType Full + string: type of genome to generate + Full ... full (normal) genome + Transcriptome ... genome consists of transcript sequences + SuperTransriptome ... genome consists of superTranscript sequences +#####UnderDevelopment_end + +# DEPRECATED: please use --genomeTransformVCF and --genomeTransformType options instead. +#genomeConsensusFile - +# string: VCF file with consensus SNPs (i.e. alternative allele is the major (AF>0.5) allele) +# DEPRECATED + + + +### Splice Junctions Database +sjdbFileChrStartEnd - + string(s): path to the files with genomic coordinates (chr start end strand) for the splice junction introns. Multiple files can be supplied and will be concatenated. + +sjdbGTFfile - + string: path to the GTF file with annotations + +sjdbGTFchrPrefix - + string: prefix for chromosome names in a GTF file (e.g. 'chr' for using ENSMEBL annotations with UCSC genomes) + +sjdbGTFfeatureExon exon + string: feature type in GTF file to be used as exons for building transcripts + +sjdbGTFtagExonParentTranscript transcript_id + string: GTF attribute name for parent transcript ID (default "transcript_id" works for GTF files) + +sjdbGTFtagExonParentGene gene_id + string: GTF attribute name for parent gene ID (default "gene_id" works for GTF files) + +sjdbGTFtagExonParentGeneName gene_name + string(s): GTF attribute name for parent gene name + +sjdbGTFtagExonParentGeneType gene_type gene_biotype + string(s): GTF attribute name for parent gene type + +sjdbOverhang 100 + int>0: length of the donor/acceptor sequence on each side of the junctions, ideally = (mate_length - 1) + +sjdbScore 2 + int: extra alignment score for alignments that cross database junctions + +sjdbInsertSave Basic + string: which files to save when sjdb junctions are inserted on the fly at the mapping step + Basic ... only small junction / transcript files + All ... all files including big Genome, SA and SAindex - this will create a complete genome directory + +### Variation parameters +varVCFfile - + string: path to the VCF file that contains variation data. The 10th column should contain the genotype information, e.g. 0/1 + +### Input Files +inputBAMfile - + string: path to BAM input file, to be used with --runMode inputAlignmentsFromBAM + +### Read Parameters +readFilesType Fastx + string: format of input read files + Fastx ... FASTA or FASTQ + SAM SE ... SAM or BAM single-end reads; for BAM use --readFilesCommand samtools view + SAM PE ... SAM or BAM paired-end reads; for BAM use --readFilesCommand samtools view + +readFilesSAMattrKeep All + string(s): for --readFilesType SAM SE/PE, which SAM tags to keep in the output BAM, e.g.: --readFilesSAMtagsKeep RG PL + All ... keep all tags + None ... do not keep any tags + +readFilesIn Read1 Read2 + string(s): paths to files that contain input read1 (and, if needed, read2) + +readFilesManifest - + string: path to the "manifest" file with the names of read files. The manifest file should contain 3 tab-separated columns: + paired-end reads: read1_file_name $tab$ read2_file_name $tab$ read_group_line. + single-end reads: read1_file_name $tab$ - $tab$ read_group_line. + Spaces, but not tabs are allowed in file names. + If read_group_line does not start with ID:, it can only contain one ID field, and ID: will be added to it. + If read_group_line starts with ID:, it can contain several fields separated by $tab$, and all fields will be be copied verbatim into SAM @RG header line. + +readFilesPrefix - + string: prefix for the read files names, i.e. it will be added in front of the strings in --readFilesIn + +readFilesCommand - + string(s): command line to execute for each of the input file. This command should generate FASTA or FASTQ text and send it to stdout + For example: zcat - to uncompress .gz files, bzcat - to uncompress .bz2 files, etc. + +readMapNumber -1 + int: number of reads to map from the beginning of the file + -1: map all reads + +readMatesLengthsIn NotEqual + string: Equal/NotEqual - lengths of names,sequences,qualities for both mates are the same / not the same. NotEqual is safe in all situations. + +readNameSeparator / + string(s): character(s) separating the part of the read names that will be trimmed in output (read name after space is always trimmed) + +readQualityScoreBase 33 + int>=0: number to be subtracted from the ASCII code to get Phred quality score + +### Read Clipping + +clipAdapterType Hamming + string: adapter clipping type + Hamming ... adapter clipping based on Hamming distance, with the number of mismatches controlled by --clip5pAdapterMMp + CellRanger4 ... 5p and 3p adapter clipping similar to CellRanger4. Utilizes Opal package by Martin Šošić: https://github.com/Martinsos/opal + None ... no adapter clipping, all other clip* parameters are disregarded + +clip3pNbases 0 + int(s): number(s) of bases to clip from 3p of each mate. If one value is given, it will be assumed the same for both mates. + +clip3pAdapterSeq - + string(s): adapter sequences to clip from 3p of each mate. If one value is given, it will be assumed the same for both mates. + polyA ... polyA sequence with the length equal to read length + +clip3pAdapterMMp 0.1 + double(s): max proportion of mismatches for 3p adapter clipping for each mate. If one value is given, it will be assumed the same for both mates. + +clip3pAfterAdapterNbases 0 + int(s): number of bases to clip from 3p of each mate after the adapter clipping. If one value is given, it will be assumed the same for both mates. + +clip5pNbases 0 + int(s): number(s) of bases to clip from 5p of each mate. If one value is given, it will be assumed the same for both mates. + +#####UnderDevelopment_begin : not supported - do not use +clip5pAdapterSeq - + string(s): adapter sequences to clip from 5p of each mate, separated by space. + +clip5pAdapterMMp 0.1 + double(s): max proportion of mismatches for 5p adapter clipping for each mate, separated by space + +clip5pAfterAdapterNbases 0 + int(s): number of bases to clip from 5p of each mate after the adapter clipping, separated by space. +#####UnderDevelopment_end + +### Limits +limitGenomeGenerateRAM 31000000000 + int>0: maximum available RAM (bytes) for genome generation + +limitIObufferSize 30000000 50000000 + int(s)>0: max available buffers size (bytes) for input/output, per thread + +limitOutSAMoneReadBytes 100000 + int>0: max size of the SAM record (bytes) for one read. Recommended value: >(2*(LengthMate1+LengthMate2+100)*outFilterMultimapNmax + +limitOutSJoneRead 1000 + int>0: max number of junctions for one read (including all multi-mappers) + +limitOutSJcollapsed 1000000 + int>0: max number of collapsed junctions + +limitBAMsortRAM 0 + int>=0: maximum available RAM (bytes) for sorting BAM. If =0, it will be set to the genome index size. 0 value can only be used with --genomeLoad NoSharedMemory option. + +limitSjdbInsertNsj 1000000 + int>=0: maximum number of junctions to be inserted to the genome on the fly at the mapping stage, including those from annotations and those detected in the 1st step of the 2-pass run + +limitNreadsSoft -1 + int: soft limit on the number of reads + +### Output: general +outFileNamePrefix ./ + string: output files name prefix (including full or relative path). Can only be defined on the command line. + +outTmpDir - + string: path to a directory that will be used as temporary by STAR. All contents of this directory will be removed! + - ... the temp directory will default to outFileNamePrefix_STARtmp + +outTmpKeep None + string: whether to keep the temporary files after STAR runs is finished + None ... remove all temporary files + All ... keep all files + +outStd Log + string: which output will be directed to stdout (standard out) + Log ... log messages + SAM ... alignments in SAM format (which normally are output to Aligned.out.sam file), normal standard output will go into Log.std.out + BAM_Unsorted ... alignments in BAM format, unsorted. Requires --outSAMtype BAM Unsorted + BAM_SortedByCoordinate ... alignments in BAM format, sorted by coordinate. Requires --outSAMtype BAM SortedByCoordinate + BAM_Quant ... alignments to transcriptome in BAM format, unsorted. Requires --quantMode TranscriptomeSAM + +outReadsUnmapped None + string: output of unmapped and partially mapped (i.e. mapped only one mate of a paired end read) reads in separate file(s). + None ... no output + Fastx ... output in separate fasta/fastq files, Unmapped.out.mate1/2 + +outQSconversionAdd 0 + int: add this number to the quality score (e.g. to convert from Illumina to Sanger, use -31) + +outMultimapperOrder Old_2.4 + string: order of multimapping alignments in the output files + Old_2.4 ... quasi-random order used before 2.5.0 + Random ... random order of alignments for each multi-mapper. Read mates (pairs) are always adjacent, all alignment for each read stay together. This option will become default in the future releases. + +### Output: SAM and BAM +outSAMtype SAM + strings: type of SAM/BAM output + 1st word: + BAM ... output BAM without sorting + SAM ... output SAM without sorting + None ... no SAM/BAM output + 2nd, 3rd: + Unsorted ... standard unsorted + SortedByCoordinate ... sorted by coordinate. This option will allocate extra memory for sorting which can be specified by --limitBAMsortRAM. + +outSAMmode Full + string: mode of SAM output + None ... no SAM output + Full ... full SAM output + NoQS ... full SAM but without quality scores + +outSAMstrandField None + string: Cufflinks-like strand field flag + None ... not used + intronMotif ... strand derived from the intron motif. This option changes the output alignments: reads with inconsistent and/or non-canonical introns are filtered out. + +outSAMattributes Standard + string(s): a string of desired SAM attributes, in the order desired for the output SAM. Tags can be listed in any combination/order. + ***Presets: + None ... no attributes + Standard ... NH HI AS nM + All ... NH HI AS nM NM MD jM jI MC ch + ***Alignment: + NH ... number of loci the reads maps to: =1 for unique mappers, >1 for multimappers. Standard SAM tag. + HI ... multiple alignment index, starts with --outSAMattrIHstart (=1 by default). Standard SAM tag. + AS ... local alignment score, +1/-1 for matches/mismateches, score* penalties for indels and gaps. For PE reads, total score for two mates. Stadnard SAM tag. + nM ... number of mismatches. For PE reads, sum over two mates. + NM ... edit distance to the reference (number of mismatched + inserted + deleted bases) for each mate. Standard SAM tag. + MD ... string encoding mismatched and deleted reference bases (see standard SAM specifications). Standard SAM tag. + jM ... intron motifs for all junctions (i.e. N in CIGAR): 0: non-canonical; 1: GT/AG, 2: CT/AC, 3: GC/AG, 4: CT/GC, 5: AT/AC, 6: GT/AT. If splice junctions database is used, and a junction is annotated, 20 is added to its motif value. + jI ... start and end of introns for all junctions (1-based). + XS ... alignment strand according to --outSAMstrandField. + MC ... mate's CIGAR string. Standard SAM tag. + ch ... marks all segment of all chimeric alingments for --chimOutType WithinBAM output. + cN ... number of bases clipped from the read ends: 5' and 3' + ***Variation: + vA ... variant allele + vG ... genomic coordinate of the variant overlapped by the read. + vW ... 1 - alignment passes WASP filtering; 2,3,4,5,6,7 - alignment does not pass WASP filtering. Requires --waspOutputMode SAMtag. + ha ... haplotype (1/2) when mapping to the diploid genome. Requires genome generated with --genomeTransformType Diploid . + ***STARsolo: + CR CY UR UY ... sequences and quality scores of cell barcodes and UMIs for the solo* demultiplexing. + GX GN ... gene ID and gene name for unique-gene reads. + gx gn ... gene IDs and gene names for unique- and multi-gene reads. + CB UB ... error-corrected cell barcodes and UMIs for solo* demultiplexing. Requires --outSAMtype BAM SortedByCoordinate. + sM ... assessment of CB and UMI. + sS ... sequence of the entire barcode (CB,UMI,adapter). + sQ ... quality of the entire barcode. + sF ... type of feature overlap and number of features for each alignment + ***Unsupported/undocumented: + rB ... alignment block read/genomic coordinates. + vR ... read coordinate of the variant. + +outSAMattrIHstart 1 + int>=0: start value for the IH attribute. 0 may be required by some downstream software, such as Cufflinks or StringTie. + +outSAMunmapped None + string(s): output of unmapped reads in the SAM format + 1st word: + None ... no output + Within ... output unmapped reads within the main SAM file (i.e. Aligned.out.sam) + 2nd word: + KeepPairs ... record unmapped mate for each alignment, and, in case of unsorted output, keep it adjacent to its mapped mate. Only affects multi-mapping reads. + +outSAMorder Paired + string: type of sorting for the SAM output + Paired: one mate after the other for all paired alignments + PairedKeepInputOrder: one mate after the other for all paired alignments, the order is kept the same as in the input FASTQ files + +outSAMprimaryFlag OneBestScore + string: which alignments are considered primary - all others will be marked with 0x100 bit in the FLAG + OneBestScore ... only one alignment with the best score is primary + AllBestScore ... all alignments with the best score are primary + +outSAMreadID Standard + string: read ID record type + Standard ... first word (until space) from the FASTx read ID line, removing /1,/2 from the end + Number ... read number (index) in the FASTx file + +outSAMmapqUnique 255 + int: 0 to 255: the MAPQ value for unique mappers + +outSAMflagOR 0 + int: 0 to 65535: sam FLAG will be bitwise OR'd with this value, i.e. FLAG=FLAG | outSAMflagOR. This is applied after all flags have been set by STAR, and after outSAMflagAND. Can be used to set specific bits that are not set otherwise. + +outSAMflagAND 65535 + int: 0 to 65535: sam FLAG will be bitwise AND'd with this value, i.e. FLAG=FLAG & outSAMflagOR. This is applied after all flags have been set by STAR, but before outSAMflagOR. Can be used to unset specific bits that are not set otherwise. + +outSAMattrRGline - + string(s): SAM/BAM read group line. The first word contains the read group identifier and must start with "ID:", e.g. --outSAMattrRGline ID:xxx CN:yy "DS:z z z". + xxx will be added as RG tag to each output alignment. Any spaces in the tag values have to be double quoted. + Comma separated RG lines correspons to different (comma separated) input files in --readFilesIn. Commas have to be surrounded by spaces, e.g. + --outSAMattrRGline ID:xxx , ID:zzz "DS:z z" , ID:yyy DS:yyyy + +outSAMheaderHD - + strings: @HD (header) line of the SAM header + +outSAMheaderPG - + strings: extra @PG (software) line of the SAM header (in addition to STAR) + +outSAMheaderCommentFile - + string: path to the file with @CO (comment) lines of the SAM header + +outSAMfilter None + string(s): filter the output into main SAM/BAM files + KeepOnlyAddedReferences ... only keep the reads for which all alignments are to the extra reference sequences added with --genomeFastaFiles at the mapping stage. + KeepAllAddedReferences ... keep all alignments to the extra reference sequences added with --genomeFastaFiles at the mapping stage. + + +outSAMmultNmax -1 + int: max number of multiple alignments for a read that will be output to the SAM/BAM files. Note that if this value is not equal to -1, the top scoring alignment will be output first + -1 ... all alignments (up to --outFilterMultimapNmax) will be output + +outSAMtlen 1 + int: calculation method for the TLEN field in the SAM/BAM files + 1 ... leftmost base of the (+)strand mate to rightmost base of the (-)mate. (+)sign for the (+)strand mate + 2 ... leftmost base of any mate to rightmost base of any mate. (+)sign for the mate with the leftmost base. This is different from 1 for overlapping mates with protruding ends + +outBAMcompression 1 + int: -1 to 10 BAM compression level, -1=default compression (6?), 0=no compression, 10=maximum compression + +outBAMsortingThreadN 0 + int: >=0: number of threads for BAM sorting. 0 will default to min(6,--runThreadN). + +outBAMsortingBinsN 50 + int: >0: number of genome bins for coordinate-sorting + +### BAM processing +bamRemoveDuplicatesType - + string: mark duplicates in the BAM file, for now only works with (i) sorted BAM fed with inputBAMfile, and (ii) for paired-end alignments only + - ... no duplicate removal/marking + UniqueIdentical ... mark all multimappers, and duplicate unique mappers. The coordinates, FLAG, CIGAR must be identical + UniqueIdenticalNotMulti ... mark duplicate unique mappers but not multimappers. + +bamRemoveDuplicatesMate2basesN 0 + int>0: number of bases from the 5' of mate 2 to use in collapsing (e.g. for RAMPAGE) + +### Output Wiggle +outWigType None + string(s): type of signal output, e.g. "bedGraph" OR "bedGraph read1_5p". Requires sorted BAM: --outSAMtype BAM SortedByCoordinate . + 1st word: + None ... no signal output + bedGraph ... bedGraph format + wiggle ... wiggle format + 2nd word: + read1_5p ... signal from only 5' of the 1st read, useful for CAGE/RAMPAGE etc + read2 ... signal from only 2nd read + +outWigStrand Stranded + string: strandedness of wiggle/bedGraph output + Stranded ... separate strands, str1 and str2 + Unstranded ... collapsed strands + +outWigReferencesPrefix - + string: prefix matching reference names to include in the output wiggle file, e.g. "chr", default "-" - include all references + +outWigNorm RPM + string: type of normalization for the signal + RPM ... reads per million of mapped reads + None ... no normalization, "raw" counts + +### Output Filtering +outFilterType Normal + string: type of filtering + Normal ... standard filtering using only current alignment + BySJout ... keep only those reads that contain junctions that passed filtering into SJ.out.tab + +outFilterMultimapScoreRange 1 + int: the score range below the maximum score for multimapping alignments + +outFilterMultimapNmax 10 + int: maximum number of loci the read is allowed to map to. Alignments (all of them) will be output only if the read maps to no more loci than this value. + Otherwise no alignments will be output, and the read will be counted as "mapped to too many loci" in the Log.final.out . + +outFilterMismatchNmax 10 + int: alignment will be output only if it has no more mismatches than this value. + +outFilterMismatchNoverLmax 0.3 + real: alignment will be output only if its ratio of mismatches to *mapped* length is less than or equal to this value. + +outFilterMismatchNoverReadLmax 1.0 + real: alignment will be output only if its ratio of mismatches to *read* length is less than or equal to this value. + + +outFilterScoreMin 0 + int: alignment will be output only if its score is higher than or equal to this value. + +outFilterScoreMinOverLread 0.66 + real: same as outFilterScoreMin, but normalized to read length (sum of mates' lengths for paired-end reads) + +outFilterMatchNmin 0 + int: alignment will be output only if the number of matched bases is higher than or equal to this value. + +outFilterMatchNminOverLread 0.66 + real: sam as outFilterMatchNmin, but normalized to the read length (sum of mates' lengths for paired-end reads). + +outFilterIntronMotifs None + string: filter alignment using their motifs + None ... no filtering + RemoveNoncanonical ... filter out alignments that contain non-canonical junctions + RemoveNoncanonicalUnannotated ... filter out alignments that contain non-canonical unannotated junctions when using annotated splice junctions database. The annotated non-canonical junctions will be kept. + +outFilterIntronStrands RemoveInconsistentStrands + string: filter alignments + RemoveInconsistentStrands ... remove alignments that have junctions with inconsistent strands + None ... no filtering + +### Output splice junctions (SJ.out.tab) +outSJtype Standard + string: type of splice junction output + Standard ... standard SJ.out.tab output + None ... no splice junction output + +### Output Filtering: Splice Junctions +outSJfilterReads All + string: which reads to consider for collapsed splice junctions output + All ... all reads, unique- and multi-mappers + Unique ... uniquely mapping reads only + +outSJfilterOverhangMin 30 12 12 12 + 4 integers: minimum overhang length for splice junctions on both sides for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif + does not apply to annotated junctions + +outSJfilterCountUniqueMin 3 1 1 1 + 4 integers: minimum uniquely mapping read count per junction for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif + Junctions are output if one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin conditions are satisfied + does not apply to annotated junctions + +outSJfilterCountTotalMin 3 1 1 1 + 4 integers: minimum total (multi-mapping+unique) read count per junction for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif + Junctions are output if one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin conditions are satisfied + does not apply to annotated junctions + +outSJfilterDistToOtherSJmin 10 0 5 10 + 4 integers>=0: minimum allowed distance to other junctions' donor/acceptor + does not apply to annotated junctions + +outSJfilterIntronMaxVsReadN 50000 100000 200000 + N integers>=0: maximum gap allowed for junctions supported by 1,2,3,,,N reads + i.e. by default junctions supported by 1 read can have gaps <=50000b, by 2 reads: <=100000b, by 3 reads: <=200000. by >=4 reads any gap <=alignIntronMax + does not apply to annotated junctions + +### Scoring +scoreGap 0 + int: splice junction penalty (independent on intron motif) + +scoreGapNoncan -8 + int: non-canonical junction penalty (in addition to scoreGap) + +scoreGapGCAG -4 + int: GC/AG and CT/GC junction penalty (in addition to scoreGap) + +scoreGapATAC -8 + int: AT/AC and GT/AT junction penalty (in addition to scoreGap) + +scoreGenomicLengthLog2scale -0.25 + int: extra score logarithmically scaled with genomic length of the alignment: scoreGenomicLengthLog2scale*log2(genomicLength) + +scoreDelOpen -2 + int: deletion open penalty + +scoreDelBase -2 + int: deletion extension penalty per base (in addition to scoreDelOpen) + +scoreInsOpen -2 + int: insertion open penalty + +scoreInsBase -2 + int: insertion extension penalty per base (in addition to scoreInsOpen) + +scoreStitchSJshift 1 + int: maximum score reduction while searching for SJ boundaries in the stitching step + + +### Alignments and Seeding + +seedSearchStartLmax 50 + int>0: defines the search start point through the read - the read is split into pieces no longer than this value + +seedSearchStartLmaxOverLread 1.0 + real: seedSearchStartLmax normalized to read length (sum of mates' lengths for paired-end reads) + +seedSearchLmax 0 + int>=0: defines the maximum length of the seeds, if =0 seed length is not limited + +seedMultimapNmax 10000 + int>0: only pieces that map fewer than this value are utilized in the stitching procedure + +seedPerReadNmax 1000 + int>0: max number of seeds per read + +seedPerWindowNmax 50 + int>0: max number of seeds per window + +seedNoneLociPerWindow 10 + int>0: max number of one seed loci per window + +seedSplitMin 12 + int>0: min length of the seed sequences split by Ns or mate gap + +seedMapMin 5 + int>0: min length of seeds to be mapped + +alignIntronMin 21 + int: minimum intron size, genomic gap is considered intron if its length>=alignIntronMin, otherwise it is considered Deletion + +alignIntronMax 0 + int: maximum intron size, if 0, max intron size will be determined by (2^winBinNbits)*winAnchorDistNbins + +alignMatesGapMax 0 + int: maximum gap between two mates, if 0, max intron gap will be determined by (2^winBinNbits)*winAnchorDistNbins + +alignSJoverhangMin 5 + int>0: minimum overhang (i.e. block size) for spliced alignments + +alignSJstitchMismatchNmax 0 -1 0 0 + 4*int>=0: maximum number of mismatches for stitching of the splice junctions (-1: no limit). + (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. + +alignSJDBoverhangMin 3 + int>0: minimum overhang (i.e. block size) for annotated (sjdb) spliced alignments + +alignSplicedMateMapLmin 0 + int>0: minimum mapped length for a read mate that is spliced + +alignSplicedMateMapLminOverLmate 0.66 + real>0: alignSplicedMateMapLmin normalized to mate length + +alignWindowsPerReadNmax 10000 + int>0: max number of windows per read + +alignTranscriptsPerWindowNmax 100 + int>0: max number of transcripts per window + +alignTranscriptsPerReadNmax 10000 + int>0: max number of different alignments per read to consider + +alignEndsType Local + string: type of read ends alignment + Local ... standard local alignment with soft-clipping allowed + EndToEnd ... force end-to-end read alignment, do not soft-clip + Extend5pOfRead1 ... fully extend only the 5p of the read1, all other ends: local alignment + Extend5pOfReads12 ... fully extend only the 5p of the both read1 and read2, all other ends: local alignment + +alignEndsProtrude 0 ConcordantPair + int, string: allow protrusion of alignment ends, i.e. start (end) of the +strand mate downstream of the start (end) of the -strand mate + 1st word: int: maximum number of protrusion bases allowed + 2nd word: string: + ConcordantPair ... report alignments with non-zero protrusion as concordant pairs + DiscordantPair ... report alignments with non-zero protrusion as discordant pairs + +alignSoftClipAtReferenceEnds Yes + string: allow the soft-clipping of the alignments past the end of the chromosomes + Yes ... allow + No ... prohibit, useful for compatibility with Cufflinks + +alignInsertionFlush None + string: how to flush ambiguous insertion positions + None ... insertions are not flushed + Right ... insertions are flushed to the right + +### Paired-End reads +peOverlapNbasesMin 0 + int>=0: minimum number of overlapping bases to trigger mates merging and realignment. Specify >0 value to switch on the "merginf of overlapping mates" algorithm. + +peOverlapMMp 0.01 + real, >=0 & <1: maximum proportion of mismatched bases in the overlap area + +### Windows, Anchors, Binning + +winAnchorMultimapNmax 50 + int>0: max number of loci anchors are allowed to map to + +winBinNbits 16 + int>0: =log2(winBin), where winBin is the size of the bin for the windows/clustering, each window will occupy an integer number of bins. + +winAnchorDistNbins 9 + int>0: max number of bins between two anchors that allows aggregation of anchors into one window + +winFlankNbins 4 + int>0: log2(winFlank), where win Flank is the size of the left and right flanking regions for each window + +winReadCoverageRelativeMin 0.5 + real>=0: minimum relative coverage of the read sequence by the seeds in a window, for STARlong algorithm only. + +winReadCoverageBasesMin 0 + int>0: minimum number of bases covered by the seeds in a window , for STARlong algorithm only. + +### Chimeric Alignments +chimOutType Junctions + string(s): type of chimeric output + Junctions ... Chimeric.out.junction + SeparateSAMold ... output old SAM into separate Chimeric.out.sam file + WithinBAM ... output into main aligned BAM files (Aligned.*.bam) + WithinBAM HardClip ... (default) hard-clipping in the CIGAR for supplemental chimeric alignments (default if no 2nd word is present) + WithinBAM SoftClip ... soft-clipping in the CIGAR for supplemental chimeric alignments + +chimSegmentMin 0 + int>=0: minimum length of chimeric segment length, if ==0, no chimeric output + +chimScoreMin 0 + int>=0: minimum total (summed) score of the chimeric segments + +chimScoreDropMax 20 + int>=0: max drop (difference) of chimeric score (the sum of scores of all chimeric segments) from the read length + +chimScoreSeparation 10 + int>=0: minimum difference (separation) between the best chimeric score and the next one + +chimScoreJunctionNonGTAG -1 + int: penalty for a non-GT/AG chimeric junction + +chimJunctionOverhangMin 20 + int>=0: minimum overhang for a chimeric junction + +chimSegmentReadGapMax 0 + int>=0: maximum gap in the read sequence between chimeric segments + +chimFilter banGenomicN + string(s): different filters for chimeric alignments + None ... no filtering + banGenomicN ... Ns are not allowed in the genome sequence around the chimeric junction + +chimMainSegmentMultNmax 10 + int>=1: maximum number of multi-alignments for the main chimeric segment. =1 will prohibit multimapping main segments. + +chimMultimapNmax 0 + int>=0: maximum number of chimeric multi-alignments + 0 ... use the old scheme for chimeric detection which only considered unique alignments + +chimMultimapScoreRange 1 + int>=0: the score range for multi-mapping chimeras below the best chimeric score. Only works with --chimMultimapNmax > 1 + +chimNonchimScoreDropMin 20 + int>=0: to trigger chimeric detection, the drop in the best non-chimeric alignment score with respect to the read length has to be greater than this value + +chimOutJunctionFormat 0 + int: formatting type for the Chimeric.out.junction file + 0 ... no comment lines/headers + 1 ... comment lines at the end of the file: command line and Nreads: total, unique/multi-mapping + +### Quantification of Annotations +quantMode - + string(s): types of quantification requested + - ... none + TranscriptomeSAM ... output SAM/BAM alignments to transcriptome into a separate file + GeneCounts ... count reads per gene + +quantTranscriptomeBAMcompression 1 + int: -2 to 10 transcriptome BAM compression level + -2 ... no BAM output + -1 ... default compression (6?) + 0 ... no compression + 10 ... maximum compression + +quantTranscriptomeSAMoutput BanSingleEnd_BanIndels_ExtendSoftclip + string: alignment filtering for TranscriptomeSAM output + BanSingleEnd_BanIndels_ExtendSoftclip ... prohibit indels and single-end alignments, extend softclips - compatible with RSEM + BanSingleEnd ... prohibit single-end alignments, allow indels and softclips + BanSingleEnd_ExtendSoftclip ... prohibit single-end alignments, extend softclips, allow indels + + +### 2-pass Mapping +twopassMode None + string: 2-pass mapping mode. + None ... 1-pass mapping + Basic ... basic 2-pass mapping, with all 1st pass junctions inserted into the genome indices on the fly + +twopass1readsN -1 + int: number of reads to process for the 1st step. Use very large number (or default -1) to map all reads in the first step. + + +### WASP parameters +waspOutputMode None + string: WASP allele-specific output type. This is re-implementation of the original WASP mappability filtering by Bryce van de Geijn, Graham McVicker, Yoav Gilad & Jonathan K Pritchard. Please cite the original WASP paper: Nature Methods 12, 1061–1063 (2015), https://www.nature.com/articles/nmeth.3582 . + SAMtag ... add WASP tags to the alignments that pass WASP filtering + +### STARsolo (single cell RNA-seq) parameters +soloType None + string(s): type of single-cell RNA-seq + CB_UMI_Simple ... (a.k.a. Droplet) one UMI and one Cell Barcode of fixed length in read2, e.g. Drop-seq and 10X Chromium. + CB_UMI_Complex ... multiple Cell Barcodes of varying length, one UMI of fixed length and one adapter sequence of fixed length are allowed in read2 only (e.g. inDrop, ddSeq). + CB_samTagOut ... output Cell Barcode as CR and/or CB SAm tag. No UMI counting. --readFilesIn cDNA_read1 [cDNA_read2 if paired-end] CellBarcode_read . Requires --outSAMtype BAM Unsorted [and/or SortedByCoordinate] + SmartSeq ... Smart-seq: each cell in a separate FASTQ (paired- or single-end), barcodes are corresponding read-groups, no UMI sequences, alignments deduplicated according to alignment start and end (after extending soft-clipped bases) + +soloCBtype Sequence + string: cell barcode type + Sequence: cell barcode is a sequence (standard option) + String: cell barcode is an arbitrary string + +soloCBwhitelist - + string(s): file(s) with whitelist(s) of cell barcodes. Only --soloType CB_UMI_Complex allows more than one whitelist file. + None ... no whitelist: all cell barcodes are allowed + +soloCBstart 1 + int>0: cell barcode start base + +soloCBlen 16 + int>0: cell barcode length + +soloUMIstart 17 + int>0: UMI start base + +soloUMIlen 10 + int>0: UMI length + +soloBarcodeReadLength 1 + int: length of the barcode read + 1 ... equal to sum of soloCBlen+soloUMIlen + 0 ... not defined, do not check + +soloBarcodeMate 0 + int: identifies which read mate contains the barcode (CB+UMI) sequence + 0 ... barcode sequence is on separate read, which should always be the last file in the --readFilesIn listed + 1 ... barcode sequence is a part of mate 1 + 2 ... barcode sequence is a part of mate 2 + +soloCBposition - + strings(s): position of Cell Barcode(s) on the barcode read. + Presently only works with --soloType CB_UMI_Complex, and barcodes are assumed to be on Read2. + Format for each barcode: startAnchor_startPosition_endAnchor_endPosition + start(end)Anchor defines the Anchor Base for the CB: 0: read start; 1: read end; 2: adapter start; 3: adapter end + start(end)Position is the 0-based position with of the CB start(end) with respect to the Anchor Base + String for different barcodes are separated by space. + Example: inDrop (Zilionis et al, Nat. Protocols, 2017): + --soloCBposition 0_0_2_-1 3_1_3_8 + +soloUMIposition - + string: position of the UMI on the barcode read, same as soloCBposition + Example: inDrop (Zilionis et al, Nat. Protocols, 2017): + --soloCBposition 3_9_3_14 + +soloAdapterSequence - + string: adapter sequence to anchor barcodes. Only one adapter sequence is allowed. + +soloAdapterMismatchesNmax 1 + int>0: maximum number of mismatches allowed in adapter sequence. + +soloCBmatchWLtype 1MM_multi + string: matching the Cell Barcodes to the WhiteList + Exact ... only exact matches allowed + 1MM ... only one match in whitelist with 1 mismatched base allowed. Allowed CBs have to have at least one read with exact match. + 1MM_multi ... multiple matches in whitelist with 1 mismatched base allowed, posterior probability calculation is used choose one of the matches. + Allowed CBs have to have at least one read with exact match. This option matches best with CellRanger 2.2.0 + 1MM_multi_pseudocounts ... same as 1MM_Multi, but pseudocounts of 1 are added to all whitelist barcodes. + 1MM_multi_Nbase_pseudocounts ... same as 1MM_multi_pseudocounts, multimatching to WL is allowed for CBs with N-bases. This option matches best with CellRanger >= 3.0.0 + EditDist_2 ... allow up to edit distance of 3 fpr each of the barcodes. May include one deletion + one insertion. Only works with --soloType CB_UMI_Complex. Matches to multiple passlist barcdoes are not allowed. Similar to ParseBio Split-seq pipeline. + +soloInputSAMattrBarcodeSeq - + string(s): when inputting reads from a SAM file (--readsFileType SAM SE/PE), these SAM attributes mark the barcode sequence (in proper order). + For instance, for 10X CellRanger or STARsolo BAMs, use --soloInputSAMattrBarcodeSeq CR UR . + This parameter is required when running STARsolo with input from SAM. + +soloInputSAMattrBarcodeQual - + string(s): when inputting reads from a SAM file (--readsFileType SAM SE/PE), these SAM attributes mark the barcode qualities (in proper order). + For instance, for 10X CellRanger or STARsolo BAMs, use --soloInputSAMattrBarcodeQual CY UY . + If this parameter is '-' (default), the quality 'H' will be assigned to all bases. + +soloStrand Forward + string: strandedness of the solo libraries: + Unstranded ... no strand information + Forward ... read strand same as the original RNA molecule + Reverse ... read strand opposite to the original RNA molecule + +soloFeatures Gene + string(s): genomic features for which the UMI counts per Cell Barcode are collected + Gene ... genes: reads match the gene transcript + SJ ... splice junctions: reported in SJ.out.tab + GeneFull ... full gene (pre-mRNA): count all reads overlapping genes' exons and introns + GeneFull_ExonOverIntron ... full gene (pre-mRNA): count all reads overlapping genes' exons and introns: prioritize 100% overlap with exons + GeneFull_Ex50pAS ... full gene (pre-RNA): count all reads overlapping genes' exons and introns: prioritize >50% overlap with exons. Do not count reads with 100% exonic overlap in the antisense direction. + +#####UnderDevelopment_begin : not supported - do not use + Transcript3p ... quantification of transcript for 3' protocols +#####UnderDevelopment_end + +soloMultiMappers Unique + string(s): counting method for reads mapping to multiple genes + Unique ... count only reads that map to unique genes + Uniform ... uniformly distribute multi-genic UMIs to all genes + Rescue ... distribute UMIs proportionally to unique+uniform counts (~ first iteration of EM) + PropUnique ... distribute UMIs proportionally to unique mappers, if present, and uniformly if not. + EM ... multi-gene UMIs are distributed using Expectation Maximization algorithm + +soloUMIdedup 1MM_All + string(s): type of UMI deduplication (collapsing) algorithm + 1MM_All ... all UMIs with 1 mismatch distance to each other are collapsed (i.e. counted once). + 1MM_Directional_UMItools ... follows the "directional" method from the UMI-tools by Smith, Heger and Sudbery (Genome Research 2017). + 1MM_Directional ... same as 1MM_Directional_UMItools, but with more stringent criteria for duplicate UMIs + Exact ... only exactly matching UMIs are collapsed. + NoDedup ... no deduplication of UMIs, count all reads. + 1MM_CR ... CellRanger2-4 algorithm for 1MM UMI collapsing. + +soloUMIfiltering - + string(s): type of UMI filtering (for reads uniquely mapping to genes) + - ... basic filtering: remove UMIs with N and homopolymers (similar to CellRanger 2.2.0). + MultiGeneUMI ... basic + remove lower-count UMIs that map to more than one gene. + MultiGeneUMI_All ... basic + remove all UMIs that map to more than one gene. + MultiGeneUMI_CR ... basic + remove lower-count UMIs that map to more than one gene, matching CellRanger > 3.0.0 . + Only works with --soloUMIdedup 1MM_CR + +soloOutFileNames Solo.out/ features.tsv barcodes.tsv matrix.mtx + string(s): file names for STARsolo output: + file_name_prefix gene_names barcode_sequences cell_feature_count_matrix + +soloCellFilter CellRanger2.2 3000 0.99 10 + string(s): cell filtering type and parameters + None ... do not output filtered cells + TopCells ... only report top cells by UMI count, followed by the exact number of cells + CellRanger2.2 ... simple filtering of CellRanger 2.2. + Can be followed by numbers: number of expected cells, robust maximum percentile for UMI count, maximum to minimum ratio for UMI count + The harcoded values are from CellRanger: nExpectedCells=3000; maxPercentile=0.99; maxMinRatio=10 + EmptyDrops_CR ... EmptyDrops filtering in CellRanger flavor. Please cite the original EmptyDrops paper: A.T.L Lun et al, Genome Biology, 20, 63 (2019): https://genomebiology.biomedcentral.com/articles/10.1186/s13059-019-1662-y + Can be followed by 10 numeric parameters: nExpectedCells maxPercentile maxMinRatio indMin indMax umiMin umiMinFracMedian candMaxN FDR simN + The harcoded values are from CellRanger: 3000 0.99 10 45000 90000 500 0.01 20000 0.01 10000 + +soloOutFormatFeaturesGeneField3 "Gene Expression" + string(s): field 3 in the Gene features.tsv file. If "-", then no 3rd field is output. + +soloCellReadStats None + string: Output reads statistics for each CB + Standard ... standard output + +#####UnderDevelopment_begin : not supported - do not use +soloClusterCBfile - + string: file containing the cluster information for cell barcodes, two columns: CB cluster_index. Only used with --soloFeatures Transcript3p +#####UnderDevelopment_end diff --git a/src/star/star_genome_generate/script.sh b/src/star/star_genome_generate/script.sh new file mode 100644 index 00000000..cb3b906c --- /dev/null +++ b/src/star/star_genome_generate/script.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -e + +## VIASH START +## VIASH END + +mkdir -p $par_index + +STAR \ + --runMode genomeGenerate \ + --genomeDir $par_index \ + --genomeFastaFiles $par_genomeFastaFiles \ + ${meta_cpus:+--runThreadN "${meta_cpus}"} \ + ${par_sjdbGTFfile:+--sjdbGTFfile "${par_sjdbGTFfile}"} \ + ${par_sjdbOverhang:+--sjdbOverhang "${par_sjdbOverhang}"} \ + ${par_genomeSAindexNbases:+--genomeSAindexNbases "${par_genomeSAindexNbases}"} \ + ${par_sjdbGTFchrPrefix:+--sjdbGTFchrPrefix "${par_sjdbGTFchrPrefix}"} \ + ${par_sjdbGTFfeatureExon:+--sjdbGTFfeatureExon "${par_sjdbGTFfeatureExon}"} \ + ${par_sjdbGTFtagExonParentTranscript:+--sjdbGTFtagExonParentTranscript "${par_sjdbGTFtagExonParentTranscript}"} \ + ${par_sjdbGTFtagExonParentGene:+--sjdbGTFtagExonParentGene "${par_sjdbGTFtagExonParentGene}"} \ + ${par_sjdbGTFtagExonParentGeneName:+--sjdbGTFtagExonParentGeneName "${par_sjdbGTFtagExonParentGeneName}"} \ + ${par_sjdbGTFtagExonParentGeneType:+--sjdbGTFtagExonParentGeneType "${sjdbGTFtagExonParentGeneType}"} \ + ${par_limitGenomeGenerateRAM:+--limitGenomeGenerateRAM "${par_limitGenomeGenerateRAM}"} \ + ${par_genomeChrBinNbits:+--genomeChrBinNbits "${par_genomeChrBinNbits}"} \ + ${par_genomeSAsparseD:+--genomeSAsparseD "${par_genomeSAsparseD}"} \ + ${par_genomeSuffixLengthMax:+--genomeSuffixLengthMax "${par_genomeSuffixLengthMax}"} \ + ${par_genomeTransformType:+--genomeTransformType "${par_genomeTransformType}"} \ + ${par_genomeTransformVCF:+--genomeTransformVCF "${par_genomeTransformVCF}"} \ diff --git a/src/star/star_genome_generate/test.sh b/src/star/star_genome_generate/test.sh new file mode 100644 index 00000000..fd0e4775 --- /dev/null +++ b/src/star/star_genome_generate/test.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +set -e + +## VIASH START +## VIASH END + +######################################################################################### + +echo "> Prepare test data" + +cat > genome.fasta <<'EOF' +>chr1 +TGGCATGAGCCAACGAACGCTGCCTCATAAGCCTCACACATCCGCGCCTATGTTGTGACTCTCTGTGAGCGTTCGTGGG +GCTCGTCACCACTATGGTTGGCCGGTTAGTAGTGTGACTCCTGGTTTTCTGGAGCTTCTTTAAACCGTAGTCCAGTCAA +TGCGAATGGCACTTCACGACGGACTGTCCTTAGCTCAGGGGA +EOF + +cat > genes.gtf <<'EOF' +chr1 example_source gene 0 50 . + . gene_id "gene1"; transcript_id "transcript1"; +chr1 example_source exon 20 40 . + . gene_id "gene1"; transcript_id "transcript1"; +EOF + +######################################################################################### + +echo "> Generate index" +"$meta_executable" \ + ${meta_cpus:+---cpus $meta_cpus} \ + --index "star_index/" \ + --genomeFastaFiles "genome.fasta" \ + --sjdbGTFfile "genes.gtf" \ + --genomeSAindexNbases 2 + +files=("Genome" "Log.out" "SA" "SAindex" "chrLength.txt" "chrName.txt" "chrNameLength.txt" "chrStart.txt" "exonGeTrInfo.tab" "exonInfo.tab" "geneInfo.tab" "genomeParameters.txt" "sjdbInfo.txt" "sjdbList.fromGTF.out.tab" "sjdbList.out.tab" "transcriptInfo.tab") + +echo ">> Check if output exists" +[ ! -d "star_index" ] && echo "Directory 'star_index' does not exist!" && exit 1 +for file in "${files[@]}"; do + [ ! -f "star_index/$file" ] && echo "File '$file' does not exist in 'star_index'." && exit 1 +done + +echo ">> Check contents of output files" +grep -q "200" "star_index/chrLength.txt" || (echo "Chromosome length in file 'chrLength.txt' is incorrect! " && exit 1) +grep -q "chr1" "star_index/chrName.txt" || (echo "Chromosome name in file 'chrName.txt' is incorrect! " && exit 1) +grep -q "chr1 200" "star_index/chrNameLength.txt" || (echo "Chromosome name in file 'chrNameLength.txt' is incorrect! " && exit 1) + +echo ">>> Test finished successfully" +exit 0 diff --git a/target/.build.yaml b/target/.build.yaml new file mode 100644 index 00000000..e69de29b diff --git a/target/executable/arriba/.config.vsh.yaml b/target/executable/arriba/.config.vsh.yaml new file mode 100644 index 00000000..e9b0f1ec --- /dev/null +++ b/target/executable/arriba/.config.vsh.yaml @@ -0,0 +1,714 @@ +name: "arriba" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--bam" + alternatives: + - "-x" + description: "File in SAM/BAM/CRAM format with main alignments as generated by\ + \ STAR\n(Aligned.out.sam). Arriba extracts candidate reads from this file.\n" + info: null + example: + - "Aligned.out.bam" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--genome" + alternatives: + - "-a" + description: "FastA file with genome sequence (assembly). The file may be gzip-compressed.\ + \ An \nindex with the file extension .fai must exist only if CRAM files are\ + \ processed.\n" + info: null + example: + - "assembly.fa" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--gene_annotation" + alternatives: + - "-g" + description: "GTF file with gene annotation. The file may be gzip-compressed.\n" + info: null + example: + - "annotation.gtf" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--known_fusions" + alternatives: + - "-k" + description: "File containing known/recurrent fusions. Some cancer entities are\ + \ often \ncharacterized by fusions between the same pair of genes. In order\ + \ to boost \nsensitivity, a list of known fusions can be supplied using this\ + \ parameter. The list \nmust contain two columns with the names of the fused\ + \ genes, separated by tabs.\n" + info: null + example: + - "known_fusions.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--blacklist" + alternatives: + - "-b" + description: "File containing blacklisted events (recurrent artifacts and transcripts\ + \ \nobserved in healthy tissue).\n" + info: null + example: + - "blacklist.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--structural_variants" + alternatives: + - "-d" + description: "Tab-separated file with coordinates of structural variants found\ + \ using \nwhole-genome sequencing data. These coordinates serve to increase\ + \ sensitivity \ntowards weakly expressed fusions and to eliminate fusions with\ + \ low evidence. \n" + info: null + example: + - "structural_variants_from_WGS.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--tags" + alternatives: + - "-t" + description: "Tab-separated file containing fusions to annotate with tags in the\ + \ 'tags' column. \nThe first two columns specify the genes; the third column\ + \ specifies the tag. The \nfile may be gzip-compressed. \n" + info: null + example: + - "tags.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--protein_domains" + alternatives: + - "-p" + description: "File in GFF3 format containing coordinates of the protein domains\ + \ of genes. The\nprotein domains retained in a fusion are listed in the column\n\ + 'retained_protein_domains'. The file may be gzip-compressed.\n" + info: null + example: + - "protein_domains.gff3" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--fusions" + alternatives: + - "-o" + description: "Output file with fusions that have passed all filters.\n" + info: null + example: + - "fusions.tsv" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--fusions_discarded" + alternatives: + - "-O" + description: "Output file with fusions that were discarded due to filtering. \n" + info: null + example: + - "fusions.discarded.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Arguments" + arguments: + - type: "long" + name: "--max_genomic_breakpoint_distance" + alternatives: + - "-D" + description: "When a file with genomic breakpoints obtained via \nwhole-genome\ + \ sequencing is supplied via the --structural_variants\nparameter, this parameter\ + \ determines how far a \ngenomic breakpoint may be away from a \ntranscriptomic\ + \ breakpoint to consider it as a \nrelated event. For events inside genes, the\ + \ \ndistance is added to the end of the gene; for \nintergenic events, the distance\ + \ threshold is \napplied as is. Default: 100000.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--strandedness" + alternatives: + - "-s" + description: "Whether a strand-specific protocol was used for library preparation,\ + \ \nand if so, the type of strandedness (auto/yes/no/reverse). When \nunstranded\ + \ data is processed, the strand can sometimes be inferred from \nsplice-patterns.\ + \ But in unclear situations, stranded data helps \nresolve ambiguities. Default:\ + \ auto\n" + info: null + required: false + choices: + - "auto" + - "yes" + - "no" + - "reverse" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--interesting_contigs" + alternatives: + - "-i" + description: "List of interesting contigs. Fusions between genes \non other contigs\ + \ are ignored. Contigs can be specified with or without the \nprefix \"chr\"\ + . Asterisks (*) are treated as wild-cards. \nDefault: 1 2 3 4 5 6 7 8 9 10 11\ + \ 12 13 14 15 16 17 18 19 20 21 22 X Y AC_* NC_*\n" + info: null + example: + - "1" + - "2" + - "AC_*" + - "NC_*" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--viral_contigs" + alternatives: + - "-v" + description: "List of viral contigs. Asterisks (*) are treated as \nwild-cards.\n\ + Default: AC_* NC_*\n" + info: null + example: + - "AC_*" + - "NC_*" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--disable_filters" + alternatives: + - "-f" + description: "List of filters to disable. By default all filters are \nenabled.\ + \ \n" + info: null + required: false + choices: + - "homologs" + - "low_entropy" + - "isoforms" + - "top_expressed_viral_contigs" + - "viral_contigs" + - "uninteresting_contigs" + - "non_coding_neighbors" + - "mismatches" + - "duplicates" + - "no_genomic_support" + - "genomic_support" + - "intronic" + - "end_to_end" + - "relative_support" + - "low_coverage_viral_contigs" + - "merge_adjacent" + - "mismappers" + - "multimappers" + - "same_gene" + - "long_gap" + - "internal_tandem_duplication" + - "small_insert_size" + - "read_through" + - "inconsistently_clipped" + - "intragenic_exonic" + - "marginal_read_through" + - "spliced" + - "hairpin" + - "blacklist" + - "min_support" + - "select_best" + - "in_vitro" + - "short_anchor" + - "known_fusions" + - "no_coverage" + - "homopolymer" + - "many_spliced" + direction: "input" + multiple: true + multiple_sep: ";" + - type: "double" + name: "--max_e_value" + alternatives: + - "-E" + description: "Arriba estimates the number of fusions with a given number of supporting\ + \ \nreads which one would expect to see by random chance. If the expected number\ + \ \nof fusions (e-value) is higher than this threshold, the fusion is \ndiscarded\ + \ by the 'relative_support' filter. Note: Increasing this \nthreshold can dramatically\ + \ increase the number of false positives and may \nincrease the runtime of resource-intensive\ + \ steps. Fractional values are \npossible. Default: 0.300000 \n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_supporting_reads" + alternatives: + - "-S" + description: "The 'min_support' filter discards all fusions with fewer than \n\ + this many supporting reads (split reads and discordant mates \ncombined). Default:\ + \ 2 \n" + info: null + example: + - 2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--max_mismappers" + alternatives: + - "-m" + description: "When more than this fraction of supporting reads turns out to be\ + \ \nmismappers, the 'mismappers' filter discards the fusion. Default: \n0.800000\n" + info: null + example: + - 0.8 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--max_homolog_identity" + alternatives: + - "-L" + description: "Genes with more than the given fraction of sequence identity are\ + \ \nconsidered homologs and removed by the 'homologs' filter. \nDefault: 0.300000\ + \ \n" + info: null + example: + - 0.3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--homopolymer_length" + alternatives: + - "-H" + description: "The 'homopolymer' filter removes breakpoints adjacent to \nhomopolymers\ + \ of the given length or more. Default: 6\n" + info: null + example: + - 6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--read_through_distance" + alternatives: + - "-R" + description: "The 'read_through' filter removes read-through fusions \nwhere the\ + \ breakpoints are less than the given distance away \nfrom each other. Default:\ + \ 10000 \n" + info: null + example: + - 10000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_anchor_length" + alternatives: + - "-A" + description: "Alignment artifacts are often characterized by split reads coming\ + \ \nfrom only one gene and no discordant mates. Moreover, the split \nreads\ + \ only align to a short stretch in one of the genes. The \n'short_anchor' filter\ + \ removes these fusions. This parameter sets \nthe threshold in bp for what\ + \ the filter considers short. Default: 23 \n" + info: null + example: + - 23 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--many_spliced_events" + alternatives: + - "-M" + description: "The 'many_spliced' filter recovers fusions between genes that \n\ + have at least this many spliced breakpoints. Default: 4\n" + info: null + example: + - 4 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--max_kmer_content" + alternatives: + - "-K" + description: "The 'low_entropy' filter removes reads with repetitive 3-mers. If\ + \ \nthe 3-mers make up more than the given fraction of the sequence, then \n\ + the read is discarded. Default: 0.600000 \n" + info: null + example: + - 0.6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--max_mismatch_pvalue" + alternatives: + - "-V" + description: "The 'mismatches' filter uses a binomial model to calculate a \n\ + p-value for observing a given number of mismatches in a read. If \nthe number\ + \ of mismatches is too high, the read is discarded. \nDefault: 0.010000 \n" + info: null + example: + - 0.05 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--fragment_length" + alternatives: + - "-F" + description: "When paired-end data is given, the fragment length is estimated\ + \ \nautomatically and this parameter has no effect. But when single-end \ndata\ + \ is given, the mean fragment length should be specified to \neffectively filter\ + \ fusions that arise from hairpin structures. \nDefault: 200 \n" + info: null + example: + - 200 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_reads" + alternatives: + - "-U" + description: "Subsample fusions with more than the given number of supporting\ + \ reads. This \nimproves performance without compromising sensitivity, as long\ + \ as the \nthreshold is high. Counting of supporting reads beyond the threshold\ + \ is \ninaccurate, obviously. Default: 300 \n" + info: null + example: + - 300 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--quantile" + alternatives: + - "-Q" + description: "Highly expressed genes are prone to produce artifacts during library\ + \ \npreparation. Genes with an expression above the given quantile are eligible\ + \ \nfor filtering by the 'in_vitro' filter. Default: 0.998000\n" + info: null + example: + - 0.998 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--exonic_fraction" + alternatives: + - "-e" + description: "The breakpoints of false-positive predictions of intragenic events\ + \ \nare often both in exons. True predictions are more likely to have at \n\ + least one breakpoint in an intron, because introns are larger. If the \nfraction\ + \ of exonic sequence between two breakpoints is smaller than \nthe given fraction,\ + \ the 'intragenic_exonic' filter discards the \nevent. Default: 0.330000 \n" + info: null + example: + - 0.33 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--top_n" + alternatives: + - "-T" + description: "Only report viral integration sites of the top N most highly expressed\ + \ viral \ncontigs. Default: 5\n" + info: null + example: + - 5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--covered_fraction" + alternatives: + - "-C" + description: "Ignore virally associated events if the virus is not fully \nexpressed,\ + \ i.e., less than the given fraction of the viral contig is \ntranscribed. Default:\ + \ 0.050000 \n" + info: null + example: + - 0.05 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_itd_length" + alternatives: + - "-l" + description: "Maximum length of internal tandem duplications. Note: Increasing\ + \ \nthis value beyond the default can impair performance and lead to many \n\ + false positives. Default: 100 \n" + info: null + example: + - 100 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--min_itd_allele_fraction" + alternatives: + - "-z" + description: "Required fraction of supporting reads to report an internal \ntandem\ + \ duplication. Default: 0.070000 \n" + info: null + example: + - 0.07 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_itd_supporting_reads" + alternatives: + - "-Z" + description: "Required absolute number of supporting reads to report an \ninternal\ + \ tandem duplication. Default: 10 \n" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--skip_duplicate_marking" + alternatives: + - "-u" + description: "Instead of performing duplicate marking itself, Arriba relies on\ + \ duplicate marking by a \npreceding program using the BAM_FDUP flag. This makes\ + \ sense when unique molecular \nidentifiers (UMI) are used.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--extra_information" + alternatives: + - "-X" + description: "To reduce the runtime and file size, by default, the columns 'fusion_transcript',\ + \ \n'peptide_sequence', and 'read_identifiers' are left empty in the file containing\ + \ \ndiscarded fusion candidates (see parameter -O). When this flag is set, this\ + \ extra \ninformation is reported in the discarded fusions file.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--fill_gaps" + alternatives: + - "-I" + description: "If assembly of the fusion transcript sequence from the supporting\ + \ reads is incomplete \n(denoted as '...'), fill the gaps using the assembly\ + \ sequence wherever possible. \n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Detect gene fusions from RNA-Seq data" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + cpus: 1 + commands: + - "ps" +keywords: +- "Gene fusion" +- "RNA-Seq" +license: "MIT" +references: + doi: + - "10.1101/gr.257246.119" +links: + repository: "https://github.com/suhrig/arriba" + homepage: "https://arriba.readthedocs.io/en/latest/" + documentation: "https://arriba.readthedocs.io/en/latest/" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/arriba:2.4.0--h0033a41_2" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "arriba -h | grep 'Version:' 2>&1 | sed 's/Version:\\s\\(.*\\)/arriba: \"\\\ + 1\"/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/arriba/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/arriba" + executable: "target/executable/arriba/arriba" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/arriba/arriba b/target/executable/arriba/arriba new file mode 100755 index 00000000..4c1e7521 --- /dev/null +++ b/target/executable/arriba/arriba @@ -0,0 +1,2306 @@ +#!/usr/bin/env bash + +# arriba main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="arriba" +VIASH_META_FUNCTIONALITY_NAME="arriba" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "arriba main" + echo "" + echo "Detect gene fusions from RNA-Seq data" + echo "" + echo "Inputs:" + echo " -x, --bam" + echo " type: file, required parameter, file must exist" + echo " example: Aligned.out.bam" + echo " File in SAM/BAM/CRAM format with main alignments as generated by STAR" + echo " (Aligned.out.sam). Arriba extracts candidate reads from this file." + echo "" + echo " -a, --genome" + echo " type: file, required parameter, file must exist" + echo " example: assembly.fa" + echo " FastA file with genome sequence (assembly). The file may be" + echo " gzip-compressed. An" + echo " index with the file extension .fai must exist only if CRAM files are" + echo " processed." + echo "" + echo " -g, --gene_annotation" + echo " type: file, required parameter, file must exist" + echo " example: annotation.gtf" + echo " GTF file with gene annotation. The file may be gzip-compressed." + echo "" + echo " -k, --known_fusions" + echo " type: file, file must exist" + echo " example: known_fusions.tsv" + echo " File containing known/recurrent fusions. Some cancer entities are often" + echo " characterized by fusions between the same pair of genes. In order to" + echo " boost" + echo " sensitivity, a list of known fusions can be supplied using this" + echo " parameter. The list" + echo " must contain two columns with the names of the fused genes, separated by" + echo " tabs." + echo "" + echo " -b, --blacklist" + echo " type: file, file must exist" + echo " example: blacklist.tsv" + echo " File containing blacklisted events (recurrent artifacts and transcripts" + echo " observed in healthy tissue)." + echo "" + echo " -d, --structural_variants" + echo " type: file, file must exist" + echo " example: structural_variants_from_WGS.tsv" + echo " Tab-separated file with coordinates of structural variants found using" + echo " whole-genome sequencing data. These coordinates serve to increase" + echo " sensitivity" + echo " towards weakly expressed fusions and to eliminate fusions with low" + echo " evidence." + echo "" + echo " -t, --tags" + echo " type: file, file must exist" + echo " example: tags.tsv" + echo " Tab-separated file containing fusions to annotate with tags in the" + echo " 'tags' column." + echo " The first two columns specify the genes; the third column specifies the" + echo " tag. The" + echo " file may be gzip-compressed." + echo "" + echo " -p, --protein_domains" + echo " type: file, file must exist" + echo " example: protein_domains.gff3" + echo " File in GFF3 format containing coordinates of the protein domains of" + echo " genes. The" + echo " protein domains retained in a fusion are listed in the column" + echo " 'retained_protein_domains'. The file may be gzip-compressed." + echo "" + echo "Outputs:" + echo " -o, --fusions" + echo " type: file, required parameter, output, file must exist" + echo " example: fusions.tsv" + echo " Output file with fusions that have passed all filters." + echo "" + echo " -O, --fusions_discarded" + echo " type: file, output, file must exist" + echo " example: fusions.discarded.tsv" + echo " Output file with fusions that were discarded due to filtering." + echo "" + echo "Arguments:" + echo " -D, --max_genomic_breakpoint_distance" + echo " type: long" + echo " When a file with genomic breakpoints obtained via" + echo " whole-genome sequencing is supplied via the --structural_variants" + echo " parameter, this parameter determines how far a" + echo " genomic breakpoint may be away from a" + echo " transcriptomic breakpoint to consider it as a" + echo " related event. For events inside genes, the" + echo " distance is added to the end of the gene; for" + echo " intergenic events, the distance threshold is" + echo " applied as is. Default: 100000." + echo "" + echo " -s, --strandedness" + echo " type: string" + echo " choices: [ auto, yes, no, reverse ]" + echo " Whether a strand-specific protocol was used for library preparation," + echo " and if so, the type of strandedness (auto/yes/no/reverse). When" + echo " unstranded data is processed, the strand can sometimes be inferred from" + echo " splice-patterns. But in unclear situations, stranded data helps" + echo " resolve ambiguities. Default: auto" + echo "" + echo " -i, --interesting_contigs" + echo " type: string, multiple values allowed" + echo " example: 1;2;AC_*;NC_*" + echo " List of interesting contigs. Fusions between genes" + echo " on other contigs are ignored. Contigs can be specified with or without" + echo " the" + echo " prefix \"chr\". Asterisks (*) are treated as wild-cards." + echo " Default: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 X Y" + echo " AC_* NC_*" + echo "" + echo " -v, --viral_contigs" + echo " type: string, multiple values allowed" + echo " example: AC_*;NC_*" + echo " List of viral contigs. Asterisks (*) are treated as" + echo " wild-cards." + echo " Default: AC_* NC_*" + echo "" + echo " -f, --disable_filters" + echo " type: string, multiple values allowed" + echo " choices: [ homologs, low_entropy, isoforms, top_expressed_viral_contigs," + echo "viral_contigs, uninteresting_contigs, non_coding_neighbors, mismatches," + echo "duplicates, no_genomic_support, genomic_support, intronic, end_to_end," + echo "relative_support, low_coverage_viral_contigs, merge_adjacent, mismappers," + echo "multimappers, same_gene, long_gap, internal_tandem_duplication," + echo "small_insert_size, read_through, inconsistently_clipped, intragenic_exonic," + echo "marginal_read_through, spliced, hairpin, blacklist, min_support, select_best," + echo "in_vitro, short_anchor, known_fusions, no_coverage, homopolymer, many_spliced ]" + echo " List of filters to disable. By default all filters are" + echo " enabled." + echo "" + echo " -E, --max_e_value" + echo " type: double" + echo " Arriba estimates the number of fusions with a given number of supporting" + echo " reads which one would expect to see by random chance. If the expected" + echo " number" + echo " of fusions (e-value) is higher than this threshold, the fusion is" + echo " discarded by the 'relative_support' filter. Note: Increasing this" + echo " threshold can dramatically increase the number of false positives and" + echo " may" + echo " increase the runtime of resource-intensive steps. Fractional values are" + echo " possible. Default: 0.300000" + echo "" + echo " -S, --min_supporting_reads" + echo " type: integer" + echo " example: 2" + echo " The 'min_support' filter discards all fusions with fewer than" + echo " this many supporting reads (split reads and discordant mates" + echo " combined). Default: 2" + echo "" + echo " -m, --max_mismappers" + echo " type: double" + echo " example: 0.8" + echo " When more than this fraction of supporting reads turns out to be" + echo " mismappers, the 'mismappers' filter discards the fusion. Default:" + echo " 0.800000" + echo "" + echo " -L, --max_homolog_identity" + echo " type: double" + echo " example: 0.3" + echo " Genes with more than the given fraction of sequence identity are" + echo " considered homologs and removed by the 'homologs' filter." + echo " Default: 0.300000" + echo "" + echo " -H, --homopolymer_length" + echo " type: integer" + echo " example: 6" + echo " The 'homopolymer' filter removes breakpoints adjacent to" + echo " homopolymers of the given length or more. Default: 6" + echo "" + echo " -R, --read_through_distance" + echo " type: integer" + echo " example: 10000" + echo " The 'read_through' filter removes read-through fusions" + echo " where the breakpoints are less than the given distance away" + echo " from each other. Default: 10000" + echo "" + echo " -A, --min_anchor_length" + echo " type: integer" + echo " example: 23" + echo " Alignment artifacts are often characterized by split reads coming" + echo " from only one gene and no discordant mates. Moreover, the split" + echo " reads only align to a short stretch in one of the genes. The" + echo " 'short_anchor' filter removes these fusions. This parameter sets" + echo " the threshold in bp for what the filter considers short. Default: 23" + echo "" + echo " -M, --many_spliced_events" + echo " type: integer" + echo " example: 4" + echo " The 'many_spliced' filter recovers fusions between genes that" + echo " have at least this many spliced breakpoints. Default: 4" + echo "" + echo " -K, --max_kmer_content" + echo " type: double" + echo " example: 0.6" + echo " The 'low_entropy' filter removes reads with repetitive 3-mers. If" + echo " the 3-mers make up more than the given fraction of the sequence, then" + echo " the read is discarded. Default: 0.600000" + echo "" + echo " -V, --max_mismatch_pvalue" + echo " type: double" + echo " example: 0.05" + echo " The 'mismatches' filter uses a binomial model to calculate a" + echo " p-value for observing a given number of mismatches in a read. If" + echo " the number of mismatches is too high, the read is discarded." + echo " Default: 0.010000" + echo "" + echo " -F, --fragment_length" + echo " type: integer" + echo " example: 200" + echo " When paired-end data is given, the fragment length is estimated" + echo " automatically and this parameter has no effect. But when single-end" + echo " data is given, the mean fragment length should be specified to" + echo " effectively filter fusions that arise from hairpin structures." + echo " Default: 200" + echo "" + echo " -U, --max_reads" + echo " type: integer" + echo " example: 300" + echo " Subsample fusions with more than the given number of supporting reads." + echo " This" + echo " improves performance without compromising sensitivity, as long as the" + echo " threshold is high. Counting of supporting reads beyond the threshold is" + echo " inaccurate, obviously. Default: 300" + echo "" + echo " -Q, --quantile" + echo " type: double" + echo " example: 0.998" + echo " Highly expressed genes are prone to produce artifacts during library" + echo " preparation. Genes with an expression above the given quantile are" + echo " eligible" + echo " for filtering by the 'in_vitro' filter. Default: 0.998000" + echo "" + echo " -e, --exonic_fraction" + echo " type: double" + echo " example: 0.33" + echo " The breakpoints of false-positive predictions of intragenic events" + echo " are often both in exons. True predictions are more likely to have at" + echo " least one breakpoint in an intron, because introns are larger. If the" + echo " fraction of exonic sequence between two breakpoints is smaller than" + echo " the given fraction, the 'intragenic_exonic' filter discards the" + echo " event. Default: 0.330000" + echo "" + echo " -T, --top_n" + echo " type: integer" + echo " example: 5" + echo " Only report viral integration sites of the top N most highly expressed" + echo " viral" + echo " contigs. Default: 5" + echo "" + echo " -C, --covered_fraction" + echo " type: double" + echo " example: 0.05" + echo " Ignore virally associated events if the virus is not fully" + echo " expressed, i.e., less than the given fraction of the viral contig is" + echo " transcribed. Default: 0.050000" + echo "" + echo " -l, --max_itd_length" + echo " type: integer" + echo " example: 100" + echo " Maximum length of internal tandem duplications. Note: Increasing" + echo " this value beyond the default can impair performance and lead to many" + echo " false positives. Default: 100" + echo "" + echo " -z, --min_itd_allele_fraction" + echo " type: double" + echo " example: 0.07" + echo " Required fraction of supporting reads to report an internal" + echo " tandem duplication. Default: 0.070000" + echo "" + echo " -Z, --min_itd_supporting_reads" + echo " type: integer" + echo " example: 10" + echo " Required absolute number of supporting reads to report an" + echo " internal tandem duplication. Default: 10" + echo "" + echo " -u, --skip_duplicate_marking" + echo " type: boolean_true" + echo " Instead of performing duplicate marking itself, Arriba relies on" + echo " duplicate marking by a" + echo " preceding program using the BAM_FDUP flag. This makes sense when unique" + echo " molecular" + echo " identifiers (UMI) are used." + echo "" + echo " -X, --extra_information" + echo " type: boolean_true" + echo " To reduce the runtime and file size, by default, the columns" + echo " 'fusion_transcript'," + echo " 'peptide_sequence', and 'read_identifiers' are left empty in the file" + echo " containing" + echo " discarded fusion candidates (see parameter -O). When this flag is set," + echo " this extra" + echo " information is reported in the discarded fusions file." + echo "" + echo " -I, --fill_gaps" + echo " type: boolean_true" + echo " If assembly of the fusion transcript sequence from the supporting reads" + echo " is incomplete" + echo " (denoted as '...'), fill the gaps using the assembly sequence wherever" + echo " possible." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/arriba:2.4.0--h0033a41_2 +ENTRYPOINT [] +RUN arriba -h | grep 'Version:' 2>&1 | sed 's/Version:\s\(.*\)/arriba: "\1"/' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component arriba" +LABEL org.opencontainers.image.created="2024-06-24T08:36:43Z" +LABEL org.opencontainers.image.source="https://github.com/suhrig/arriba" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "arriba main" + exit + ;; + --bam) + [ -n "$VIASH_PAR_BAM" ] && ViashError Bad arguments for option \'--bam\': \'$VIASH_PAR_BAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bam. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bam=*) + [ -n "$VIASH_PAR_BAM" ] && ViashError Bad arguments for option \'--bam=*\': \'$VIASH_PAR_BAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAM=$(ViashRemoveFlags "$1") + shift 1 + ;; + -x) + [ -n "$VIASH_PAR_BAM" ] && ViashError Bad arguments for option \'-x\': \'$VIASH_PAR_BAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -x. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genome) + [ -n "$VIASH_PAR_GENOME" ] && ViashError Bad arguments for option \'--genome\': \'$VIASH_PAR_GENOME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genome. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genome=*) + [ -n "$VIASH_PAR_GENOME" ] && ViashError Bad arguments for option \'--genome=*\': \'$VIASH_PAR_GENOME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOME=$(ViashRemoveFlags "$1") + shift 1 + ;; + -a) + [ -n "$VIASH_PAR_GENOME" ] && ViashError Bad arguments for option \'-a\': \'$VIASH_PAR_GENOME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -a. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --gene_annotation) + [ -n "$VIASH_PAR_GENE_ANNOTATION" ] && ViashError Bad arguments for option \'--gene_annotation\': \'$VIASH_PAR_GENE_ANNOTATION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENE_ANNOTATION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --gene_annotation. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --gene_annotation=*) + [ -n "$VIASH_PAR_GENE_ANNOTATION" ] && ViashError Bad arguments for option \'--gene_annotation=*\': \'$VIASH_PAR_GENE_ANNOTATION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENE_ANNOTATION=$(ViashRemoveFlags "$1") + shift 1 + ;; + -g) + [ -n "$VIASH_PAR_GENE_ANNOTATION" ] && ViashError Bad arguments for option \'-g\': \'$VIASH_PAR_GENE_ANNOTATION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENE_ANNOTATION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -g. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --known_fusions) + [ -n "$VIASH_PAR_KNOWN_FUSIONS" ] && ViashError Bad arguments for option \'--known_fusions\': \'$VIASH_PAR_KNOWN_FUSIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KNOWN_FUSIONS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --known_fusions. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --known_fusions=*) + [ -n "$VIASH_PAR_KNOWN_FUSIONS" ] && ViashError Bad arguments for option \'--known_fusions=*\': \'$VIASH_PAR_KNOWN_FUSIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KNOWN_FUSIONS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -k) + [ -n "$VIASH_PAR_KNOWN_FUSIONS" ] && ViashError Bad arguments for option \'-k\': \'$VIASH_PAR_KNOWN_FUSIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KNOWN_FUSIONS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -k. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --blacklist) + [ -n "$VIASH_PAR_BLACKLIST" ] && ViashError Bad arguments for option \'--blacklist\': \'$VIASH_PAR_BLACKLIST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BLACKLIST="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --blacklist. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --blacklist=*) + [ -n "$VIASH_PAR_BLACKLIST" ] && ViashError Bad arguments for option \'--blacklist=*\': \'$VIASH_PAR_BLACKLIST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BLACKLIST=$(ViashRemoveFlags "$1") + shift 1 + ;; + -b) + [ -n "$VIASH_PAR_BLACKLIST" ] && ViashError Bad arguments for option \'-b\': \'$VIASH_PAR_BLACKLIST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BLACKLIST="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -b. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --structural_variants) + [ -n "$VIASH_PAR_STRUCTURAL_VARIANTS" ] && ViashError Bad arguments for option \'--structural_variants\': \'$VIASH_PAR_STRUCTURAL_VARIANTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRUCTURAL_VARIANTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --structural_variants. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --structural_variants=*) + [ -n "$VIASH_PAR_STRUCTURAL_VARIANTS" ] && ViashError Bad arguments for option \'--structural_variants=*\': \'$VIASH_PAR_STRUCTURAL_VARIANTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRUCTURAL_VARIANTS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -d) + [ -n "$VIASH_PAR_STRUCTURAL_VARIANTS" ] && ViashError Bad arguments for option \'-d\': \'$VIASH_PAR_STRUCTURAL_VARIANTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRUCTURAL_VARIANTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -d. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tags) + [ -n "$VIASH_PAR_TAGS" ] && ViashError Bad arguments for option \'--tags\': \'$VIASH_PAR_TAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --tags. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tags=*) + [ -n "$VIASH_PAR_TAGS" ] && ViashError Bad arguments for option \'--tags=*\': \'$VIASH_PAR_TAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -t) + [ -n "$VIASH_PAR_TAGS" ] && ViashError Bad arguments for option \'-t\': \'$VIASH_PAR_TAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -t. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --protein_domains) + [ -n "$VIASH_PAR_PROTEIN_DOMAINS" ] && ViashError Bad arguments for option \'--protein_domains\': \'$VIASH_PAR_PROTEIN_DOMAINS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PROTEIN_DOMAINS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --protein_domains. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --protein_domains=*) + [ -n "$VIASH_PAR_PROTEIN_DOMAINS" ] && ViashError Bad arguments for option \'--protein_domains=*\': \'$VIASH_PAR_PROTEIN_DOMAINS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PROTEIN_DOMAINS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -p) + [ -n "$VIASH_PAR_PROTEIN_DOMAINS" ] && ViashError Bad arguments for option \'-p\': \'$VIASH_PAR_PROTEIN_DOMAINS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PROTEIN_DOMAINS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -p. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fusions) + [ -n "$VIASH_PAR_FUSIONS" ] && ViashError Bad arguments for option \'--fusions\': \'$VIASH_PAR_FUSIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FUSIONS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --fusions. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fusions=*) + [ -n "$VIASH_PAR_FUSIONS" ] && ViashError Bad arguments for option \'--fusions=*\': \'$VIASH_PAR_FUSIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FUSIONS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_FUSIONS" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_FUSIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FUSIONS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fusions_discarded) + [ -n "$VIASH_PAR_FUSIONS_DISCARDED" ] && ViashError Bad arguments for option \'--fusions_discarded\': \'$VIASH_PAR_FUSIONS_DISCARDED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FUSIONS_DISCARDED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --fusions_discarded. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fusions_discarded=*) + [ -n "$VIASH_PAR_FUSIONS_DISCARDED" ] && ViashError Bad arguments for option \'--fusions_discarded=*\': \'$VIASH_PAR_FUSIONS_DISCARDED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FUSIONS_DISCARDED=$(ViashRemoveFlags "$1") + shift 1 + ;; + -O) + [ -n "$VIASH_PAR_FUSIONS_DISCARDED" ] && ViashError Bad arguments for option \'-O\': \'$VIASH_PAR_FUSIONS_DISCARDED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FUSIONS_DISCARDED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -O. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_genomic_breakpoint_distance) + [ -n "$VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE" ] && ViashError Bad arguments for option \'--max_genomic_breakpoint_distance\': \'$VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_genomic_breakpoint_distance. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_genomic_breakpoint_distance=*) + [ -n "$VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE" ] && ViashError Bad arguments for option \'--max_genomic_breakpoint_distance=*\': \'$VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -D) + [ -n "$VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE" ] && ViashError Bad arguments for option \'-D\': \'$VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -D. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --strandedness) + [ -n "$VIASH_PAR_STRANDEDNESS" ] && ViashError Bad arguments for option \'--strandedness\': \'$VIASH_PAR_STRANDEDNESS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRANDEDNESS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --strandedness. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --strandedness=*) + [ -n "$VIASH_PAR_STRANDEDNESS" ] && ViashError Bad arguments for option \'--strandedness=*\': \'$VIASH_PAR_STRANDEDNESS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRANDEDNESS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -s) + [ -n "$VIASH_PAR_STRANDEDNESS" ] && ViashError Bad arguments for option \'-s\': \'$VIASH_PAR_STRANDEDNESS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRANDEDNESS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -s. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --interesting_contigs) + if [ -z "$VIASH_PAR_INTERESTING_CONTIGS" ]; then + VIASH_PAR_INTERESTING_CONTIGS="$2" + else + VIASH_PAR_INTERESTING_CONTIGS="$VIASH_PAR_INTERESTING_CONTIGS;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --interesting_contigs. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --interesting_contigs=*) + if [ -z "$VIASH_PAR_INTERESTING_CONTIGS" ]; then + VIASH_PAR_INTERESTING_CONTIGS=$(ViashRemoveFlags "$1") + else + VIASH_PAR_INTERESTING_CONTIGS="$VIASH_PAR_INTERESTING_CONTIGS;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -i) + if [ -z "$VIASH_PAR_INTERESTING_CONTIGS" ]; then + VIASH_PAR_INTERESTING_CONTIGS="$2" + else + VIASH_PAR_INTERESTING_CONTIGS="$VIASH_PAR_INTERESTING_CONTIGS;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -i. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --viral_contigs) + if [ -z "$VIASH_PAR_VIRAL_CONTIGS" ]; then + VIASH_PAR_VIRAL_CONTIGS="$2" + else + VIASH_PAR_VIRAL_CONTIGS="$VIASH_PAR_VIRAL_CONTIGS;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --viral_contigs. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --viral_contigs=*) + if [ -z "$VIASH_PAR_VIRAL_CONTIGS" ]; then + VIASH_PAR_VIRAL_CONTIGS=$(ViashRemoveFlags "$1") + else + VIASH_PAR_VIRAL_CONTIGS="$VIASH_PAR_VIRAL_CONTIGS;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -v) + if [ -z "$VIASH_PAR_VIRAL_CONTIGS" ]; then + VIASH_PAR_VIRAL_CONTIGS="$2" + else + VIASH_PAR_VIRAL_CONTIGS="$VIASH_PAR_VIRAL_CONTIGS;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -v. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --disable_filters) + if [ -z "$VIASH_PAR_DISABLE_FILTERS" ]; then + VIASH_PAR_DISABLE_FILTERS="$2" + else + VIASH_PAR_DISABLE_FILTERS="$VIASH_PAR_DISABLE_FILTERS;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --disable_filters. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --disable_filters=*) + if [ -z "$VIASH_PAR_DISABLE_FILTERS" ]; then + VIASH_PAR_DISABLE_FILTERS=$(ViashRemoveFlags "$1") + else + VIASH_PAR_DISABLE_FILTERS="$VIASH_PAR_DISABLE_FILTERS;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -f) + if [ -z "$VIASH_PAR_DISABLE_FILTERS" ]; then + VIASH_PAR_DISABLE_FILTERS="$2" + else + VIASH_PAR_DISABLE_FILTERS="$VIASH_PAR_DISABLE_FILTERS;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -f. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_e_value) + [ -n "$VIASH_PAR_MAX_E_VALUE" ] && ViashError Bad arguments for option \'--max_e_value\': \'$VIASH_PAR_MAX_E_VALUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_E_VALUE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_e_value. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_e_value=*) + [ -n "$VIASH_PAR_MAX_E_VALUE" ] && ViashError Bad arguments for option \'--max_e_value=*\': \'$VIASH_PAR_MAX_E_VALUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_E_VALUE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -E) + [ -n "$VIASH_PAR_MAX_E_VALUE" ] && ViashError Bad arguments for option \'-E\': \'$VIASH_PAR_MAX_E_VALUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_E_VALUE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -E. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_supporting_reads) + [ -n "$VIASH_PAR_MIN_SUPPORTING_READS" ] && ViashError Bad arguments for option \'--min_supporting_reads\': \'$VIASH_PAR_MIN_SUPPORTING_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_SUPPORTING_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_supporting_reads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_supporting_reads=*) + [ -n "$VIASH_PAR_MIN_SUPPORTING_READS" ] && ViashError Bad arguments for option \'--min_supporting_reads=*\': \'$VIASH_PAR_MIN_SUPPORTING_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_SUPPORTING_READS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -S) + [ -n "$VIASH_PAR_MIN_SUPPORTING_READS" ] && ViashError Bad arguments for option \'-S\': \'$VIASH_PAR_MIN_SUPPORTING_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_SUPPORTING_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -S. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_mismappers) + [ -n "$VIASH_PAR_MAX_MISMAPPERS" ] && ViashError Bad arguments for option \'--max_mismappers\': \'$VIASH_PAR_MAX_MISMAPPERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_MISMAPPERS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_mismappers. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_mismappers=*) + [ -n "$VIASH_PAR_MAX_MISMAPPERS" ] && ViashError Bad arguments for option \'--max_mismappers=*\': \'$VIASH_PAR_MAX_MISMAPPERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_MISMAPPERS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -m) + [ -n "$VIASH_PAR_MAX_MISMAPPERS" ] && ViashError Bad arguments for option \'-m\': \'$VIASH_PAR_MAX_MISMAPPERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_MISMAPPERS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -m. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_homolog_identity) + [ -n "$VIASH_PAR_MAX_HOMOLOG_IDENTITY" ] && ViashError Bad arguments for option \'--max_homolog_identity\': \'$VIASH_PAR_MAX_HOMOLOG_IDENTITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_HOMOLOG_IDENTITY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_homolog_identity. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_homolog_identity=*) + [ -n "$VIASH_PAR_MAX_HOMOLOG_IDENTITY" ] && ViashError Bad arguments for option \'--max_homolog_identity=*\': \'$VIASH_PAR_MAX_HOMOLOG_IDENTITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_HOMOLOG_IDENTITY=$(ViashRemoveFlags "$1") + shift 1 + ;; + -L) + [ -n "$VIASH_PAR_MAX_HOMOLOG_IDENTITY" ] && ViashError Bad arguments for option \'-L\': \'$VIASH_PAR_MAX_HOMOLOG_IDENTITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_HOMOLOG_IDENTITY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -L. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --homopolymer_length) + [ -n "$VIASH_PAR_HOMOPOLYMER_LENGTH" ] && ViashError Bad arguments for option \'--homopolymer_length\': \'$VIASH_PAR_HOMOPOLYMER_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HOMOPOLYMER_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --homopolymer_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --homopolymer_length=*) + [ -n "$VIASH_PAR_HOMOPOLYMER_LENGTH" ] && ViashError Bad arguments for option \'--homopolymer_length=*\': \'$VIASH_PAR_HOMOPOLYMER_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HOMOPOLYMER_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -H) + [ -n "$VIASH_PAR_HOMOPOLYMER_LENGTH" ] && ViashError Bad arguments for option \'-H\': \'$VIASH_PAR_HOMOPOLYMER_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HOMOPOLYMER_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -H. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read_through_distance) + [ -n "$VIASH_PAR_READ_THROUGH_DISTANCE" ] && ViashError Bad arguments for option \'--read_through_distance\': \'$VIASH_PAR_READ_THROUGH_DISTANCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_THROUGH_DISTANCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --read_through_distance. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read_through_distance=*) + [ -n "$VIASH_PAR_READ_THROUGH_DISTANCE" ] && ViashError Bad arguments for option \'--read_through_distance=*\': \'$VIASH_PAR_READ_THROUGH_DISTANCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_THROUGH_DISTANCE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -R) + [ -n "$VIASH_PAR_READ_THROUGH_DISTANCE" ] && ViashError Bad arguments for option \'-R\': \'$VIASH_PAR_READ_THROUGH_DISTANCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_THROUGH_DISTANCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -R. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_anchor_length) + [ -n "$VIASH_PAR_MIN_ANCHOR_LENGTH" ] && ViashError Bad arguments for option \'--min_anchor_length\': \'$VIASH_PAR_MIN_ANCHOR_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ANCHOR_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_anchor_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_anchor_length=*) + [ -n "$VIASH_PAR_MIN_ANCHOR_LENGTH" ] && ViashError Bad arguments for option \'--min_anchor_length=*\': \'$VIASH_PAR_MIN_ANCHOR_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ANCHOR_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -A) + [ -n "$VIASH_PAR_MIN_ANCHOR_LENGTH" ] && ViashError Bad arguments for option \'-A\': \'$VIASH_PAR_MIN_ANCHOR_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ANCHOR_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -A. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --many_spliced_events) + [ -n "$VIASH_PAR_MANY_SPLICED_EVENTS" ] && ViashError Bad arguments for option \'--many_spliced_events\': \'$VIASH_PAR_MANY_SPLICED_EVENTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MANY_SPLICED_EVENTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --many_spliced_events. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --many_spliced_events=*) + [ -n "$VIASH_PAR_MANY_SPLICED_EVENTS" ] && ViashError Bad arguments for option \'--many_spliced_events=*\': \'$VIASH_PAR_MANY_SPLICED_EVENTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MANY_SPLICED_EVENTS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -M) + [ -n "$VIASH_PAR_MANY_SPLICED_EVENTS" ] && ViashError Bad arguments for option \'-M\': \'$VIASH_PAR_MANY_SPLICED_EVENTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MANY_SPLICED_EVENTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -M. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_kmer_content) + [ -n "$VIASH_PAR_MAX_KMER_CONTENT" ] && ViashError Bad arguments for option \'--max_kmer_content\': \'$VIASH_PAR_MAX_KMER_CONTENT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_KMER_CONTENT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_kmer_content. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_kmer_content=*) + [ -n "$VIASH_PAR_MAX_KMER_CONTENT" ] && ViashError Bad arguments for option \'--max_kmer_content=*\': \'$VIASH_PAR_MAX_KMER_CONTENT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_KMER_CONTENT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -K) + [ -n "$VIASH_PAR_MAX_KMER_CONTENT" ] && ViashError Bad arguments for option \'-K\': \'$VIASH_PAR_MAX_KMER_CONTENT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_KMER_CONTENT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -K. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_mismatch_pvalue) + [ -n "$VIASH_PAR_MAX_MISMATCH_PVALUE" ] && ViashError Bad arguments for option \'--max_mismatch_pvalue\': \'$VIASH_PAR_MAX_MISMATCH_PVALUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_MISMATCH_PVALUE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_mismatch_pvalue. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_mismatch_pvalue=*) + [ -n "$VIASH_PAR_MAX_MISMATCH_PVALUE" ] && ViashError Bad arguments for option \'--max_mismatch_pvalue=*\': \'$VIASH_PAR_MAX_MISMATCH_PVALUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_MISMATCH_PVALUE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -V) + [ -n "$VIASH_PAR_MAX_MISMATCH_PVALUE" ] && ViashError Bad arguments for option \'-V\': \'$VIASH_PAR_MAX_MISMATCH_PVALUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_MISMATCH_PVALUE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -V. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fragment_length) + [ -n "$VIASH_PAR_FRAGMENT_LENGTH" ] && ViashError Bad arguments for option \'--fragment_length\': \'$VIASH_PAR_FRAGMENT_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRAGMENT_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --fragment_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fragment_length=*) + [ -n "$VIASH_PAR_FRAGMENT_LENGTH" ] && ViashError Bad arguments for option \'--fragment_length=*\': \'$VIASH_PAR_FRAGMENT_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRAGMENT_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -F) + [ -n "$VIASH_PAR_FRAGMENT_LENGTH" ] && ViashError Bad arguments for option \'-F\': \'$VIASH_PAR_FRAGMENT_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRAGMENT_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -F. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_reads) + [ -n "$VIASH_PAR_MAX_READS" ] && ViashError Bad arguments for option \'--max_reads\': \'$VIASH_PAR_MAX_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_reads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_reads=*) + [ -n "$VIASH_PAR_MAX_READS" ] && ViashError Bad arguments for option \'--max_reads=*\': \'$VIASH_PAR_MAX_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_READS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -U) + [ -n "$VIASH_PAR_MAX_READS" ] && ViashError Bad arguments for option \'-U\': \'$VIASH_PAR_MAX_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -U. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quantile) + [ -n "$VIASH_PAR_QUANTILE" ] && ViashError Bad arguments for option \'--quantile\': \'$VIASH_PAR_QUANTILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUANTILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --quantile. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quantile=*) + [ -n "$VIASH_PAR_QUANTILE" ] && ViashError Bad arguments for option \'--quantile=*\': \'$VIASH_PAR_QUANTILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUANTILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -Q) + [ -n "$VIASH_PAR_QUANTILE" ] && ViashError Bad arguments for option \'-Q\': \'$VIASH_PAR_QUANTILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUANTILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -Q. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --exonic_fraction) + [ -n "$VIASH_PAR_EXONIC_FRACTION" ] && ViashError Bad arguments for option \'--exonic_fraction\': \'$VIASH_PAR_EXONIC_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXONIC_FRACTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --exonic_fraction. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --exonic_fraction=*) + [ -n "$VIASH_PAR_EXONIC_FRACTION" ] && ViashError Bad arguments for option \'--exonic_fraction=*\': \'$VIASH_PAR_EXONIC_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXONIC_FRACTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + -e) + [ -n "$VIASH_PAR_EXONIC_FRACTION" ] && ViashError Bad arguments for option \'-e\': \'$VIASH_PAR_EXONIC_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXONIC_FRACTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -e. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --top_n) + [ -n "$VIASH_PAR_TOP_N" ] && ViashError Bad arguments for option \'--top_n\': \'$VIASH_PAR_TOP_N\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TOP_N="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --top_n. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --top_n=*) + [ -n "$VIASH_PAR_TOP_N" ] && ViashError Bad arguments for option \'--top_n=*\': \'$VIASH_PAR_TOP_N\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TOP_N=$(ViashRemoveFlags "$1") + shift 1 + ;; + -T) + [ -n "$VIASH_PAR_TOP_N" ] && ViashError Bad arguments for option \'-T\': \'$VIASH_PAR_TOP_N\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TOP_N="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -T. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --covered_fraction) + [ -n "$VIASH_PAR_COVERED_FRACTION" ] && ViashError Bad arguments for option \'--covered_fraction\': \'$VIASH_PAR_COVERED_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COVERED_FRACTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --covered_fraction. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --covered_fraction=*) + [ -n "$VIASH_PAR_COVERED_FRACTION" ] && ViashError Bad arguments for option \'--covered_fraction=*\': \'$VIASH_PAR_COVERED_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COVERED_FRACTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + -C) + [ -n "$VIASH_PAR_COVERED_FRACTION" ] && ViashError Bad arguments for option \'-C\': \'$VIASH_PAR_COVERED_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COVERED_FRACTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -C. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_itd_length) + [ -n "$VIASH_PAR_MAX_ITD_LENGTH" ] && ViashError Bad arguments for option \'--max_itd_length\': \'$VIASH_PAR_MAX_ITD_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_ITD_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_itd_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_itd_length=*) + [ -n "$VIASH_PAR_MAX_ITD_LENGTH" ] && ViashError Bad arguments for option \'--max_itd_length=*\': \'$VIASH_PAR_MAX_ITD_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_ITD_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -l) + [ -n "$VIASH_PAR_MAX_ITD_LENGTH" ] && ViashError Bad arguments for option \'-l\': \'$VIASH_PAR_MAX_ITD_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_ITD_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -l. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_itd_allele_fraction) + [ -n "$VIASH_PAR_MIN_ITD_ALLELE_FRACTION" ] && ViashError Bad arguments for option \'--min_itd_allele_fraction\': \'$VIASH_PAR_MIN_ITD_ALLELE_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ITD_ALLELE_FRACTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_itd_allele_fraction. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_itd_allele_fraction=*) + [ -n "$VIASH_PAR_MIN_ITD_ALLELE_FRACTION" ] && ViashError Bad arguments for option \'--min_itd_allele_fraction=*\': \'$VIASH_PAR_MIN_ITD_ALLELE_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ITD_ALLELE_FRACTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + -z) + [ -n "$VIASH_PAR_MIN_ITD_ALLELE_FRACTION" ] && ViashError Bad arguments for option \'-z\': \'$VIASH_PAR_MIN_ITD_ALLELE_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ITD_ALLELE_FRACTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -z. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_itd_supporting_reads) + [ -n "$VIASH_PAR_MIN_ITD_SUPPORTING_READS" ] && ViashError Bad arguments for option \'--min_itd_supporting_reads\': \'$VIASH_PAR_MIN_ITD_SUPPORTING_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ITD_SUPPORTING_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_itd_supporting_reads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_itd_supporting_reads=*) + [ -n "$VIASH_PAR_MIN_ITD_SUPPORTING_READS" ] && ViashError Bad arguments for option \'--min_itd_supporting_reads=*\': \'$VIASH_PAR_MIN_ITD_SUPPORTING_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ITD_SUPPORTING_READS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -Z) + [ -n "$VIASH_PAR_MIN_ITD_SUPPORTING_READS" ] && ViashError Bad arguments for option \'-Z\': \'$VIASH_PAR_MIN_ITD_SUPPORTING_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ITD_SUPPORTING_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -Z. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --skip_duplicate_marking) + [ -n "$VIASH_PAR_SKIP_DUPLICATE_MARKING" ] && ViashError Bad arguments for option \'--skip_duplicate_marking\': \'$VIASH_PAR_SKIP_DUPLICATE_MARKING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SKIP_DUPLICATE_MARKING=true + shift 1 + ;; + -u) + [ -n "$VIASH_PAR_SKIP_DUPLICATE_MARKING" ] && ViashError Bad arguments for option \'-u\': \'$VIASH_PAR_SKIP_DUPLICATE_MARKING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SKIP_DUPLICATE_MARKING=true + shift 1 + ;; + --extra_information) + [ -n "$VIASH_PAR_EXTRA_INFORMATION" ] && ViashError Bad arguments for option \'--extra_information\': \'$VIASH_PAR_EXTRA_INFORMATION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXTRA_INFORMATION=true + shift 1 + ;; + -X) + [ -n "$VIASH_PAR_EXTRA_INFORMATION" ] && ViashError Bad arguments for option \'-X\': \'$VIASH_PAR_EXTRA_INFORMATION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXTRA_INFORMATION=true + shift 1 + ;; + --fill_gaps) + [ -n "$VIASH_PAR_FILL_GAPS" ] && ViashError Bad arguments for option \'--fill_gaps\': \'$VIASH_PAR_FILL_GAPS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILL_GAPS=true + shift 1 + ;; + -I) + [ -n "$VIASH_PAR_FILL_GAPS" ] && ViashError Bad arguments for option \'-I\': \'$VIASH_PAR_FILL_GAPS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILL_GAPS=true + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/arriba:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults +if [ -z ${VIASH_META_CPUS+x} ]; then + VIASH_META_CPUS="1" +fi + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_BAM+x} ]; then + ViashError '--bam' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_GENOME+x} ]; then + ViashError '--genome' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_GENE_ANNOTATION+x} ]; then + ViashError '--gene_annotation' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_FUSIONS+x} ]; then + ViashError '--fusions' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_SKIP_DUPLICATE_MARKING+x} ]; then + VIASH_PAR_SKIP_DUPLICATE_MARKING="false" +fi +if [ -z ${VIASH_PAR_EXTRA_INFORMATION+x} ]; then + VIASH_PAR_EXTRA_INFORMATION="false" +fi +if [ -z ${VIASH_PAR_FILL_GAPS+x} ]; then + VIASH_PAR_FILL_GAPS="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_BAM" ] && [ ! -e "$VIASH_PAR_BAM" ]; then + ViashError "Input file '$VIASH_PAR_BAM' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_GENOME" ] && [ ! -e "$VIASH_PAR_GENOME" ]; then + ViashError "Input file '$VIASH_PAR_GENOME' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_GENE_ANNOTATION" ] && [ ! -e "$VIASH_PAR_GENE_ANNOTATION" ]; then + ViashError "Input file '$VIASH_PAR_GENE_ANNOTATION' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_KNOWN_FUSIONS" ] && [ ! -e "$VIASH_PAR_KNOWN_FUSIONS" ]; then + ViashError "Input file '$VIASH_PAR_KNOWN_FUSIONS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_BLACKLIST" ] && [ ! -e "$VIASH_PAR_BLACKLIST" ]; then + ViashError "Input file '$VIASH_PAR_BLACKLIST' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_STRUCTURAL_VARIANTS" ] && [ ! -e "$VIASH_PAR_STRUCTURAL_VARIANTS" ]; then + ViashError "Input file '$VIASH_PAR_STRUCTURAL_VARIANTS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_TAGS" ] && [ ! -e "$VIASH_PAR_TAGS" ]; then + ViashError "Input file '$VIASH_PAR_TAGS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_PROTEIN_DOMAINS" ] && [ ! -e "$VIASH_PAR_PROTEIN_DOMAINS" ]; then + ViashError "Input file '$VIASH_PAR_PROTEIN_DOMAINS' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE" ]]; then + if ! [[ "$VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_genomic_breakpoint_distance' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_E_VALUE" ]]; then + if ! [[ "$VIASH_PAR_MAX_E_VALUE" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--max_e_value' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_SUPPORTING_READS" ]]; then + if ! [[ "$VIASH_PAR_MIN_SUPPORTING_READS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_supporting_reads' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_MISMAPPERS" ]]; then + if ! [[ "$VIASH_PAR_MAX_MISMAPPERS" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--max_mismappers' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_HOMOLOG_IDENTITY" ]]; then + if ! [[ "$VIASH_PAR_MAX_HOMOLOG_IDENTITY" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--max_homolog_identity' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_HOMOPOLYMER_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_HOMOPOLYMER_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--homopolymer_length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_READ_THROUGH_DISTANCE" ]]; then + if ! [[ "$VIASH_PAR_READ_THROUGH_DISTANCE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--read_through_distance' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_ANCHOR_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_MIN_ANCHOR_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_anchor_length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MANY_SPLICED_EVENTS" ]]; then + if ! [[ "$VIASH_PAR_MANY_SPLICED_EVENTS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--many_spliced_events' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_KMER_CONTENT" ]]; then + if ! [[ "$VIASH_PAR_MAX_KMER_CONTENT" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--max_kmer_content' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_MISMATCH_PVALUE" ]]; then + if ! [[ "$VIASH_PAR_MAX_MISMATCH_PVALUE" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--max_mismatch_pvalue' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FRAGMENT_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_FRAGMENT_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--fragment_length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_READS" ]]; then + if ! [[ "$VIASH_PAR_MAX_READS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_reads' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_QUANTILE" ]]; then + if ! [[ "$VIASH_PAR_QUANTILE" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--quantile' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_EXONIC_FRACTION" ]]; then + if ! [[ "$VIASH_PAR_EXONIC_FRACTION" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--exonic_fraction' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TOP_N" ]]; then + if ! [[ "$VIASH_PAR_TOP_N" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--top_n' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_COVERED_FRACTION" ]]; then + if ! [[ "$VIASH_PAR_COVERED_FRACTION" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--covered_fraction' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_ITD_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_MAX_ITD_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_itd_length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_ITD_ALLELE_FRACTION" ]]; then + if ! [[ "$VIASH_PAR_MIN_ITD_ALLELE_FRACTION" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--min_itd_allele_fraction' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_ITD_SUPPORTING_READS" ]]; then + if ! [[ "$VIASH_PAR_MIN_ITD_SUPPORTING_READS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_itd_supporting_reads' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SKIP_DUPLICATE_MARKING" ]]; then + if ! [[ "$VIASH_PAR_SKIP_DUPLICATE_MARKING" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--skip_duplicate_marking' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_EXTRA_INFORMATION" ]]; then + if ! [[ "$VIASH_PAR_EXTRA_INFORMATION" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--extra_information' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FILL_GAPS" ]]; then + if ! [[ "$VIASH_PAR_FILL_GAPS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--fill_gaps' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# check whether value is belongs to a set of choices +if [ ! -z "$VIASH_PAR_STRANDEDNESS" ]; then + VIASH_PAR_STRANDEDNESS_CHOICES=("auto;yes;no;reverse") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_STRANDEDNESS_CHOICES[*]};" =~ ";$VIASH_PAR_STRANDEDNESS;" ]]; then + ViashError '--strandedness' specified value of \'$VIASH_PAR_STRANDEDNESS\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +if [ ! -z "$VIASH_PAR_DISABLE_FILTERS" ]; then + VIASH_PAR_DISABLE_FILTERS_CHOICES=("homologs;low_entropy;isoforms;top_expressed_viral_contigs;viral_contigs;uninteresting_contigs;non_coding_neighbors;mismatches;duplicates;no_genomic_support;genomic_support;intronic;end_to_end;relative_support;low_coverage_viral_contigs;merge_adjacent;mismappers;multimappers;same_gene;long_gap;internal_tandem_duplication;small_insert_size;read_through;inconsistently_clipped;intragenic_exonic;marginal_read_through;spliced;hairpin;blacklist;min_support;select_best;in_vitro;short_anchor;known_fusions;no_coverage;homopolymer;many_spliced") + IFS=';' + set -f + for val in $VIASH_PAR_DISABLE_FILTERS; do + if ! [[ ";${VIASH_PAR_DISABLE_FILTERS_CHOICES[*]};" =~ ";${val};" ]]; then + ViashError '--disable_filters' specified value of \'${val}\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_FUSIONS" ] && [ ! -d "$(dirname "$VIASH_PAR_FUSIONS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_FUSIONS")" +fi +if [ ! -z "$VIASH_PAR_FUSIONS_DISCARDED" ] && [ ! -d "$(dirname "$VIASH_PAR_FUSIONS_DISCARDED")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_FUSIONS_DISCARDED")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_BAM" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_BAM")" ) + VIASH_PAR_BAM=$(ViashDockerAutodetectMount "$VIASH_PAR_BAM") +fi +if [ ! -z "$VIASH_PAR_GENOME" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_GENOME")" ) + VIASH_PAR_GENOME=$(ViashDockerAutodetectMount "$VIASH_PAR_GENOME") +fi +if [ ! -z "$VIASH_PAR_GENE_ANNOTATION" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_GENE_ANNOTATION")" ) + VIASH_PAR_GENE_ANNOTATION=$(ViashDockerAutodetectMount "$VIASH_PAR_GENE_ANNOTATION") +fi +if [ ! -z "$VIASH_PAR_KNOWN_FUSIONS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_KNOWN_FUSIONS")" ) + VIASH_PAR_KNOWN_FUSIONS=$(ViashDockerAutodetectMount "$VIASH_PAR_KNOWN_FUSIONS") +fi +if [ ! -z "$VIASH_PAR_BLACKLIST" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_BLACKLIST")" ) + VIASH_PAR_BLACKLIST=$(ViashDockerAutodetectMount "$VIASH_PAR_BLACKLIST") +fi +if [ ! -z "$VIASH_PAR_STRUCTURAL_VARIANTS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_STRUCTURAL_VARIANTS")" ) + VIASH_PAR_STRUCTURAL_VARIANTS=$(ViashDockerAutodetectMount "$VIASH_PAR_STRUCTURAL_VARIANTS") +fi +if [ ! -z "$VIASH_PAR_TAGS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_TAGS")" ) + VIASH_PAR_TAGS=$(ViashDockerAutodetectMount "$VIASH_PAR_TAGS") +fi +if [ ! -z "$VIASH_PAR_PROTEIN_DOMAINS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_PROTEIN_DOMAINS")" ) + VIASH_PAR_PROTEIN_DOMAINS=$(ViashDockerAutodetectMount "$VIASH_PAR_PROTEIN_DOMAINS") +fi +if [ ! -z "$VIASH_PAR_FUSIONS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FUSIONS")" ) + VIASH_PAR_FUSIONS=$(ViashDockerAutodetectMount "$VIASH_PAR_FUSIONS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_FUSIONS" ) +fi +if [ ! -z "$VIASH_PAR_FUSIONS_DISCARDED" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FUSIONS_DISCARDED")" ) + VIASH_PAR_FUSIONS_DISCARDED=$(ViashDockerAutodetectMount "$VIASH_PAR_FUSIONS_DISCARDED") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_FUSIONS_DISCARDED" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-arriba-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_BAM+x} ]; then echo "${VIASH_PAR_BAM}" | sed "s#'#'\"'\"'#g;s#.*#par_bam='&'#" ; else echo "# par_bam="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOME+x} ]; then echo "${VIASH_PAR_GENOME}" | sed "s#'#'\"'\"'#g;s#.*#par_genome='&'#" ; else echo "# par_genome="; fi ) +$( if [ ! -z ${VIASH_PAR_GENE_ANNOTATION+x} ]; then echo "${VIASH_PAR_GENE_ANNOTATION}" | sed "s#'#'\"'\"'#g;s#.*#par_gene_annotation='&'#" ; else echo "# par_gene_annotation="; fi ) +$( if [ ! -z ${VIASH_PAR_KNOWN_FUSIONS+x} ]; then echo "${VIASH_PAR_KNOWN_FUSIONS}" | sed "s#'#'\"'\"'#g;s#.*#par_known_fusions='&'#" ; else echo "# par_known_fusions="; fi ) +$( if [ ! -z ${VIASH_PAR_BLACKLIST+x} ]; then echo "${VIASH_PAR_BLACKLIST}" | sed "s#'#'\"'\"'#g;s#.*#par_blacklist='&'#" ; else echo "# par_blacklist="; fi ) +$( if [ ! -z ${VIASH_PAR_STRUCTURAL_VARIANTS+x} ]; then echo "${VIASH_PAR_STRUCTURAL_VARIANTS}" | sed "s#'#'\"'\"'#g;s#.*#par_structural_variants='&'#" ; else echo "# par_structural_variants="; fi ) +$( if [ ! -z ${VIASH_PAR_TAGS+x} ]; then echo "${VIASH_PAR_TAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_tags='&'#" ; else echo "# par_tags="; fi ) +$( if [ ! -z ${VIASH_PAR_PROTEIN_DOMAINS+x} ]; then echo "${VIASH_PAR_PROTEIN_DOMAINS}" | sed "s#'#'\"'\"'#g;s#.*#par_protein_domains='&'#" ; else echo "# par_protein_domains="; fi ) +$( if [ ! -z ${VIASH_PAR_FUSIONS+x} ]; then echo "${VIASH_PAR_FUSIONS}" | sed "s#'#'\"'\"'#g;s#.*#par_fusions='&'#" ; else echo "# par_fusions="; fi ) +$( if [ ! -z ${VIASH_PAR_FUSIONS_DISCARDED+x} ]; then echo "${VIASH_PAR_FUSIONS_DISCARDED}" | sed "s#'#'\"'\"'#g;s#.*#par_fusions_discarded='&'#" ; else echo "# par_fusions_discarded="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE+x} ]; then echo "${VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE}" | sed "s#'#'\"'\"'#g;s#.*#par_max_genomic_breakpoint_distance='&'#" ; else echo "# par_max_genomic_breakpoint_distance="; fi ) +$( if [ ! -z ${VIASH_PAR_STRANDEDNESS+x} ]; then echo "${VIASH_PAR_STRANDEDNESS}" | sed "s#'#'\"'\"'#g;s#.*#par_strandedness='&'#" ; else echo "# par_strandedness="; fi ) +$( if [ ! -z ${VIASH_PAR_INTERESTING_CONTIGS+x} ]; then echo "${VIASH_PAR_INTERESTING_CONTIGS}" | sed "s#'#'\"'\"'#g;s#.*#par_interesting_contigs='&'#" ; else echo "# par_interesting_contigs="; fi ) +$( if [ ! -z ${VIASH_PAR_VIRAL_CONTIGS+x} ]; then echo "${VIASH_PAR_VIRAL_CONTIGS}" | sed "s#'#'\"'\"'#g;s#.*#par_viral_contigs='&'#" ; else echo "# par_viral_contigs="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_FILTERS+x} ]; then echo "${VIASH_PAR_DISABLE_FILTERS}" | sed "s#'#'\"'\"'#g;s#.*#par_disable_filters='&'#" ; else echo "# par_disable_filters="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_E_VALUE+x} ]; then echo "${VIASH_PAR_MAX_E_VALUE}" | sed "s#'#'\"'\"'#g;s#.*#par_max_e_value='&'#" ; else echo "# par_max_e_value="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_SUPPORTING_READS+x} ]; then echo "${VIASH_PAR_MIN_SUPPORTING_READS}" | sed "s#'#'\"'\"'#g;s#.*#par_min_supporting_reads='&'#" ; else echo "# par_min_supporting_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_MISMAPPERS+x} ]; then echo "${VIASH_PAR_MAX_MISMAPPERS}" | sed "s#'#'\"'\"'#g;s#.*#par_max_mismappers='&'#" ; else echo "# par_max_mismappers="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_HOMOLOG_IDENTITY+x} ]; then echo "${VIASH_PAR_MAX_HOMOLOG_IDENTITY}" | sed "s#'#'\"'\"'#g;s#.*#par_max_homolog_identity='&'#" ; else echo "# par_max_homolog_identity="; fi ) +$( if [ ! -z ${VIASH_PAR_HOMOPOLYMER_LENGTH+x} ]; then echo "${VIASH_PAR_HOMOPOLYMER_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_homopolymer_length='&'#" ; else echo "# par_homopolymer_length="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_THROUGH_DISTANCE+x} ]; then echo "${VIASH_PAR_READ_THROUGH_DISTANCE}" | sed "s#'#'\"'\"'#g;s#.*#par_read_through_distance='&'#" ; else echo "# par_read_through_distance="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ANCHOR_LENGTH+x} ]; then echo "${VIASH_PAR_MIN_ANCHOR_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_min_anchor_length='&'#" ; else echo "# par_min_anchor_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MANY_SPLICED_EVENTS+x} ]; then echo "${VIASH_PAR_MANY_SPLICED_EVENTS}" | sed "s#'#'\"'\"'#g;s#.*#par_many_spliced_events='&'#" ; else echo "# par_many_spliced_events="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_KMER_CONTENT+x} ]; then echo "${VIASH_PAR_MAX_KMER_CONTENT}" | sed "s#'#'\"'\"'#g;s#.*#par_max_kmer_content='&'#" ; else echo "# par_max_kmer_content="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_MISMATCH_PVALUE+x} ]; then echo "${VIASH_PAR_MAX_MISMATCH_PVALUE}" | sed "s#'#'\"'\"'#g;s#.*#par_max_mismatch_pvalue='&'#" ; else echo "# par_max_mismatch_pvalue="; fi ) +$( if [ ! -z ${VIASH_PAR_FRAGMENT_LENGTH+x} ]; then echo "${VIASH_PAR_FRAGMENT_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_fragment_length='&'#" ; else echo "# par_fragment_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_READS+x} ]; then echo "${VIASH_PAR_MAX_READS}" | sed "s#'#'\"'\"'#g;s#.*#par_max_reads='&'#" ; else echo "# par_max_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_QUANTILE+x} ]; then echo "${VIASH_PAR_QUANTILE}" | sed "s#'#'\"'\"'#g;s#.*#par_quantile='&'#" ; else echo "# par_quantile="; fi ) +$( if [ ! -z ${VIASH_PAR_EXONIC_FRACTION+x} ]; then echo "${VIASH_PAR_EXONIC_FRACTION}" | sed "s#'#'\"'\"'#g;s#.*#par_exonic_fraction='&'#" ; else echo "# par_exonic_fraction="; fi ) +$( if [ ! -z ${VIASH_PAR_TOP_N+x} ]; then echo "${VIASH_PAR_TOP_N}" | sed "s#'#'\"'\"'#g;s#.*#par_top_n='&'#" ; else echo "# par_top_n="; fi ) +$( if [ ! -z ${VIASH_PAR_COVERED_FRACTION+x} ]; then echo "${VIASH_PAR_COVERED_FRACTION}" | sed "s#'#'\"'\"'#g;s#.*#par_covered_fraction='&'#" ; else echo "# par_covered_fraction="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_ITD_LENGTH+x} ]; then echo "${VIASH_PAR_MAX_ITD_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_max_itd_length='&'#" ; else echo "# par_max_itd_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ITD_ALLELE_FRACTION+x} ]; then echo "${VIASH_PAR_MIN_ITD_ALLELE_FRACTION}" | sed "s#'#'\"'\"'#g;s#.*#par_min_itd_allele_fraction='&'#" ; else echo "# par_min_itd_allele_fraction="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ITD_SUPPORTING_READS+x} ]; then echo "${VIASH_PAR_MIN_ITD_SUPPORTING_READS}" | sed "s#'#'\"'\"'#g;s#.*#par_min_itd_supporting_reads='&'#" ; else echo "# par_min_itd_supporting_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_SKIP_DUPLICATE_MARKING+x} ]; then echo "${VIASH_PAR_SKIP_DUPLICATE_MARKING}" | sed "s#'#'\"'\"'#g;s#.*#par_skip_duplicate_marking='&'#" ; else echo "# par_skip_duplicate_marking="; fi ) +$( if [ ! -z ${VIASH_PAR_EXTRA_INFORMATION+x} ]; then echo "${VIASH_PAR_EXTRA_INFORMATION}" | sed "s#'#'\"'\"'#g;s#.*#par_extra_information='&'#" ; else echo "# par_extra_information="; fi ) +$( if [ ! -z ${VIASH_PAR_FILL_GAPS+x} ]; then echo "${VIASH_PAR_FILL_GAPS}" | sed "s#'#'\"'\"'#g;s#.*#par_fill_gaps='&'#" ; else echo "# par_fill_gaps="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# unset flags +[[ "\$par_skip_duplicate_marking" == "false" ]] && unset par_skip_duplicate_marking +[[ "\$par_extra_information" == "false" ]] && unset par_extra_information +[[ "\$par_fill_gaps" == "false" ]] && unset par_fill_gaps + +# replace ';' with ',' +par_interesting_contigs=\$(echo \$par_interesting_contigs | tr ';' ',') +par_viral_contigs=\$(echo \$par_viral_contigs | tr ';' ',') +par_disable_filters=\$(echo \$par_disable_filters | tr ';' ',') + +# run arriba +arriba \\ + -x "\$par_bam" \\ + -a "\$par_genome" \\ + -g "\$par_gene_annotation" \\ + -o "\$par_fusions" \\ + \${par_known_fusions:+-k "\${par_known_fusions}"} \\ + \${par_blacklist:+-b "\${par_blacklist}"} \\ + \${par_structural_variants:+-d "\${par_structural_variants}"} \\ + \${par_tags:+-t "\${par_tags}"} \\ + \${par_protein_domains:+-p "\${par_protein_domains}"} \\ + \${par_fusions_discarded:+-O "\${par_fusions_discarded}"} \\ + \${par_max_genomic_breakpoint_distance:+-D "\${par_max_genomic_breakpoint_distance}"} \\ + \${par_strandedness:+-s "\${par_strandedness}"} \\ + \${par_interesting_contigs:+-i "\${par_interesting_contigs}"} \\ + \${par_viral_contigs:+-v "\${par_viral_contigs}"} \\ + \${par_disable_filters:+-f "\${par_disable_filters}"} \\ + \${par_max_e_value:+-E "\${par_max_e_value}"} \\ + \${par_min_supporting_reads:+-S "\${par_min_supporting_reads}"} \\ + \${par_max_mismappers:+-m "\${par_max_mismappers}"} \\ + \${par_max_homolog_identity:+-L "\${par_max_homolog_identity}"} \\ + \${par_homopolymer_length:+-H "\${par_homopolymer_length}"} \\ + \${par_read_through_distance:+-R "\${par_read_through_distance}"} \\ + \${par_min_anchor_length:+-A "\${par_min_anchor_length}"} \\ + \${par_many_spliced_events:+-M "\${par_many_spliced_events}"} \\ + \${par_max_kmer_content:+-K "\${par_max_kmer_content}"} \\ + \${par_max_mismatch_pvalue:+-V "\${par_max_mismatch_pvalue}"} \\ + \${par_fragment_length:+-F "\${par_fragment_length}"} \\ + \${par_max_reads:+-U "\${par_max_reads}"} \\ + \${par_quantile:+-Q "\${par_quantile}"} \\ + \${par_exonic_fraction:+-e "\${par_exonic_fraction}"} \\ + \${par_top_n:+-T "\${par_top_n}"} \\ + \${par_covered_fraction:+-C "\${par_covered_fraction}"} \\ + \${par_max_itd_length:+-l "\${par_max_itd_length}"} \\ + \${par_min_itd_allele_fraction:+-z "\${par_min_itd_allele_fraction}"} \\ + \${par_min_itd_supporting_reads:+-Z "\${par_min_itd_supporting_reads}"} \\ + \${par_skip_duplicate_marking:+-u} \\ + \${par_extra_information:+-X} \\ + \${par_fill_gaps:+-I} +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_BAM" ]; then + VIASH_PAR_BAM=$(ViashDockerStripAutomount "$VIASH_PAR_BAM") + fi + if [ ! -z "$VIASH_PAR_GENOME" ]; then + VIASH_PAR_GENOME=$(ViashDockerStripAutomount "$VIASH_PAR_GENOME") + fi + if [ ! -z "$VIASH_PAR_GENE_ANNOTATION" ]; then + VIASH_PAR_GENE_ANNOTATION=$(ViashDockerStripAutomount "$VIASH_PAR_GENE_ANNOTATION") + fi + if [ ! -z "$VIASH_PAR_KNOWN_FUSIONS" ]; then + VIASH_PAR_KNOWN_FUSIONS=$(ViashDockerStripAutomount "$VIASH_PAR_KNOWN_FUSIONS") + fi + if [ ! -z "$VIASH_PAR_BLACKLIST" ]; then + VIASH_PAR_BLACKLIST=$(ViashDockerStripAutomount "$VIASH_PAR_BLACKLIST") + fi + if [ ! -z "$VIASH_PAR_STRUCTURAL_VARIANTS" ]; then + VIASH_PAR_STRUCTURAL_VARIANTS=$(ViashDockerStripAutomount "$VIASH_PAR_STRUCTURAL_VARIANTS") + fi + if [ ! -z "$VIASH_PAR_TAGS" ]; then + VIASH_PAR_TAGS=$(ViashDockerStripAutomount "$VIASH_PAR_TAGS") + fi + if [ ! -z "$VIASH_PAR_PROTEIN_DOMAINS" ]; then + VIASH_PAR_PROTEIN_DOMAINS=$(ViashDockerStripAutomount "$VIASH_PAR_PROTEIN_DOMAINS") + fi + if [ ! -z "$VIASH_PAR_FUSIONS" ]; then + VIASH_PAR_FUSIONS=$(ViashDockerStripAutomount "$VIASH_PAR_FUSIONS") + fi + if [ ! -z "$VIASH_PAR_FUSIONS_DISCARDED" ]; then + VIASH_PAR_FUSIONS_DISCARDED=$(ViashDockerStripAutomount "$VIASH_PAR_FUSIONS_DISCARDED") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_FUSIONS" ] && [ ! -e "$VIASH_PAR_FUSIONS" ]; then + ViashError "Output file '$VIASH_PAR_FUSIONS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_FUSIONS_DISCARDED" ] && [ ! -e "$VIASH_PAR_FUSIONS_DISCARDED" ]; then + ViashError "Output file '$VIASH_PAR_FUSIONS_DISCARDED' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/bcl_convert/.config.vsh.yaml b/target/executable/bcl_convert/.config.vsh.yaml new file mode 100644 index 00000000..5ca4f853 --- /dev/null +++ b/target/executable/bcl_convert/.config.vsh.yaml @@ -0,0 +1,412 @@ +name: "bcl_convert" +version: "main" +argument_groups: +- name: "Input arguments" + arguments: + - type: "file" + name: "--bcl_input_directory" + alternatives: + - "-i" + description: "Input run directory" + info: null + example: + - "bcl_dir" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--sample_sheet" + alternatives: + - "-s" + description: "Path to SampleSheet.csv file (default searched for in --bcl_input_directory)" + info: null + example: + - "bcl_dir/sample_sheet.csv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--run_info" + description: "Path to RunInfo.xml file (default root of BCL input directory)" + info: null + example: + - "bcl_dir/RunInfo.xml" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Lane and tile settings" + arguments: + - type: "integer" + name: "--bcl_only_lane" + description: "Convert only specified lane number (default all lanes)" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--first_tile_only" + description: "Only convert first tile of input (for testing & debugging)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--tiles" + description: "Process only a subset of tiles by a regular expression" + info: null + example: + - "s_[0-9]+_1" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--exclude_tiles" + description: "Exclude set of tiles by a regular expression" + info: null + example: + - "s_[0-9]+_1" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Resource arguments" + arguments: + - type: "boolean" + name: "--shared_thread_odirect_output" + description: "Use linux native asynchronous io (io_submit) for file output (Default=false)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bcl_num_parallel_tiles" + description: "\\# of tiles to process in parallel (default 1)" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bcl_num_conversion_threads" + description: "\\# of threads for conversion (per tile, default # cpu threads)" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bcl_num_compression_threads" + description: "\\# of threads for fastq.gz output compression (per tile, default\ + \ # cpu threads, or HW+12)" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bcl_num_decompression_threads" + description: "\\# of threads for bcl/cbcl input decompression (per tile, default\ + \ half # cpu threads, or HW+8). Only applies when preloading files" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Run arguments" + arguments: + - type: "boolean" + name: "--bcl_only_matched_reads" + description: "For pure BCL conversion, do not output files for 'Undetermined'\ + \ [unmatched] reads (output by default)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--no_lane_splitting" + description: "Do not split FASTQ file by lane (false by default)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--num_unknown_barcodes_reported" + description: "\\# of Top Unknown Barcodes to output (1000 by default)" + info: null + example: + - 1000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--bcl_validate_sample_sheet_only" + description: "Only validate RunInfo.xml & SampleSheet files (produce no FASTQ\ + \ files)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--strict_mode" + description: "Abort if any files are missing (false by default)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--sample_name_column_enabled" + description: "Use sample sheet 'Sample_Name' column when naming fastq files &\ + \ subdirectories" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output arguments" + arguments: + - type: "file" + name: "--output_directory" + alternatives: + - "-o" + description: "Output directory containig fastq files" + info: null + example: + - "fastq_dir" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--bcl_sampleproject_subdirectories" + description: "Output to subdirectories based upon sample sheet 'Sample_Project'\ + \ column" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--fastq_gzip_compression_level" + description: "Set fastq output compression level 0-9 (default 1)" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reports" + description: "Reports directory" + info: null + example: + - "reports_dir" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--logs" + description: "Reports directory" + info: null + example: + - "logs_dir" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Convert bcl files to fastq files using bcl-convert.\nInformation about\ + \ upgrading from bcl2fastq via\n[Upgrading from bcl2fastq to BCL Convert](https://emea.support.illumina.com/bulletins/2020/10/upgrading-from-bcl2fastq-to-bcl-convert.html)\n\ + and [BCL Convert Compatible Products](https://support.illumina.com/sequencing/sequencing_software/bcl-convert/compatibility.html)\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +license: "MIT" +links: + repository: "https://github.com/viash-hub/biobox" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "debian:trixie-slim" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "apt" + packages: + - "wget" + - "gdb" + - "which" + - "hostname" + - "alien" + - "procps" + interactive: false + - type: "docker" + run: + - "wget https://s3.amazonaws.com/webdata.illumina.com/downloads/software/bcl-convert/bcl-convert-4.2.7-2.el8.x86_64.rpm\ + \ -O /tmp/bcl-convert.rpm && \\\nalien -i /tmp/bcl-convert.rpm && \\\nrm -rf\ + \ /var/lib/apt/lists/* && \\\nrm /tmp/bcl-convert.rpm\n" + - type: "docker" + run: + - "echo \"bcl-convert: \\\"$(bcl-convert -V 2>&1 >/dev/null | sed -n '/Version/\ + \ s/^bcl-convert\\ Version //p')\\\"\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/bcl_convert/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/bcl_convert" + executable: "target/executable/bcl_convert/bcl_convert" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/bcl_convert/bcl_convert b/target/executable/bcl_convert/bcl_convert new file mode 100755 index 00000000..d96bae66 --- /dev/null +++ b/target/executable/bcl_convert/bcl_convert @@ -0,0 +1,1623 @@ +#!/usr/bin/env bash + +# bcl_convert main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="bcl_convert" +VIASH_META_FUNCTIONALITY_NAME="bcl_convert" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "bcl_convert main" + echo "" + echo "Convert bcl files to fastq files using bcl-convert." + echo "Information about upgrading from bcl2fastq via" + echo "[Upgrading from bcl2fastq to BCL" + echo "Convert](https://emea.support.illumina.com/bulletins/2020/10/upgrading-from-bcl2fastq-to-bcl-convert.html)" + echo "and [BCL Convert Compatible" + echo "Products](https://support.illumina.com/sequencing/sequencing_software/bcl-convert/compatibility.html)" + echo "" + echo "Input arguments:" + echo " -i, --bcl_input_directory" + echo " type: file, required parameter, file must exist" + echo " example: bcl_dir" + echo " Input run directory" + echo "" + echo " -s, --sample_sheet" + echo " type: file, file must exist" + echo " example: bcl_dir/sample_sheet.csv" + echo " Path to SampleSheet.csv file (default searched for in" + echo " --bcl_input_directory)" + echo "" + echo " --run_info" + echo " type: file, file must exist" + echo " example: bcl_dir/RunInfo.xml" + echo " Path to RunInfo.xml file (default root of BCL input directory)" + echo "" + echo "Lane and tile settings:" + echo " --bcl_only_lane" + echo " type: integer" + echo " example: 1" + echo " Convert only specified lane number (default all lanes)" + echo "" + echo " --first_tile_only" + echo " type: boolean" + echo " example: true" + echo " Only convert first tile of input (for testing & debugging)" + echo "" + echo " --tiles" + echo " type: string" + echo " example: s_[0-9]+_1" + echo " Process only a subset of tiles by a regular expression" + echo "" + echo " --exclude_tiles" + echo " type: string" + echo " example: s_[0-9]+_1" + echo " Exclude set of tiles by a regular expression" + echo "" + echo "Resource arguments:" + echo " --shared_thread_odirect_output" + echo " type: boolean" + echo " example: true" + echo " Use linux native asynchronous io (io_submit) for file output" + echo " (Default=false)" + echo "" + echo " --bcl_num_parallel_tiles" + echo " type: integer" + echo " example: 1" + echo " \\# of tiles to process in parallel (default 1)" + echo "" + echo " --bcl_num_conversion_threads" + echo " type: integer" + echo " example: 1" + echo " \\# of threads for conversion (per tile, default # cpu threads)" + echo "" + echo " --bcl_num_compression_threads" + echo " type: integer" + echo " example: 1" + echo " \\# of threads for fastq.gz output compression (per tile, default # cpu" + echo " threads, or HW+12)" + echo "" + echo " --bcl_num_decompression_threads" + echo " type: integer" + echo " example: 1" + echo " \\# of threads for bcl/cbcl input decompression (per tile, default half #" + echo " cpu threads, or HW+8). Only applies when preloading files" + echo "" + echo "Run arguments:" + echo " --bcl_only_matched_reads" + echo " type: boolean" + echo " example: true" + echo " For pure BCL conversion, do not output files for 'Undetermined'" + echo " [unmatched] reads (output by default)" + echo "" + echo " --no_lane_splitting" + echo " type: boolean" + echo " example: true" + echo " Do not split FASTQ file by lane (false by default)" + echo "" + echo " --num_unknown_barcodes_reported" + echo " type: integer" + echo " example: 1000" + echo " \\# of Top Unknown Barcodes to output (1000 by default)" + echo "" + echo " --bcl_validate_sample_sheet_only" + echo " type: boolean" + echo " example: true" + echo " Only validate RunInfo.xml & SampleSheet files (produce no FASTQ files)" + echo "" + echo " --strict_mode" + echo " type: boolean" + echo " example: true" + echo " Abort if any files are missing (false by default)" + echo "" + echo " --sample_name_column_enabled" + echo " type: boolean" + echo " example: true" + echo " Use sample sheet 'Sample_Name' column when naming fastq files &" + echo " subdirectories" + echo "" + echo "Output arguments:" + echo " -o, --output_directory" + echo " type: file, required parameter, output, file must exist" + echo " example: fastq_dir" + echo " Output directory containig fastq files" + echo "" + echo " --bcl_sampleproject_subdirectories" + echo " type: boolean" + echo " example: true" + echo " Output to subdirectories based upon sample sheet 'Sample_Project' column" + echo "" + echo " --fastq_gzip_compression_level" + echo " type: integer" + echo " example: 1" + echo " Set fastq output compression level 0-9 (default 1)" + echo "" + echo " --reports" + echo " type: file, output, file must exist" + echo " example: reports_dir" + echo " Reports directory" + echo "" + echo " --logs" + echo " type: file, output, file must exist" + echo " example: logs_dir" + echo " Reports directory" +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM debian:trixie-slim +ENTRYPOINT [] +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y wget gdb which hostname alien procps && \ + rm -rf /var/lib/apt/lists/* + +RUN wget https://s3.amazonaws.com/webdata.illumina.com/downloads/software/bcl-convert/bcl-convert-4.2.7-2.el8.x86_64.rpm -O /tmp/bcl-convert.rpm && \ +alien -i /tmp/bcl-convert.rpm && \ +rm -rf /var/lib/apt/lists/* && \ +rm /tmp/bcl-convert.rpm + +RUN echo "bcl-convert: \"$(bcl-convert -V 2>&1 >/dev/null | sed -n '/Version/ s/^bcl-convert\ Version //p')\"" > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component bcl_convert" +LABEL org.opencontainers.image.created="2024-06-24T08:36:37Z" +LABEL org.opencontainers.image.source="https://github.com/viash-hub/biobox" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "bcl_convert main" + exit + ;; + --bcl_input_directory) + [ -n "$VIASH_PAR_BCL_INPUT_DIRECTORY" ] && ViashError Bad arguments for option \'--bcl_input_directory\': \'$VIASH_PAR_BCL_INPUT_DIRECTORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_INPUT_DIRECTORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bcl_input_directory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bcl_input_directory=*) + [ -n "$VIASH_PAR_BCL_INPUT_DIRECTORY" ] && ViashError Bad arguments for option \'--bcl_input_directory=*\': \'$VIASH_PAR_BCL_INPUT_DIRECTORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_INPUT_DIRECTORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + -i) + [ -n "$VIASH_PAR_BCL_INPUT_DIRECTORY" ] && ViashError Bad arguments for option \'-i\': \'$VIASH_PAR_BCL_INPUT_DIRECTORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_INPUT_DIRECTORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -i. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sample_sheet) + [ -n "$VIASH_PAR_SAMPLE_SHEET" ] && ViashError Bad arguments for option \'--sample_sheet\': \'$VIASH_PAR_SAMPLE_SHEET\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_SHEET="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sample_sheet. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sample_sheet=*) + [ -n "$VIASH_PAR_SAMPLE_SHEET" ] && ViashError Bad arguments for option \'--sample_sheet=*\': \'$VIASH_PAR_SAMPLE_SHEET\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_SHEET=$(ViashRemoveFlags "$1") + shift 1 + ;; + -s) + [ -n "$VIASH_PAR_SAMPLE_SHEET" ] && ViashError Bad arguments for option \'-s\': \'$VIASH_PAR_SAMPLE_SHEET\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_SHEET="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -s. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --run_info) + [ -n "$VIASH_PAR_RUN_INFO" ] && ViashError Bad arguments for option \'--run_info\': \'$VIASH_PAR_RUN_INFO\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RUN_INFO="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --run_info. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --run_info=*) + [ -n "$VIASH_PAR_RUN_INFO" ] && ViashError Bad arguments for option \'--run_info=*\': \'$VIASH_PAR_RUN_INFO\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RUN_INFO=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bcl_only_lane) + [ -n "$VIASH_PAR_BCL_ONLY_LANE" ] && ViashError Bad arguments for option \'--bcl_only_lane\': \'$VIASH_PAR_BCL_ONLY_LANE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_ONLY_LANE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bcl_only_lane. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bcl_only_lane=*) + [ -n "$VIASH_PAR_BCL_ONLY_LANE" ] && ViashError Bad arguments for option \'--bcl_only_lane=*\': \'$VIASH_PAR_BCL_ONLY_LANE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_ONLY_LANE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --first_tile_only) + [ -n "$VIASH_PAR_FIRST_TILE_ONLY" ] && ViashError Bad arguments for option \'--first_tile_only\': \'$VIASH_PAR_FIRST_TILE_ONLY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FIRST_TILE_ONLY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --first_tile_only. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --first_tile_only=*) + [ -n "$VIASH_PAR_FIRST_TILE_ONLY" ] && ViashError Bad arguments for option \'--first_tile_only=*\': \'$VIASH_PAR_FIRST_TILE_ONLY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FIRST_TILE_ONLY=$(ViashRemoveFlags "$1") + shift 1 + ;; + --tiles) + [ -n "$VIASH_PAR_TILES" ] && ViashError Bad arguments for option \'--tiles\': \'$VIASH_PAR_TILES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TILES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --tiles. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tiles=*) + [ -n "$VIASH_PAR_TILES" ] && ViashError Bad arguments for option \'--tiles=*\': \'$VIASH_PAR_TILES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TILES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --exclude_tiles) + [ -n "$VIASH_PAR_EXCLUDE_TILES" ] && ViashError Bad arguments for option \'--exclude_tiles\': \'$VIASH_PAR_EXCLUDE_TILES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCLUDE_TILES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --exclude_tiles. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --exclude_tiles=*) + [ -n "$VIASH_PAR_EXCLUDE_TILES" ] && ViashError Bad arguments for option \'--exclude_tiles=*\': \'$VIASH_PAR_EXCLUDE_TILES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCLUDE_TILES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --shared_thread_odirect_output) + [ -n "$VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT" ] && ViashError Bad arguments for option \'--shared_thread_odirect_output\': \'$VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --shared_thread_odirect_output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --shared_thread_odirect_output=*) + [ -n "$VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT" ] && ViashError Bad arguments for option \'--shared_thread_odirect_output=*\': \'$VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bcl_num_parallel_tiles) + [ -n "$VIASH_PAR_BCL_NUM_PARALLEL_TILES" ] && ViashError Bad arguments for option \'--bcl_num_parallel_tiles\': \'$VIASH_PAR_BCL_NUM_PARALLEL_TILES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_NUM_PARALLEL_TILES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bcl_num_parallel_tiles. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bcl_num_parallel_tiles=*) + [ -n "$VIASH_PAR_BCL_NUM_PARALLEL_TILES" ] && ViashError Bad arguments for option \'--bcl_num_parallel_tiles=*\': \'$VIASH_PAR_BCL_NUM_PARALLEL_TILES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_NUM_PARALLEL_TILES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bcl_num_conversion_threads) + [ -n "$VIASH_PAR_BCL_NUM_CONVERSION_THREADS" ] && ViashError Bad arguments for option \'--bcl_num_conversion_threads\': \'$VIASH_PAR_BCL_NUM_CONVERSION_THREADS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_NUM_CONVERSION_THREADS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bcl_num_conversion_threads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bcl_num_conversion_threads=*) + [ -n "$VIASH_PAR_BCL_NUM_CONVERSION_THREADS" ] && ViashError Bad arguments for option \'--bcl_num_conversion_threads=*\': \'$VIASH_PAR_BCL_NUM_CONVERSION_THREADS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_NUM_CONVERSION_THREADS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bcl_num_compression_threads) + [ -n "$VIASH_PAR_BCL_NUM_COMPRESSION_THREADS" ] && ViashError Bad arguments for option \'--bcl_num_compression_threads\': \'$VIASH_PAR_BCL_NUM_COMPRESSION_THREADS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_NUM_COMPRESSION_THREADS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bcl_num_compression_threads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bcl_num_compression_threads=*) + [ -n "$VIASH_PAR_BCL_NUM_COMPRESSION_THREADS" ] && ViashError Bad arguments for option \'--bcl_num_compression_threads=*\': \'$VIASH_PAR_BCL_NUM_COMPRESSION_THREADS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_NUM_COMPRESSION_THREADS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bcl_num_decompression_threads) + [ -n "$VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS" ] && ViashError Bad arguments for option \'--bcl_num_decompression_threads\': \'$VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bcl_num_decompression_threads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bcl_num_decompression_threads=*) + [ -n "$VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS" ] && ViashError Bad arguments for option \'--bcl_num_decompression_threads=*\': \'$VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bcl_only_matched_reads) + [ -n "$VIASH_PAR_BCL_ONLY_MATCHED_READS" ] && ViashError Bad arguments for option \'--bcl_only_matched_reads\': \'$VIASH_PAR_BCL_ONLY_MATCHED_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_ONLY_MATCHED_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bcl_only_matched_reads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bcl_only_matched_reads=*) + [ -n "$VIASH_PAR_BCL_ONLY_MATCHED_READS" ] && ViashError Bad arguments for option \'--bcl_only_matched_reads=*\': \'$VIASH_PAR_BCL_ONLY_MATCHED_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_ONLY_MATCHED_READS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --no_lane_splitting) + [ -n "$VIASH_PAR_NO_LANE_SPLITTING" ] && ViashError Bad arguments for option \'--no_lane_splitting\': \'$VIASH_PAR_NO_LANE_SPLITTING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_LANE_SPLITTING="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --no_lane_splitting. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --no_lane_splitting=*) + [ -n "$VIASH_PAR_NO_LANE_SPLITTING" ] && ViashError Bad arguments for option \'--no_lane_splitting=*\': \'$VIASH_PAR_NO_LANE_SPLITTING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_LANE_SPLITTING=$(ViashRemoveFlags "$1") + shift 1 + ;; + --num_unknown_barcodes_reported) + [ -n "$VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED" ] && ViashError Bad arguments for option \'--num_unknown_barcodes_reported\': \'$VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --num_unknown_barcodes_reported. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --num_unknown_barcodes_reported=*) + [ -n "$VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED" ] && ViashError Bad arguments for option \'--num_unknown_barcodes_reported=*\': \'$VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bcl_validate_sample_sheet_only) + [ -n "$VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY" ] && ViashError Bad arguments for option \'--bcl_validate_sample_sheet_only\': \'$VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bcl_validate_sample_sheet_only. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bcl_validate_sample_sheet_only=*) + [ -n "$VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY" ] && ViashError Bad arguments for option \'--bcl_validate_sample_sheet_only=*\': \'$VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY=$(ViashRemoveFlags "$1") + shift 1 + ;; + --strict_mode) + [ -n "$VIASH_PAR_STRICT_MODE" ] && ViashError Bad arguments for option \'--strict_mode\': \'$VIASH_PAR_STRICT_MODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRICT_MODE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --strict_mode. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --strict_mode=*) + [ -n "$VIASH_PAR_STRICT_MODE" ] && ViashError Bad arguments for option \'--strict_mode=*\': \'$VIASH_PAR_STRICT_MODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRICT_MODE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sample_name_column_enabled) + [ -n "$VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED" ] && ViashError Bad arguments for option \'--sample_name_column_enabled\': \'$VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sample_name_column_enabled. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sample_name_column_enabled=*) + [ -n "$VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED" ] && ViashError Bad arguments for option \'--sample_name_column_enabled=*\': \'$VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output_directory) + [ -n "$VIASH_PAR_OUTPUT_DIRECTORY" ] && ViashError Bad arguments for option \'--output_directory\': \'$VIASH_PAR_OUTPUT_DIRECTORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_DIRECTORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_directory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_directory=*) + [ -n "$VIASH_PAR_OUTPUT_DIRECTORY" ] && ViashError Bad arguments for option \'--output_directory=*\': \'$VIASH_PAR_OUTPUT_DIRECTORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_DIRECTORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTPUT_DIRECTORY" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTPUT_DIRECTORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_DIRECTORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bcl_sampleproject_subdirectories) + [ -n "$VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES" ] && ViashError Bad arguments for option \'--bcl_sampleproject_subdirectories\': \'$VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bcl_sampleproject_subdirectories. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bcl_sampleproject_subdirectories=*) + [ -n "$VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES" ] && ViashError Bad arguments for option \'--bcl_sampleproject_subdirectories=*\': \'$VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --fastq_gzip_compression_level) + [ -n "$VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL" ] && ViashError Bad arguments for option \'--fastq_gzip_compression_level\': \'$VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --fastq_gzip_compression_level. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fastq_gzip_compression_level=*) + [ -n "$VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL" ] && ViashError Bad arguments for option \'--fastq_gzip_compression_level=*\': \'$VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL=$(ViashRemoveFlags "$1") + shift 1 + ;; + --reports) + [ -n "$VIASH_PAR_REPORTS" ] && ViashError Bad arguments for option \'--reports\': \'$VIASH_PAR_REPORTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REPORTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --reports. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --reports=*) + [ -n "$VIASH_PAR_REPORTS" ] && ViashError Bad arguments for option \'--reports=*\': \'$VIASH_PAR_REPORTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REPORTS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --logs) + [ -n "$VIASH_PAR_LOGS" ] && ViashError Bad arguments for option \'--logs\': \'$VIASH_PAR_LOGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LOGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --logs. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --logs=*) + [ -n "$VIASH_PAR_LOGS" ] && ViashError Bad arguments for option \'--logs=*\': \'$VIASH_PAR_LOGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LOGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/bcl_convert:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_BCL_INPUT_DIRECTORY+x} ]; then + ViashError '--bcl_input_directory' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUTPUT_DIRECTORY+x} ]; then + ViashError '--output_directory' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_BCL_INPUT_DIRECTORY" ] && [ ! -e "$VIASH_PAR_BCL_INPUT_DIRECTORY" ]; then + ViashError "Input file '$VIASH_PAR_BCL_INPUT_DIRECTORY' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_SAMPLE_SHEET" ] && [ ! -e "$VIASH_PAR_SAMPLE_SHEET" ]; then + ViashError "Input file '$VIASH_PAR_SAMPLE_SHEET' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_RUN_INFO" ] && [ ! -e "$VIASH_PAR_RUN_INFO" ]; then + ViashError "Input file '$VIASH_PAR_RUN_INFO' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_BCL_ONLY_LANE" ]]; then + if ! [[ "$VIASH_PAR_BCL_ONLY_LANE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--bcl_only_lane' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FIRST_TILE_ONLY" ]]; then + if ! [[ "$VIASH_PAR_FIRST_TILE_ONLY" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--first_tile_only' has to be a boolean. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT" ]]; then + if ! [[ "$VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--shared_thread_odirect_output' has to be a boolean. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BCL_NUM_PARALLEL_TILES" ]]; then + if ! [[ "$VIASH_PAR_BCL_NUM_PARALLEL_TILES" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--bcl_num_parallel_tiles' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BCL_NUM_CONVERSION_THREADS" ]]; then + if ! [[ "$VIASH_PAR_BCL_NUM_CONVERSION_THREADS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--bcl_num_conversion_threads' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BCL_NUM_COMPRESSION_THREADS" ]]; then + if ! [[ "$VIASH_PAR_BCL_NUM_COMPRESSION_THREADS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--bcl_num_compression_threads' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS" ]]; then + if ! [[ "$VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--bcl_num_decompression_threads' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BCL_ONLY_MATCHED_READS" ]]; then + if ! [[ "$VIASH_PAR_BCL_ONLY_MATCHED_READS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--bcl_only_matched_reads' has to be a boolean. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_LANE_SPLITTING" ]]; then + if ! [[ "$VIASH_PAR_NO_LANE_SPLITTING" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_lane_splitting' has to be a boolean. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED" ]]; then + if ! [[ "$VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--num_unknown_barcodes_reported' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY" ]]; then + if ! [[ "$VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--bcl_validate_sample_sheet_only' has to be a boolean. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_STRICT_MODE" ]]; then + if ! [[ "$VIASH_PAR_STRICT_MODE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--strict_mode' has to be a boolean. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED" ]]; then + if ! [[ "$VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--sample_name_column_enabled' has to be a boolean. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES" ]]; then + if ! [[ "$VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--bcl_sampleproject_subdirectories' has to be a boolean. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL" ]]; then + if ! [[ "$VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--fastq_gzip_compression_level' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT_DIRECTORY" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT_DIRECTORY")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT_DIRECTORY")" +fi +if [ ! -z "$VIASH_PAR_REPORTS" ] && [ ! -d "$(dirname "$VIASH_PAR_REPORTS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_REPORTS")" +fi +if [ ! -z "$VIASH_PAR_LOGS" ] && [ ! -d "$(dirname "$VIASH_PAR_LOGS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_LOGS")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_BCL_INPUT_DIRECTORY" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_BCL_INPUT_DIRECTORY")" ) + VIASH_PAR_BCL_INPUT_DIRECTORY=$(ViashDockerAutodetectMount "$VIASH_PAR_BCL_INPUT_DIRECTORY") +fi +if [ ! -z "$VIASH_PAR_SAMPLE_SHEET" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SAMPLE_SHEET")" ) + VIASH_PAR_SAMPLE_SHEET=$(ViashDockerAutodetectMount "$VIASH_PAR_SAMPLE_SHEET") +fi +if [ ! -z "$VIASH_PAR_RUN_INFO" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_RUN_INFO")" ) + VIASH_PAR_RUN_INFO=$(ViashDockerAutodetectMount "$VIASH_PAR_RUN_INFO") +fi +if [ ! -z "$VIASH_PAR_OUTPUT_DIRECTORY" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT_DIRECTORY")" ) + VIASH_PAR_OUTPUT_DIRECTORY=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT_DIRECTORY") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT_DIRECTORY" ) +fi +if [ ! -z "$VIASH_PAR_REPORTS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REPORTS")" ) + VIASH_PAR_REPORTS=$(ViashDockerAutodetectMount "$VIASH_PAR_REPORTS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_REPORTS" ) +fi +if [ ! -z "$VIASH_PAR_LOGS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_LOGS")" ) + VIASH_PAR_LOGS=$(ViashDockerAutodetectMount "$VIASH_PAR_LOGS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_LOGS" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-bcl_convert-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_BCL_INPUT_DIRECTORY+x} ]; then echo "${VIASH_PAR_BCL_INPUT_DIRECTORY}" | sed "s#'#'\"'\"'#g;s#.*#par_bcl_input_directory='&'#" ; else echo "# par_bcl_input_directory="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_SHEET+x} ]; then echo "${VIASH_PAR_SAMPLE_SHEET}" | sed "s#'#'\"'\"'#g;s#.*#par_sample_sheet='&'#" ; else echo "# par_sample_sheet="; fi ) +$( if [ ! -z ${VIASH_PAR_RUN_INFO+x} ]; then echo "${VIASH_PAR_RUN_INFO}" | sed "s#'#'\"'\"'#g;s#.*#par_run_info='&'#" ; else echo "# par_run_info="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_ONLY_LANE+x} ]; then echo "${VIASH_PAR_BCL_ONLY_LANE}" | sed "s#'#'\"'\"'#g;s#.*#par_bcl_only_lane='&'#" ; else echo "# par_bcl_only_lane="; fi ) +$( if [ ! -z ${VIASH_PAR_FIRST_TILE_ONLY+x} ]; then echo "${VIASH_PAR_FIRST_TILE_ONLY}" | sed "s#'#'\"'\"'#g;s#.*#par_first_tile_only='&'#" ; else echo "# par_first_tile_only="; fi ) +$( if [ ! -z ${VIASH_PAR_TILES+x} ]; then echo "${VIASH_PAR_TILES}" | sed "s#'#'\"'\"'#g;s#.*#par_tiles='&'#" ; else echo "# par_tiles="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCLUDE_TILES+x} ]; then echo "${VIASH_PAR_EXCLUDE_TILES}" | sed "s#'#'\"'\"'#g;s#.*#par_exclude_tiles='&'#" ; else echo "# par_exclude_tiles="; fi ) +$( if [ ! -z ${VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT+x} ]; then echo "${VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_shared_thread_odirect_output='&'#" ; else echo "# par_shared_thread_odirect_output="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_NUM_PARALLEL_TILES+x} ]; then echo "${VIASH_PAR_BCL_NUM_PARALLEL_TILES}" | sed "s#'#'\"'\"'#g;s#.*#par_bcl_num_parallel_tiles='&'#" ; else echo "# par_bcl_num_parallel_tiles="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_NUM_CONVERSION_THREADS+x} ]; then echo "${VIASH_PAR_BCL_NUM_CONVERSION_THREADS}" | sed "s#'#'\"'\"'#g;s#.*#par_bcl_num_conversion_threads='&'#" ; else echo "# par_bcl_num_conversion_threads="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_NUM_COMPRESSION_THREADS+x} ]; then echo "${VIASH_PAR_BCL_NUM_COMPRESSION_THREADS}" | sed "s#'#'\"'\"'#g;s#.*#par_bcl_num_compression_threads='&'#" ; else echo "# par_bcl_num_compression_threads="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS+x} ]; then echo "${VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS}" | sed "s#'#'\"'\"'#g;s#.*#par_bcl_num_decompression_threads='&'#" ; else echo "# par_bcl_num_decompression_threads="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_ONLY_MATCHED_READS+x} ]; then echo "${VIASH_PAR_BCL_ONLY_MATCHED_READS}" | sed "s#'#'\"'\"'#g;s#.*#par_bcl_only_matched_reads='&'#" ; else echo "# par_bcl_only_matched_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_LANE_SPLITTING+x} ]; then echo "${VIASH_PAR_NO_LANE_SPLITTING}" | sed "s#'#'\"'\"'#g;s#.*#par_no_lane_splitting='&'#" ; else echo "# par_no_lane_splitting="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED+x} ]; then echo "${VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED}" | sed "s#'#'\"'\"'#g;s#.*#par_num_unknown_barcodes_reported='&'#" ; else echo "# par_num_unknown_barcodes_reported="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY+x} ]; then echo "${VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY}" | sed "s#'#'\"'\"'#g;s#.*#par_bcl_validate_sample_sheet_only='&'#" ; else echo "# par_bcl_validate_sample_sheet_only="; fi ) +$( if [ ! -z ${VIASH_PAR_STRICT_MODE+x} ]; then echo "${VIASH_PAR_STRICT_MODE}" | sed "s#'#'\"'\"'#g;s#.*#par_strict_mode='&'#" ; else echo "# par_strict_mode="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED+x} ]; then echo "${VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED}" | sed "s#'#'\"'\"'#g;s#.*#par_sample_name_column_enabled='&'#" ; else echo "# par_sample_name_column_enabled="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_DIRECTORY+x} ]; then echo "${VIASH_PAR_OUTPUT_DIRECTORY}" | sed "s#'#'\"'\"'#g;s#.*#par_output_directory='&'#" ; else echo "# par_output_directory="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES+x} ]; then echo "${VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES}" | sed "s#'#'\"'\"'#g;s#.*#par_bcl_sampleproject_subdirectories='&'#" ; else echo "# par_bcl_sampleproject_subdirectories="; fi ) +$( if [ ! -z ${VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL+x} ]; then echo "${VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL}" | sed "s#'#'\"'\"'#g;s#.*#par_fastq_gzip_compression_level='&'#" ; else echo "# par_fastq_gzip_compression_level="; fi ) +$( if [ ! -z ${VIASH_PAR_REPORTS+x} ]; then echo "${VIASH_PAR_REPORTS}" | sed "s#'#'\"'\"'#g;s#.*#par_reports='&'#" ; else echo "# par_reports="; fi ) +$( if [ ! -z ${VIASH_PAR_LOGS+x} ]; then echo "${VIASH_PAR_LOGS}" | sed "s#'#'\"'\"'#g;s#.*#par_logs='&'#" ; else echo "# par_logs="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END +#!/bin/bash + +set -eo pipefail + +\$(which bcl-convert) \\ + --bcl-input-directory "\$par_bcl_input_directory" \\ + --output-directory "\$par_output_directory" \\ + \${par_sample_sheet:+ --sample-sheet "\$par_sample_sheet"} \\ + \${par_run_info:+ --run-info "\$par_run_info"} \\ + \${par_bcl_only_lane:+ --bcl-only-lane "\$par_bcl_only_lane"} \\ + \${par_first_tile_only:+ --first-tile-only "\$par_first_tile_only"} \\ + \${par_tiles:+ --tiles "\$par_tiles"} \\ + \${par_exclude_tiles:+ --exclude-tiles "\$par_exclude_tiles"} \\ + \${par_shared_thread_odirect_output:+ --shared-thread-odirect-output "\$par_shared_thread_odirect_output"} \\ + \${par_bcl_num_parallel_tiles:+ --bcl-num-parallel-tiles "\$par_bcl_num_parallel_tiles"} \\ + \${par_bcl_num_conversion_threads:+ --bcl-num-conversion-threads "\$par_bcl_num_conversion_threads"} \\ + \${par_bcl_num_compression_threads:+ --bcl-num-compression-threads "\$par_bcl_num_compression_threads"} \\ + \${par_bcl_num_decompression_threads:+ --bcl-num-decompression-threads "\$par_bcl_num_decompression_threads"} \\ + \${par_bcl_only_matched_reads:+ --bcl-only-matched-reads "\$par_bcl_only_matched_reads"} \\ + \${par_no_lane_splitting:+ --no-lane-splitting "\$par_no_lane_splitting"} \\ + \${par_num_unknown_barcodes_reported:+ --num-unknown-barcodes-reported "\$par_num_unknown_barcodes_reported"} \\ + \${par_bcl_validate_sample_sheet_only:+ --bcl-validate-sample-sheet-only "\$par_bcl_validate_sample_sheet_only"} \\ + \${par_strict_mode:+ --strict-mode "\$par_strict_mode"} \\ + \${par_sample_name_column_enabled:+ --sample-name-column-enabled "\$par_sample_name_column_enabled"} \\ + \${par_bcl_sampleproject_subdirectories:+ --bcl-sampleproject-subdirectories "\$par_bcl_sampleproject_subdirectories"} \\ + \${par_fastq_gzip_compression_level:+ --fastq-gzip-compression-level "\$par_fastq_gzip_compression_level"} + +if [ ! -z "\$par_reports" ]; then + echo "Moving reports to their own location" + mv "\${par_output_directory}/Reports" "\$par_reports" +else + echo "Leaving reports alone" +fi + +if [ ! -z "\$par_logs" ]; then + echo "Moving logs to their own location" + mv "\${par_output_directory}/Logs" "\$par_logs" +else + echo "Leaving logs alone" +fi +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_BCL_INPUT_DIRECTORY" ]; then + VIASH_PAR_BCL_INPUT_DIRECTORY=$(ViashDockerStripAutomount "$VIASH_PAR_BCL_INPUT_DIRECTORY") + fi + if [ ! -z "$VIASH_PAR_SAMPLE_SHEET" ]; then + VIASH_PAR_SAMPLE_SHEET=$(ViashDockerStripAutomount "$VIASH_PAR_SAMPLE_SHEET") + fi + if [ ! -z "$VIASH_PAR_RUN_INFO" ]; then + VIASH_PAR_RUN_INFO=$(ViashDockerStripAutomount "$VIASH_PAR_RUN_INFO") + fi + if [ ! -z "$VIASH_PAR_OUTPUT_DIRECTORY" ]; then + VIASH_PAR_OUTPUT_DIRECTORY=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT_DIRECTORY") + fi + if [ ! -z "$VIASH_PAR_REPORTS" ]; then + VIASH_PAR_REPORTS=$(ViashDockerStripAutomount "$VIASH_PAR_REPORTS") + fi + if [ ! -z "$VIASH_PAR_LOGS" ]; then + VIASH_PAR_LOGS=$(ViashDockerStripAutomount "$VIASH_PAR_LOGS") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT_DIRECTORY" ] && [ ! -e "$VIASH_PAR_OUTPUT_DIRECTORY" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT_DIRECTORY' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REPORTS" ] && [ ! -e "$VIASH_PAR_REPORTS" ]; then + ViashError "Output file '$VIASH_PAR_REPORTS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_LOGS" ] && [ ! -e "$VIASH_PAR_LOGS" ]; then + ViashError "Output file '$VIASH_PAR_LOGS' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/bedtools/bedtools_getfasta/.config.vsh.yaml b/target/executable/bedtools/bedtools_getfasta/.config.vsh.yaml new file mode 100644 index 00000000..d36016e5 --- /dev/null +++ b/target/executable/bedtools/bedtools_getfasta/.config.vsh.yaml @@ -0,0 +1,243 @@ +name: "bedtools_getfasta" +namespace: "bedtools" +version: "main" +argument_groups: +- name: "Input arguments" + arguments: + - type: "file" + name: "--input_fasta" + description: "FASTA file containing sequences for each interval specified in the\ + \ input BED file.\nThe headers in the input FASTA file must exactly match the\ + \ chromosome column in the BED file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--input_bed" + description: "BED file containing intervals to extract from the FASTA file.\n\ + BED files containing a single region require a newline character\nat the end\ + \ of the line, otherwise a blank output file is produced.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--rna" + description: "The FASTA is RNA not DNA. Reverse complementation handled accordingly.\n" + info: null + direction: "input" +- name: "Run arguments" + arguments: + - type: "boolean_true" + name: "--strandedness" + alternatives: + - "-s" + description: "Force strandedness. If the feature occupies the antisense strand,\ + \ the output sequence will\nbe reverse complemented. By default strandedness\ + \ is not taken into account.\n" + info: null + direction: "input" +- name: "Output arguments" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output file where the output from the 'bedtools getfasta' commend\ + \ will\nbe written to.\n" + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--tab" + description: "Report extract sequences in a tab-delimited format instead of in\ + \ FASTA format.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--bed_out" + description: "Report extract sequences in a tab-delimited BED format instead of\ + \ in FASTA format.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--name" + description: "Set the FASTA header for each extracted sequence to be the \"name\"\ + \ and coordinate columns from the BED feature.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--name_only" + description: "Set the FASTA header for each extracted sequence to be the \"name\"\ + \ columns from the BED feature.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--split" + description: "When --input is in BED12 format, create a separate fasta entry for\ + \ each block in a BED12 record,\nblocks being described in the 11th and 12th\ + \ column of the BED.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--full_header" + description: "Use full fasta header. By default, only the word before the first\ + \ space or tab is used.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Extract sequences from a FASTA file for each of the intervals defined\ + \ in a BED/GFF/VCF file." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "sequencing" +- "fasta" +- "BED" +- "GFF" +- "VCF" +license: "GPL-2.0" +references: + doi: + - "10.1093/bioinformatics/btq033" +links: + repository: "https://github.com/arq5x/bedtools2" + documentation: "https://bedtools.readthedocs.io/en/latest/content/tools/getfasta.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "debian:stable-slim" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "apt" + packages: + - "bedtools" + - "procps" + interactive: false + - type: "docker" + run: + - "echo \"bedtools: \\\"$(bedtools --version | sed -n 's/^bedtools //p')\\\"\"\ + \ > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/bedtools/bedtools_getfasta/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/bedtools/bedtools_getfasta" + executable: "target/executable/bedtools/bedtools_getfasta/bedtools_getfasta" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/bedtools/bedtools_getfasta/bedtools_getfasta b/target/executable/bedtools/bedtools_getfasta/bedtools_getfasta new file mode 100755 index 00000000..09ced562 --- /dev/null +++ b/target/executable/bedtools/bedtools_getfasta/bedtools_getfasta @@ -0,0 +1,1272 @@ +#!/usr/bin/env bash + +# bedtools_getfasta main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="bedtools_getfasta" +VIASH_META_FUNCTIONALITY_NAME="bedtools_getfasta" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "bedtools_getfasta main" + echo "" + echo "Extract sequences from a FASTA file for each of the intervals defined in a" + echo "BED/GFF/VCF file." + echo "" + echo "Input arguments:" + echo " --input_fasta" + echo " type: file, file must exist" + echo " FASTA file containing sequences for each interval specified in the input" + echo " BED file." + echo " The headers in the input FASTA file must exactly match the chromosome" + echo " column in the BED file." + echo "" + echo " --input_bed" + echo " type: file, file must exist" + echo " BED file containing intervals to extract from the FASTA file." + echo " BED files containing a single region require a newline character" + echo " at the end of the line, otherwise a blank output file is produced." + echo "" + echo " --rna" + echo " type: boolean_true" + echo " The FASTA is RNA not DNA. Reverse complementation handled accordingly." + echo "" + echo "Run arguments:" + echo " -s, --strandedness" + echo " type: boolean_true" + echo " Force strandedness. If the feature occupies the antisense strand, the" + echo " output sequence will" + echo " be reverse complemented. By default strandedness is not taken into" + echo " account." + echo "" + echo "Output arguments:" + echo " -o, --output" + echo " type: file, required parameter, output, file must exist" + echo " Output file where the output from the 'bedtools getfasta' commend will" + echo " be written to." + echo "" + echo " --tab" + echo " type: boolean_true" + echo " Report extract sequences in a tab-delimited format instead of in FASTA" + echo " format." + echo "" + echo " --bed_out" + echo " type: boolean_true" + echo " Report extract sequences in a tab-delimited BED format instead of in" + echo " FASTA format." + echo "" + echo " --name" + echo " type: boolean_true" + echo " Set the FASTA header for each extracted sequence to be the \"name\" and" + echo " coordinate columns from the BED feature." + echo "" + echo " --name_only" + echo " type: boolean_true" + echo " Set the FASTA header for each extracted sequence to be the \"name\"" + echo " columns from the BED feature." + echo "" + echo " --split" + echo " type: boolean_true" + echo " When --input is in BED12 format, create a separate fasta entry for each" + echo " block in a BED12 record," + echo " blocks being described in the 11th and 12th column of the BED." + echo "" + echo " --full_header" + echo " type: boolean_true" + echo " Use full fasta header. By default, only the word before the first space" + echo " or tab is used." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM debian:stable-slim +ENTRYPOINT [] +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y bedtools procps && \ + rm -rf /var/lib/apt/lists/* + +RUN echo "bedtools: \"$(bedtools --version | sed -n 's/^bedtools //p')\"" > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component bedtools bedtools_getfasta" +LABEL org.opencontainers.image.created="2024-06-24T08:36:44Z" +LABEL org.opencontainers.image.source="https://github.com/arq5x/bedtools2" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "bedtools_getfasta main" + exit + ;; + --input_fasta) + [ -n "$VIASH_PAR_INPUT_FASTA" ] && ViashError Bad arguments for option \'--input_fasta\': \'$VIASH_PAR_INPUT_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_FASTA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input_fasta. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input_fasta=*) + [ -n "$VIASH_PAR_INPUT_FASTA" ] && ViashError Bad arguments for option \'--input_fasta=*\': \'$VIASH_PAR_INPUT_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_FASTA=$(ViashRemoveFlags "$1") + shift 1 + ;; + --input_bed) + [ -n "$VIASH_PAR_INPUT_BED" ] && ViashError Bad arguments for option \'--input_bed\': \'$VIASH_PAR_INPUT_BED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_BED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input_bed. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input_bed=*) + [ -n "$VIASH_PAR_INPUT_BED" ] && ViashError Bad arguments for option \'--input_bed=*\': \'$VIASH_PAR_INPUT_BED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_BED=$(ViashRemoveFlags "$1") + shift 1 + ;; + --rna) + [ -n "$VIASH_PAR_RNA" ] && ViashError Bad arguments for option \'--rna\': \'$VIASH_PAR_RNA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RNA=true + shift 1 + ;; + --strandedness) + [ -n "$VIASH_PAR_STRANDEDNESS" ] && ViashError Bad arguments for option \'--strandedness\': \'$VIASH_PAR_STRANDEDNESS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRANDEDNESS=true + shift 1 + ;; + -s) + [ -n "$VIASH_PAR_STRANDEDNESS" ] && ViashError Bad arguments for option \'-s\': \'$VIASH_PAR_STRANDEDNESS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRANDEDNESS=true + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tab) + [ -n "$VIASH_PAR_TAB" ] && ViashError Bad arguments for option \'--tab\': \'$VIASH_PAR_TAB\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAB=true + shift 1 + ;; + --bed_out) + [ -n "$VIASH_PAR_BED_OUT" ] && ViashError Bad arguments for option \'--bed_out\': \'$VIASH_PAR_BED_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BED_OUT=true + shift 1 + ;; + --name) + [ -n "$VIASH_PAR_NAME" ] && ViashError Bad arguments for option \'--name\': \'$VIASH_PAR_NAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NAME=true + shift 1 + ;; + --name_only) + [ -n "$VIASH_PAR_NAME_ONLY" ] && ViashError Bad arguments for option \'--name_only\': \'$VIASH_PAR_NAME_ONLY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NAME_ONLY=true + shift 1 + ;; + --split) + [ -n "$VIASH_PAR_SPLIT" ] && ViashError Bad arguments for option \'--split\': \'$VIASH_PAR_SPLIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLIT=true + shift 1 + ;; + --full_header) + [ -n "$VIASH_PAR_FULL_HEADER" ] && ViashError Bad arguments for option \'--full_header\': \'$VIASH_PAR_FULL_HEADER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FULL_HEADER=true + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/bedtools/bedtools_getfasta:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_RNA+x} ]; then + VIASH_PAR_RNA="false" +fi +if [ -z ${VIASH_PAR_STRANDEDNESS+x} ]; then + VIASH_PAR_STRANDEDNESS="false" +fi +if [ -z ${VIASH_PAR_TAB+x} ]; then + VIASH_PAR_TAB="false" +fi +if [ -z ${VIASH_PAR_BED_OUT+x} ]; then + VIASH_PAR_BED_OUT="false" +fi +if [ -z ${VIASH_PAR_NAME+x} ]; then + VIASH_PAR_NAME="false" +fi +if [ -z ${VIASH_PAR_NAME_ONLY+x} ]; then + VIASH_PAR_NAME_ONLY="false" +fi +if [ -z ${VIASH_PAR_SPLIT+x} ]; then + VIASH_PAR_SPLIT="false" +fi +if [ -z ${VIASH_PAR_FULL_HEADER+x} ]; then + VIASH_PAR_FULL_HEADER="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT_FASTA" ] && [ ! -e "$VIASH_PAR_INPUT_FASTA" ]; then + ViashError "Input file '$VIASH_PAR_INPUT_FASTA' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_INPUT_BED" ] && [ ! -e "$VIASH_PAR_INPUT_BED" ]; then + ViashError "Input file '$VIASH_PAR_INPUT_BED' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_RNA" ]]; then + if ! [[ "$VIASH_PAR_RNA" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--rna' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_STRANDEDNESS" ]]; then + if ! [[ "$VIASH_PAR_STRANDEDNESS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--strandedness' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TAB" ]]; then + if ! [[ "$VIASH_PAR_TAB" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--tab' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BED_OUT" ]]; then + if ! [[ "$VIASH_PAR_BED_OUT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--bed_out' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NAME" ]]; then + if ! [[ "$VIASH_PAR_NAME" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--name' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NAME_ONLY" ]]; then + if ! [[ "$VIASH_PAR_NAME_ONLY" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--name_only' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SPLIT" ]]; then + if ! [[ "$VIASH_PAR_SPLIT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--split' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FULL_HEADER" ]]; then + if ! [[ "$VIASH_PAR_FULL_HEADER" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--full_header' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT_FASTA" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT_FASTA")" ) + VIASH_PAR_INPUT_FASTA=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT_FASTA") +fi +if [ ! -z "$VIASH_PAR_INPUT_BED" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT_BED")" ) + VIASH_PAR_INPUT_BED=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT_BED") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-bedtools_getfasta-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT_FASTA+x} ]; then echo "${VIASH_PAR_INPUT_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_input_fasta='&'#" ; else echo "# par_input_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_BED+x} ]; then echo "${VIASH_PAR_INPUT_BED}" | sed "s#'#'\"'\"'#g;s#.*#par_input_bed='&'#" ; else echo "# par_input_bed="; fi ) +$( if [ ! -z ${VIASH_PAR_RNA+x} ]; then echo "${VIASH_PAR_RNA}" | sed "s#'#'\"'\"'#g;s#.*#par_rna='&'#" ; else echo "# par_rna="; fi ) +$( if [ ! -z ${VIASH_PAR_STRANDEDNESS+x} ]; then echo "${VIASH_PAR_STRANDEDNESS}" | sed "s#'#'\"'\"'#g;s#.*#par_strandedness='&'#" ; else echo "# par_strandedness="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_TAB+x} ]; then echo "${VIASH_PAR_TAB}" | sed "s#'#'\"'\"'#g;s#.*#par_tab='&'#" ; else echo "# par_tab="; fi ) +$( if [ ! -z ${VIASH_PAR_BED_OUT+x} ]; then echo "${VIASH_PAR_BED_OUT}" | sed "s#'#'\"'\"'#g;s#.*#par_bed_out='&'#" ; else echo "# par_bed_out="; fi ) +$( if [ ! -z ${VIASH_PAR_NAME+x} ]; then echo "${VIASH_PAR_NAME}" | sed "s#'#'\"'\"'#g;s#.*#par_name='&'#" ; else echo "# par_name="; fi ) +$( if [ ! -z ${VIASH_PAR_NAME_ONLY+x} ]; then echo "${VIASH_PAR_NAME_ONLY}" | sed "s#'#'\"'\"'#g;s#.*#par_name_only='&'#" ; else echo "# par_name_only="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLIT+x} ]; then echo "${VIASH_PAR_SPLIT}" | sed "s#'#'\"'\"'#g;s#.*#par_split='&'#" ; else echo "# par_split="; fi ) +$( if [ ! -z ${VIASH_PAR_FULL_HEADER+x} ]; then echo "${VIASH_PAR_FULL_HEADER}" | sed "s#'#'\"'\"'#g;s#.*#par_full_header='&'#" ; else echo "# par_full_header="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END +#!/usr/bin/env bash +set -eo pipefail + +unset_if_false=( par_rna par_strandedness par_tab par_bed_out par_name par_name_only par_split par_full_header ) + +for par in \${unset_if_false[@]}; do + test_val="\${!par}" + [[ "\$test_val" == "false" ]] && unset \$par +done + +bedtools getfasta \\ + -fi "\$par_input_fasta" \\ + -bed "\$par_input_bed" \\ + \${par_rna:+-rna} \\ + \${par_name:+-name} \\ + \${par_name_only:+-nameOnly} \\ + \${par_tab:+-tab} \\ + \${par_bed_out:+-bedOut} \\ + \${par_strandedness:+-s} \\ + \${par_split:+-split} \\ + \${par_full_header:+-fullHeader} > "\$par_output" +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT_FASTA" ]; then + VIASH_PAR_INPUT_FASTA=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT_FASTA") + fi + if [ ! -z "$VIASH_PAR_INPUT_BED" ]; then + VIASH_PAR_INPUT_BED=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT_BED") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/busco/busco_download_datasets/.config.vsh.yaml b/target/executable/busco/busco_download_datasets/.config.vsh.yaml new file mode 100644 index 00000000..9e903bf8 --- /dev/null +++ b/target/executable/busco/busco_download_datasets/.config.vsh.yaml @@ -0,0 +1,170 @@ +name: "busco_download_datasets" +namespace: "busco" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "string" + name: "--download" + description: "Download dataset. Possible values are a specific dataset name, \"\ + all\", \"prokaryota\", \"eukaryota\", or \"virus\".\nThe full list of available\ + \ datasets can be viewed [here](https://busco-data.ezlab.org/v5/data/lineages/)\ + \ or by running the busco/busco_list_datasets component.\n" + info: null + example: + - "stramenopiles_odb10" + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--download_path" + description: "Local filepath for storing BUSCO dataset downloads\n" + info: null + example: + - "busco_downloads" + default: + - "busco_downloads" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Downloads available busco datasets" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "lineage datasets" +license: "MIT" +references: + doi: + - "10.1007/978-1-4939-9173-0_14" +links: + repository: "https://gitlab.com/ezlab/busco" + homepage: "https://busco.ezlab.org/" + documentation: "https://busco.ezlab.org/busco_userguide.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "busco --version | sed 's/BUSCO\\s\\(.*\\)/busco: \"\\1\"/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/busco/busco_download_datasets/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/busco/busco_download_datasets" + executable: "target/executable/busco/busco_download_datasets/busco_download_datasets" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/busco/busco_download_datasets/busco_download_datasets b/target/executable/busco/busco_download_datasets/busco_download_datasets new file mode 100755 index 00000000..6fc6d579 --- /dev/null +++ b/target/executable/busco/busco_download_datasets/busco_download_datasets @@ -0,0 +1,1048 @@ +#!/usr/bin/env bash + +# busco_download_datasets main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="busco_download_datasets" +VIASH_META_FUNCTIONALITY_NAME="busco_download_datasets" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "busco_download_datasets main" + echo "" + echo "Downloads available busco datasets" + echo "" + echo "Inputs:" + echo " --download" + echo " type: string, required parameter" + echo " example: stramenopiles_odb10" + echo " Download dataset. Possible values are a specific dataset name, \"all\"," + echo " \"prokaryota\", \"eukaryota\", or \"virus\"." + echo " The full list of available datasets can be viewed" + echo " [here](https://busco-data.ezlab.org/v5/data/lineages/) or by running the" + echo " busco/busco_list_datasets component." + echo "" + echo "Outputs:" + echo " --download_path" + echo " type: file, output, file must exist" + echo " default: busco_downloads" + echo " example: busco_downloads" + echo " Local filepath for storing BUSCO dataset downloads" +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0 +ENTRYPOINT [] +RUN busco --version | sed 's/BUSCO\s\(.*\)/busco: "\1"/' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component busco busco_download_datasets" +LABEL org.opencontainers.image.created="2024-06-24T08:36:44Z" +LABEL org.opencontainers.image.source="https://gitlab.com/ezlab/busco" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "busco_download_datasets main" + exit + ;; + --download) + [ -n "$VIASH_PAR_DOWNLOAD" ] && ViashError Bad arguments for option \'--download\': \'$VIASH_PAR_DOWNLOAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DOWNLOAD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --download. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --download=*) + [ -n "$VIASH_PAR_DOWNLOAD" ] && ViashError Bad arguments for option \'--download=*\': \'$VIASH_PAR_DOWNLOAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DOWNLOAD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --download_path) + [ -n "$VIASH_PAR_DOWNLOAD_PATH" ] && ViashError Bad arguments for option \'--download_path\': \'$VIASH_PAR_DOWNLOAD_PATH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DOWNLOAD_PATH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --download_path. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --download_path=*) + [ -n "$VIASH_PAR_DOWNLOAD_PATH" ] && ViashError Bad arguments for option \'--download_path=*\': \'$VIASH_PAR_DOWNLOAD_PATH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DOWNLOAD_PATH=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/busco/busco_download_datasets:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_DOWNLOAD+x} ]; then + ViashError '--download' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_DOWNLOAD_PATH+x} ]; then + VIASH_PAR_DOWNLOAD_PATH="busco_downloads" +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_DOWNLOAD_PATH" ] && [ ! -d "$(dirname "$VIASH_PAR_DOWNLOAD_PATH")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_DOWNLOAD_PATH")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_DOWNLOAD_PATH" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_DOWNLOAD_PATH")" ) + VIASH_PAR_DOWNLOAD_PATH=$(ViashDockerAutodetectMount "$VIASH_PAR_DOWNLOAD_PATH") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_DOWNLOAD_PATH" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-busco_download_datasets-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_DOWNLOAD+x} ]; then echo "${VIASH_PAR_DOWNLOAD}" | sed "s#'#'\"'\"'#g;s#.*#par_download='&'#" ; else echo "# par_download="; fi ) +$( if [ ! -z ${VIASH_PAR_DOWNLOAD_PATH+x} ]; then echo "${VIASH_PAR_DOWNLOAD_PATH}" | sed "s#'#'\"'\"'#g;s#.*#par_download_path='&'#" ; else echo "# par_download_path="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + + +if [ ! -d "\$par_download_path" ]; then + mkdir -p "\$par_download_path" +fi + +busco \\ + --download_path "\$par_download_path" \\ + --download "\$par_download" + +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_DOWNLOAD_PATH" ]; then + VIASH_PAR_DOWNLOAD_PATH=$(ViashDockerStripAutomount "$VIASH_PAR_DOWNLOAD_PATH") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_DOWNLOAD_PATH" ] && [ ! -e "$VIASH_PAR_DOWNLOAD_PATH" ]; then + ViashError "Output file '$VIASH_PAR_DOWNLOAD_PATH' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/busco/busco_list_datasets/.config.vsh.yaml b/target/executable/busco/busco_list_datasets/.config.vsh.yaml new file mode 100644 index 00000000..816eba36 --- /dev/null +++ b/target/executable/busco/busco_list_datasets/.config.vsh.yaml @@ -0,0 +1,157 @@ +name: "busco_list_datasets" +namespace: "busco" +version: "main" +argument_groups: +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output file of the available busco datasets\n" + info: null + example: + - "file.txt" + default: + - "busco_dataset_list.txt" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Lists the available busco datasets" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "lineage datasets" +license: "MIT" +references: + doi: + - "10.1007/978-1-4939-9173-0_14" +links: + repository: "https://gitlab.com/ezlab/busco" + homepage: "https://busco.ezlab.org/" + documentation: "https://busco.ezlab.org/busco_userguide.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "busco --version | sed 's/BUSCO\\s\\(.*\\)/busco: \"\\1\"/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/busco/busco_list_datasets/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/busco/busco_list_datasets" + executable: "target/executable/busco/busco_list_datasets/busco_list_datasets" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/busco/busco_list_datasets/busco_list_datasets b/target/executable/busco/busco_list_datasets/busco_list_datasets new file mode 100755 index 00000000..638824a9 --- /dev/null +++ b/target/executable/busco/busco_list_datasets/busco_list_datasets @@ -0,0 +1,1020 @@ +#!/usr/bin/env bash + +# busco_list_datasets main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="busco_list_datasets" +VIASH_META_FUNCTIONALITY_NAME="busco_list_datasets" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "busco_list_datasets main" + echo "" + echo "Lists the available busco datasets" + echo "" + echo "Outputs:" + echo " -o, --output" + echo " type: file, output, file must exist" + echo " default: busco_dataset_list.txt" + echo " example: file.txt" + echo " Output file of the available busco datasets" +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0 +ENTRYPOINT [] +RUN busco --version | sed 's/BUSCO\s\(.*\)/busco: "\1"/' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component busco busco_list_datasets" +LABEL org.opencontainers.image.created="2024-06-24T08:36:43Z" +LABEL org.opencontainers.image.source="https://gitlab.com/ezlab/busco" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "busco_list_datasets main" + exit + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/busco/busco_list_datasets:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + VIASH_PAR_OUTPUT="busco_dataset_list.txt" +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-busco_list_datasets-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +busco --list-datasets | awk '/^#{40}/{flag=1; next} flag{print}' > \$par_output +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/busco/busco_run/.config.vsh.yaml b/target/executable/busco/busco_run/.config.vsh.yaml new file mode 100644 index 00000000..fc6e63d6 --- /dev/null +++ b/target/executable/busco/busco_run/.config.vsh.yaml @@ -0,0 +1,430 @@ +name: "busco_run" +namespace: "busco" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + alternatives: + - "-i" + description: "Input sequence file in FASTA format. Can be an assembled genome\ + \ or transcriptome (DNA), or protein sequences from an annotated gene set. Also\ + \ possible to use a path to a directory containing multiple input files.\n" + info: null + example: + - "file.fasta" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--mode" + alternatives: + - "-m" + description: "Specify which BUSCO analysis mode to run. There are three valid\ + \ modes:\n - geno or genome, for genome assemblies (DNA)\n - tran or transcriptome,\ + \ for transcriptome assemblies (DNA)\n - prot or proteins, for annotated gene\ + \ sets (protein)\n" + info: null + example: + - "proteins" + required: true + choices: + - "genome" + - "geno" + - "transcriptome" + - "tran" + - "proteins" + - "prot" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--lineage_dataset" + alternatives: + - "-l" + description: "Specify a BUSCO lineage dataset that is most closely related to\ + \ the assembly or gene set being assessed. \nThe full list of available datasets\ + \ can be viewed [here](https://busco-data.ezlab.org/v5/data/lineages/) or by\ + \ running the busco/busco_list_datasets component.\nWhen unsure, the \"--auto_lineage\"\ + \ flag can be set to automatically find the optimal lineage path.\nBUSCO will\ + \ automatically download the requested dataset if it is not already present\ + \ in the download folder. \nYou can optionally provide a path to a local dataset\ + \ instead of a name, e.g. path/to/dataset.\nDatasets can be downloaded using\ + \ the busco/busco_download_dataset component.\n" + info: null + example: + - "stramenopiles_odb10" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--short_summary_json" + description: "Output file for short summary in JSON format.\n" + info: null + example: + - "short_summary.json" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--short_summary_txt" + description: "Output file for short summary in TXT format.\n" + info: null + example: + - "short_summary.txt" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--full_table" + description: "Full table output in TSV format.\n" + info: null + example: + - "full_table.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--missing_busco_list" + description: "Missing list output in TSV format.\n" + info: null + example: + - "missing_busco_list.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--output_dir" + description: "The full output directory, if so desired.\n" + info: null + example: + - "output_dir" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Resource and Run Settings" + arguments: + - type: "boolean_true" + name: "--force" + description: "Force rewriting of existing files. Must be used when output files\ + \ with the provided name already exist.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--quiet" + alternatives: + - "-q" + description: "Disable the info logs, displays only errors.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--restart" + alternatives: + - "-r" + description: "Continue a run that had already partially completed. Restarting\ + \ skips calls to tools that have completed but performs all pre- and post-processing\ + \ steps.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--tar" + description: "Compress some subdirectories with many files to save space.\n" + info: null + direction: "input" +- name: "Lineage Dataset Settings" + arguments: + - type: "boolean_true" + name: "--auto_lineage" + description: "Run auto-lineage pipelilne to automatically determine BUSCO lineage\ + \ dataset that is most closely related to the assembly or gene set being assessed.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--auto_lineage_euk" + description: "Run auto-placement just on eukaryota tree to find optimal lineage\ + \ path.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--auto_lineage_prok" + description: "Run auto_lineage just on prokaryota trees to find optimum lineage\ + \ path.\n" + info: null + direction: "input" + - type: "string" + name: "--datasets_version" + description: "Specify the version of BUSCO datasets\n" + info: null + example: + - "odb10" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Augustus Settings" + arguments: + - type: "boolean_true" + name: "--augustus" + description: "Use augustus gene predictor for eukaryote runs.\n" + info: null + direction: "input" + - type: "string" + name: "--augustus_parameters" + description: "Additional parameters to be passed to Augustus (see Augustus documentation:\ + \ https://github.com/Gaius-Augustus/Augustus/blob/master/docs/RUNNING-AUGUSTUS.md).\n\ + Parameters should be contained within a single string, without whitespace and\ + \ seperated by commas.\n" + info: null + example: + - "--PARAM1=VALUE1,--PARAM2=VALUE2" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--augustus_species" + description: "Specify the augustus species\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--long" + description: "Optimize Augustus self-training mode. This adds considerably to\ + \ the run time, but can improve results for some non-model organisms.\n" + info: null + direction: "input" +- name: "BBTools Settings" + arguments: + - type: "integer" + name: "--contig_break" + description: "Number of contiguous Ns to signify a break between contigs in BBTools\ + \ analysis.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--limit" + description: "Number of candidate regions (contig or transcript) from the BLAST\ + \ output to consider per BUSCO.\nThis option is only effective in pipelines\ + \ using BLAST, i.e. the genome pipeline (see --augustus) or the prokaryota transcriptome\ + \ pipeline.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--scaffold_composition" + description: "Writes ACGTN content per scaffold to a file scaffold_composition.txt.\n" + info: null + direction: "input" +- name: "BLAST Settings" + arguments: + - type: "double" + name: "--e_value" + description: "E-value cutoff for BLAST searches.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Protein Gene Prediction settings" + arguments: + - type: "boolean_true" + name: "--miniprot" + description: "Use Miniprot gene predictor.\n" + info: null + direction: "input" +- name: "MetaEuk Settings" + arguments: + - type: "string" + name: "--metaeuk_parameters" + description: "Pass additional arguments to Metaeuk for the first run (see Metaeuk\ + \ documentation https://github.com/soedinglab/metaeuk).\nAll parameters should\ + \ be contained within a single string with no white space, with each parameter\ + \ separated by a comma.\n" + info: null + example: + - "--max-overlap=15,--min-exon-aa=15" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--metaeuk_rerun_parameters" + description: "Pass additional arguments to Metaeuk for the second run (see Metaeuk\ + \ documentation https://github.com/soedinglab/metaeuk).\nAll parameters should\ + \ be contained within a single string with no white space, with each parameter\ + \ separated by a comma.\n" + info: null + example: + - "--max-overlap=15,--min-exon-aa=15" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Assessment of genome assembly and annotation completeness with single\ + \ copy orthologs" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "Genome assembly" +- "quality control" +license: "MIT" +references: + doi: + - "10.1007/978-1-4939-9173-0_14" +links: + repository: "https://gitlab.com/ezlab/busco" + homepage: "https://busco.ezlab.org/" + documentation: "https://busco.ezlab.org/busco_userguide.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "busco --version | sed 's/BUSCO\\s\\(.*\\)/busco: \"\\1\"/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/busco/busco_run/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/busco/busco_run" + executable: "target/executable/busco/busco_run/busco_run" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/busco/busco_run/busco_run b/target/executable/busco/busco_run/busco_run new file mode 100755 index 00000000..ac4172a2 --- /dev/null +++ b/target/executable/busco/busco_run/busco_run @@ -0,0 +1,1723 @@ +#!/usr/bin/env bash + +# busco_run main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="busco_run" +VIASH_META_FUNCTIONALITY_NAME="busco_run" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "busco_run main" + echo "" + echo "Assessment of genome assembly and annotation completeness with single copy" + echo "orthologs" + echo "" + echo "Inputs:" + echo " -i, --input" + echo " type: file, required parameter, file must exist" + echo " example: file.fasta" + echo " Input sequence file in FASTA format. Can be an assembled genome or" + echo " transcriptome (DNA), or protein sequences from an annotated gene set." + echo " Also possible to use a path to a directory containing multiple input" + echo " files." + echo "" + echo " -m, --mode" + echo " type: string, required parameter" + echo " example: proteins" + echo " choices: [ genome, geno, transcriptome, tran, proteins, prot ]" + echo " Specify which BUSCO analysis mode to run. There are three valid modes:" + echo " - geno or genome, for genome assemblies (DNA)" + echo " - tran or transcriptome, for transcriptome assemblies (DNA)" + echo " - prot or proteins, for annotated gene sets (protein)" + echo "" + echo " -l, --lineage_dataset" + echo " type: string" + echo " example: stramenopiles_odb10" + echo " Specify a BUSCO lineage dataset that is most closely related to the" + echo " assembly or gene set being assessed." + echo " The full list of available datasets can be viewed" + echo " [here](https://busco-data.ezlab.org/v5/data/lineages/) or by running the" + echo " busco/busco_list_datasets component." + echo " When unsure, the \"--auto_lineage\" flag can be set to automatically find" + echo " the optimal lineage path." + echo " BUSCO will automatically download the requested dataset if it is not" + echo " already present in the download folder." + echo " You can optionally provide a path to a local dataset instead of a name," + echo " e.g. path/to/dataset." + echo " Datasets can be downloaded using the busco/busco_download_dataset" + echo " component." + echo "" + echo "Outputs:" + echo " --short_summary_json" + echo " type: file, output, file must exist" + echo " example: short_summary.json" + echo " Output file for short summary in JSON format." + echo "" + echo " --short_summary_txt" + echo " type: file, output, file must exist" + echo " example: short_summary.txt" + echo " Output file for short summary in TXT format." + echo "" + echo " --full_table" + echo " type: file, output, file must exist" + echo " example: full_table.tsv" + echo " Full table output in TSV format." + echo "" + echo " --missing_busco_list" + echo " type: file, output, file must exist" + echo " example: missing_busco_list.tsv" + echo " Missing list output in TSV format." + echo "" + echo " --output_dir" + echo " type: file, output, file must exist" + echo " example: output_dir" + echo " The full output directory, if so desired." + echo "" + echo "Resource and Run Settings:" + echo " --force" + echo " type: boolean_true" + echo " Force rewriting of existing files. Must be used when output files with" + echo " the provided name already exist." + echo "" + echo " -q, --quiet" + echo " type: boolean_true" + echo " Disable the info logs, displays only errors." + echo "" + echo " -r, --restart" + echo " type: boolean_true" + echo " Continue a run that had already partially completed. Restarting skips" + echo " calls to tools that have completed but performs all pre- and" + echo " post-processing steps." + echo "" + echo " --tar" + echo " type: boolean_true" + echo " Compress some subdirectories with many files to save space." + echo "" + echo "Lineage Dataset Settings:" + echo " --auto_lineage" + echo " type: boolean_true" + echo " Run auto-lineage pipelilne to automatically determine BUSCO lineage" + echo " dataset that is most closely related to the assembly or gene set being" + echo " assessed." + echo "" + echo " --auto_lineage_euk" + echo " type: boolean_true" + echo " Run auto-placement just on eukaryota tree to find optimal lineage path." + echo "" + echo " --auto_lineage_prok" + echo " type: boolean_true" + echo " Run auto_lineage just on prokaryota trees to find optimum lineage path." + echo "" + echo " --datasets_version" + echo " type: string" + echo " example: odb10" + echo " Specify the version of BUSCO datasets" + echo "" + echo "Augustus Settings:" + echo " --augustus" + echo " type: boolean_true" + echo " Use augustus gene predictor for eukaryote runs." + echo "" + echo " --augustus_parameters" + echo " type: string" + echo " example: --PARAM1=VALUE1,--PARAM2=VALUE2" + echo " Additional parameters to be passed to Augustus (see Augustus" + echo " documentation:" + echo " " + echo "https://github.com/Gaius-Augustus/Augustus/blob/master/docs/RUNNING-AUGUSTUS.md)." + echo " Parameters should be contained within a single string, without" + echo " whitespace and seperated by commas." + echo "" + echo " --augustus_species" + echo " type: string" + echo " Specify the augustus species" + echo "" + echo " --long" + echo " type: boolean_true" + echo " Optimize Augustus self-training mode. This adds considerably to the run" + echo " time, but can improve results for some non-model organisms." + echo "" + echo "BBTools Settings:" + echo " --contig_break" + echo " type: integer" + echo " Number of contiguous Ns to signify a break between contigs in BBTools" + echo " analysis." + echo "" + echo " --limit" + echo " type: integer" + echo " Number of candidate regions (contig or transcript) from the BLAST output" + echo " to consider per BUSCO." + echo " This option is only effective in pipelines using BLAST, i.e. the genome" + echo " pipeline (see --augustus) or the prokaryota transcriptome pipeline." + echo "" + echo " --scaffold_composition" + echo " type: boolean_true" + echo " Writes ACGTN content per scaffold to a file scaffold_composition.txt." + echo "" + echo "BLAST Settings:" + echo " --e_value" + echo " type: double" + echo " E-value cutoff for BLAST searches." + echo "" + echo "Protein Gene Prediction settings:" + echo " --miniprot" + echo " type: boolean_true" + echo " Use Miniprot gene predictor." + echo "" + echo "MetaEuk Settings:" + echo " --metaeuk_parameters" + echo " type: string" + echo " example: --max-overlap=15,--min-exon-aa=15" + echo " Pass additional arguments to Metaeuk for the first run (see Metaeuk" + echo " documentation https://github.com/soedinglab/metaeuk)." + echo " All parameters should be contained within a single string with no white" + echo " space, with each parameter separated by a comma." + echo "" + echo " --metaeuk_rerun_parameters" + echo " type: string" + echo " example: --max-overlap=15,--min-exon-aa=15" + echo " Pass additional arguments to Metaeuk for the second run (see Metaeuk" + echo " documentation https://github.com/soedinglab/metaeuk)." + echo " All parameters should be contained within a single string with no white" + echo " space, with each parameter separated by a comma." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0 +ENTRYPOINT [] +RUN busco --version | sed 's/BUSCO\s\(.*\)/busco: "\1"/' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component busco busco_run" +LABEL org.opencontainers.image.created="2024-06-24T08:36:43Z" +LABEL org.opencontainers.image.source="https://gitlab.com/ezlab/busco" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "busco_run main" + exit + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -i) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'-i\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -i. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mode) + [ -n "$VIASH_PAR_MODE" ] && ViashError Bad arguments for option \'--mode\': \'$VIASH_PAR_MODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MODE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --mode. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mode=*) + [ -n "$VIASH_PAR_MODE" ] && ViashError Bad arguments for option \'--mode=*\': \'$VIASH_PAR_MODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MODE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -m) + [ -n "$VIASH_PAR_MODE" ] && ViashError Bad arguments for option \'-m\': \'$VIASH_PAR_MODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MODE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -m. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --lineage_dataset) + [ -n "$VIASH_PAR_LINEAGE_DATASET" ] && ViashError Bad arguments for option \'--lineage_dataset\': \'$VIASH_PAR_LINEAGE_DATASET\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LINEAGE_DATASET="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --lineage_dataset. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --lineage_dataset=*) + [ -n "$VIASH_PAR_LINEAGE_DATASET" ] && ViashError Bad arguments for option \'--lineage_dataset=*\': \'$VIASH_PAR_LINEAGE_DATASET\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LINEAGE_DATASET=$(ViashRemoveFlags "$1") + shift 1 + ;; + -l) + [ -n "$VIASH_PAR_LINEAGE_DATASET" ] && ViashError Bad arguments for option \'-l\': \'$VIASH_PAR_LINEAGE_DATASET\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LINEAGE_DATASET="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -l. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --short_summary_json) + [ -n "$VIASH_PAR_SHORT_SUMMARY_JSON" ] && ViashError Bad arguments for option \'--short_summary_json\': \'$VIASH_PAR_SHORT_SUMMARY_JSON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SHORT_SUMMARY_JSON="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --short_summary_json. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --short_summary_json=*) + [ -n "$VIASH_PAR_SHORT_SUMMARY_JSON" ] && ViashError Bad arguments for option \'--short_summary_json=*\': \'$VIASH_PAR_SHORT_SUMMARY_JSON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SHORT_SUMMARY_JSON=$(ViashRemoveFlags "$1") + shift 1 + ;; + --short_summary_txt) + [ -n "$VIASH_PAR_SHORT_SUMMARY_TXT" ] && ViashError Bad arguments for option \'--short_summary_txt\': \'$VIASH_PAR_SHORT_SUMMARY_TXT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SHORT_SUMMARY_TXT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --short_summary_txt. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --short_summary_txt=*) + [ -n "$VIASH_PAR_SHORT_SUMMARY_TXT" ] && ViashError Bad arguments for option \'--short_summary_txt=*\': \'$VIASH_PAR_SHORT_SUMMARY_TXT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SHORT_SUMMARY_TXT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --full_table) + [ -n "$VIASH_PAR_FULL_TABLE" ] && ViashError Bad arguments for option \'--full_table\': \'$VIASH_PAR_FULL_TABLE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FULL_TABLE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --full_table. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --full_table=*) + [ -n "$VIASH_PAR_FULL_TABLE" ] && ViashError Bad arguments for option \'--full_table=*\': \'$VIASH_PAR_FULL_TABLE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FULL_TABLE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --missing_busco_list) + [ -n "$VIASH_PAR_MISSING_BUSCO_LIST" ] && ViashError Bad arguments for option \'--missing_busco_list\': \'$VIASH_PAR_MISSING_BUSCO_LIST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MISSING_BUSCO_LIST="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --missing_busco_list. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --missing_busco_list=*) + [ -n "$VIASH_PAR_MISSING_BUSCO_LIST" ] && ViashError Bad arguments for option \'--missing_busco_list=*\': \'$VIASH_PAR_MISSING_BUSCO_LIST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MISSING_BUSCO_LIST=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output_dir) + [ -n "$VIASH_PAR_OUTPUT_DIR" ] && ViashError Bad arguments for option \'--output_dir\': \'$VIASH_PAR_OUTPUT_DIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_DIR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_dir. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_dir=*) + [ -n "$VIASH_PAR_OUTPUT_DIR" ] && ViashError Bad arguments for option \'--output_dir=*\': \'$VIASH_PAR_OUTPUT_DIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_DIR=$(ViashRemoveFlags "$1") + shift 1 + ;; + --force) + [ -n "$VIASH_PAR_FORCE" ] && ViashError Bad arguments for option \'--force\': \'$VIASH_PAR_FORCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORCE=true + shift 1 + ;; + --quiet) + [ -n "$VIASH_PAR_QUIET" ] && ViashError Bad arguments for option \'--quiet\': \'$VIASH_PAR_QUIET\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUIET=true + shift 1 + ;; + -q) + [ -n "$VIASH_PAR_QUIET" ] && ViashError Bad arguments for option \'-q\': \'$VIASH_PAR_QUIET\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUIET=true + shift 1 + ;; + --restart) + [ -n "$VIASH_PAR_RESTART" ] && ViashError Bad arguments for option \'--restart\': \'$VIASH_PAR_RESTART\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RESTART=true + shift 1 + ;; + -r) + [ -n "$VIASH_PAR_RESTART" ] && ViashError Bad arguments for option \'-r\': \'$VIASH_PAR_RESTART\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RESTART=true + shift 1 + ;; + --tar) + [ -n "$VIASH_PAR_TAR" ] && ViashError Bad arguments for option \'--tar\': \'$VIASH_PAR_TAR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAR=true + shift 1 + ;; + --auto_lineage) + [ -n "$VIASH_PAR_AUTO_LINEAGE" ] && ViashError Bad arguments for option \'--auto_lineage\': \'$VIASH_PAR_AUTO_LINEAGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUTO_LINEAGE=true + shift 1 + ;; + --auto_lineage_euk) + [ -n "$VIASH_PAR_AUTO_LINEAGE_EUK" ] && ViashError Bad arguments for option \'--auto_lineage_euk\': \'$VIASH_PAR_AUTO_LINEAGE_EUK\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUTO_LINEAGE_EUK=true + shift 1 + ;; + --auto_lineage_prok) + [ -n "$VIASH_PAR_AUTO_LINEAGE_PROK" ] && ViashError Bad arguments for option \'--auto_lineage_prok\': \'$VIASH_PAR_AUTO_LINEAGE_PROK\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUTO_LINEAGE_PROK=true + shift 1 + ;; + --datasets_version) + [ -n "$VIASH_PAR_DATASETS_VERSION" ] && ViashError Bad arguments for option \'--datasets_version\': \'$VIASH_PAR_DATASETS_VERSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DATASETS_VERSION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --datasets_version. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --datasets_version=*) + [ -n "$VIASH_PAR_DATASETS_VERSION" ] && ViashError Bad arguments for option \'--datasets_version=*\': \'$VIASH_PAR_DATASETS_VERSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DATASETS_VERSION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --augustus) + [ -n "$VIASH_PAR_AUGUSTUS" ] && ViashError Bad arguments for option \'--augustus\': \'$VIASH_PAR_AUGUSTUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUGUSTUS=true + shift 1 + ;; + --augustus_parameters) + [ -n "$VIASH_PAR_AUGUSTUS_PARAMETERS" ] && ViashError Bad arguments for option \'--augustus_parameters\': \'$VIASH_PAR_AUGUSTUS_PARAMETERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUGUSTUS_PARAMETERS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --augustus_parameters. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --augustus_parameters=*) + [ -n "$VIASH_PAR_AUGUSTUS_PARAMETERS" ] && ViashError Bad arguments for option \'--augustus_parameters=*\': \'$VIASH_PAR_AUGUSTUS_PARAMETERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUGUSTUS_PARAMETERS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --augustus_species) + [ -n "$VIASH_PAR_AUGUSTUS_SPECIES" ] && ViashError Bad arguments for option \'--augustus_species\': \'$VIASH_PAR_AUGUSTUS_SPECIES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUGUSTUS_SPECIES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --augustus_species. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --augustus_species=*) + [ -n "$VIASH_PAR_AUGUSTUS_SPECIES" ] && ViashError Bad arguments for option \'--augustus_species=*\': \'$VIASH_PAR_AUGUSTUS_SPECIES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUGUSTUS_SPECIES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --long) + [ -n "$VIASH_PAR_LONG" ] && ViashError Bad arguments for option \'--long\': \'$VIASH_PAR_LONG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LONG=true + shift 1 + ;; + --contig_break) + [ -n "$VIASH_PAR_CONTIG_BREAK" ] && ViashError Bad arguments for option \'--contig_break\': \'$VIASH_PAR_CONTIG_BREAK\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CONTIG_BREAK="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --contig_break. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --contig_break=*) + [ -n "$VIASH_PAR_CONTIG_BREAK" ] && ViashError Bad arguments for option \'--contig_break=*\': \'$VIASH_PAR_CONTIG_BREAK\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CONTIG_BREAK=$(ViashRemoveFlags "$1") + shift 1 + ;; + --limit) + [ -n "$VIASH_PAR_LIMIT" ] && ViashError Bad arguments for option \'--limit\': \'$VIASH_PAR_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --limit. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --limit=*) + [ -n "$VIASH_PAR_LIMIT" ] && ViashError Bad arguments for option \'--limit=*\': \'$VIASH_PAR_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMIT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --scaffold_composition) + [ -n "$VIASH_PAR_SCAFFOLD_COMPOSITION" ] && ViashError Bad arguments for option \'--scaffold_composition\': \'$VIASH_PAR_SCAFFOLD_COMPOSITION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCAFFOLD_COMPOSITION=true + shift 1 + ;; + --e_value) + [ -n "$VIASH_PAR_E_VALUE" ] && ViashError Bad arguments for option \'--e_value\': \'$VIASH_PAR_E_VALUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_E_VALUE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --e_value. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --e_value=*) + [ -n "$VIASH_PAR_E_VALUE" ] && ViashError Bad arguments for option \'--e_value=*\': \'$VIASH_PAR_E_VALUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_E_VALUE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --miniprot) + [ -n "$VIASH_PAR_MINIPROT" ] && ViashError Bad arguments for option \'--miniprot\': \'$VIASH_PAR_MINIPROT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MINIPROT=true + shift 1 + ;; + --metaeuk_parameters) + [ -n "$VIASH_PAR_METAEUK_PARAMETERS" ] && ViashError Bad arguments for option \'--metaeuk_parameters\': \'$VIASH_PAR_METAEUK_PARAMETERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_METAEUK_PARAMETERS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --metaeuk_parameters. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --metaeuk_parameters=*) + [ -n "$VIASH_PAR_METAEUK_PARAMETERS" ] && ViashError Bad arguments for option \'--metaeuk_parameters=*\': \'$VIASH_PAR_METAEUK_PARAMETERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_METAEUK_PARAMETERS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --metaeuk_rerun_parameters) + [ -n "$VIASH_PAR_METAEUK_RERUN_PARAMETERS" ] && ViashError Bad arguments for option \'--metaeuk_rerun_parameters\': \'$VIASH_PAR_METAEUK_RERUN_PARAMETERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_METAEUK_RERUN_PARAMETERS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --metaeuk_rerun_parameters. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --metaeuk_rerun_parameters=*) + [ -n "$VIASH_PAR_METAEUK_RERUN_PARAMETERS" ] && ViashError Bad arguments for option \'--metaeuk_rerun_parameters=*\': \'$VIASH_PAR_METAEUK_RERUN_PARAMETERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_METAEUK_RERUN_PARAMETERS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/busco/busco_run:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_MODE+x} ]; then + ViashError '--mode' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_FORCE+x} ]; then + VIASH_PAR_FORCE="false" +fi +if [ -z ${VIASH_PAR_QUIET+x} ]; then + VIASH_PAR_QUIET="false" +fi +if [ -z ${VIASH_PAR_RESTART+x} ]; then + VIASH_PAR_RESTART="false" +fi +if [ -z ${VIASH_PAR_TAR+x} ]; then + VIASH_PAR_TAR="false" +fi +if [ -z ${VIASH_PAR_AUTO_LINEAGE+x} ]; then + VIASH_PAR_AUTO_LINEAGE="false" +fi +if [ -z ${VIASH_PAR_AUTO_LINEAGE_EUK+x} ]; then + VIASH_PAR_AUTO_LINEAGE_EUK="false" +fi +if [ -z ${VIASH_PAR_AUTO_LINEAGE_PROK+x} ]; then + VIASH_PAR_AUTO_LINEAGE_PROK="false" +fi +if [ -z ${VIASH_PAR_AUGUSTUS+x} ]; then + VIASH_PAR_AUGUSTUS="false" +fi +if [ -z ${VIASH_PAR_LONG+x} ]; then + VIASH_PAR_LONG="false" +fi +if [ -z ${VIASH_PAR_SCAFFOLD_COMPOSITION+x} ]; then + VIASH_PAR_SCAFFOLD_COMPOSITION="false" +fi +if [ -z ${VIASH_PAR_MINIPROT+x} ]; then + VIASH_PAR_MINIPROT="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_FORCE" ]]; then + if ! [[ "$VIASH_PAR_FORCE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--force' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_QUIET" ]]; then + if ! [[ "$VIASH_PAR_QUIET" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--quiet' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_RESTART" ]]; then + if ! [[ "$VIASH_PAR_RESTART" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--restart' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TAR" ]]; then + if ! [[ "$VIASH_PAR_TAR" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--tar' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_AUTO_LINEAGE" ]]; then + if ! [[ "$VIASH_PAR_AUTO_LINEAGE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--auto_lineage' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_AUTO_LINEAGE_EUK" ]]; then + if ! [[ "$VIASH_PAR_AUTO_LINEAGE_EUK" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--auto_lineage_euk' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_AUTO_LINEAGE_PROK" ]]; then + if ! [[ "$VIASH_PAR_AUTO_LINEAGE_PROK" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--auto_lineage_prok' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_AUGUSTUS" ]]; then + if ! [[ "$VIASH_PAR_AUGUSTUS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--augustus' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LONG" ]]; then + if ! [[ "$VIASH_PAR_LONG" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--long' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CONTIG_BREAK" ]]; then + if ! [[ "$VIASH_PAR_CONTIG_BREAK" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--contig_break' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LIMIT" ]]; then + if ! [[ "$VIASH_PAR_LIMIT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--limit' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCAFFOLD_COMPOSITION" ]]; then + if ! [[ "$VIASH_PAR_SCAFFOLD_COMPOSITION" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--scaffold_composition' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_E_VALUE" ]]; then + if ! [[ "$VIASH_PAR_E_VALUE" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--e_value' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MINIPROT" ]]; then + if ! [[ "$VIASH_PAR_MINIPROT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--miniprot' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# check whether value is belongs to a set of choices +if [ ! -z "$VIASH_PAR_MODE" ]; then + VIASH_PAR_MODE_CHOICES=("genome;geno;transcriptome;tran;proteins;prot") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_MODE_CHOICES[*]};" =~ ";$VIASH_PAR_MODE;" ]]; then + ViashError '--mode' specified value of \'$VIASH_PAR_MODE\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_SHORT_SUMMARY_JSON" ] && [ ! -d "$(dirname "$VIASH_PAR_SHORT_SUMMARY_JSON")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_SHORT_SUMMARY_JSON")" +fi +if [ ! -z "$VIASH_PAR_SHORT_SUMMARY_TXT" ] && [ ! -d "$(dirname "$VIASH_PAR_SHORT_SUMMARY_TXT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_SHORT_SUMMARY_TXT")" +fi +if [ ! -z "$VIASH_PAR_FULL_TABLE" ] && [ ! -d "$(dirname "$VIASH_PAR_FULL_TABLE")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_FULL_TABLE")" +fi +if [ ! -z "$VIASH_PAR_MISSING_BUSCO_LIST" ] && [ ! -d "$(dirname "$VIASH_PAR_MISSING_BUSCO_LIST")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_MISSING_BUSCO_LIST")" +fi +if [ ! -z "$VIASH_PAR_OUTPUT_DIR" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT_DIR")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT_DIR")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_SHORT_SUMMARY_JSON" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SHORT_SUMMARY_JSON")" ) + VIASH_PAR_SHORT_SUMMARY_JSON=$(ViashDockerAutodetectMount "$VIASH_PAR_SHORT_SUMMARY_JSON") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_SHORT_SUMMARY_JSON" ) +fi +if [ ! -z "$VIASH_PAR_SHORT_SUMMARY_TXT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SHORT_SUMMARY_TXT")" ) + VIASH_PAR_SHORT_SUMMARY_TXT=$(ViashDockerAutodetectMount "$VIASH_PAR_SHORT_SUMMARY_TXT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_SHORT_SUMMARY_TXT" ) +fi +if [ ! -z "$VIASH_PAR_FULL_TABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FULL_TABLE")" ) + VIASH_PAR_FULL_TABLE=$(ViashDockerAutodetectMount "$VIASH_PAR_FULL_TABLE") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_FULL_TABLE" ) +fi +if [ ! -z "$VIASH_PAR_MISSING_BUSCO_LIST" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_MISSING_BUSCO_LIST")" ) + VIASH_PAR_MISSING_BUSCO_LIST=$(ViashDockerAutodetectMount "$VIASH_PAR_MISSING_BUSCO_LIST") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_MISSING_BUSCO_LIST" ) +fi +if [ ! -z "$VIASH_PAR_OUTPUT_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT_DIR")" ) + VIASH_PAR_OUTPUT_DIR=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT_DIR") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT_DIR" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-busco_run-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_MODE+x} ]; then echo "${VIASH_PAR_MODE}" | sed "s#'#'\"'\"'#g;s#.*#par_mode='&'#" ; else echo "# par_mode="; fi ) +$( if [ ! -z ${VIASH_PAR_LINEAGE_DATASET+x} ]; then echo "${VIASH_PAR_LINEAGE_DATASET}" | sed "s#'#'\"'\"'#g;s#.*#par_lineage_dataset='&'#" ; else echo "# par_lineage_dataset="; fi ) +$( if [ ! -z ${VIASH_PAR_SHORT_SUMMARY_JSON+x} ]; then echo "${VIASH_PAR_SHORT_SUMMARY_JSON}" | sed "s#'#'\"'\"'#g;s#.*#par_short_summary_json='&'#" ; else echo "# par_short_summary_json="; fi ) +$( if [ ! -z ${VIASH_PAR_SHORT_SUMMARY_TXT+x} ]; then echo "${VIASH_PAR_SHORT_SUMMARY_TXT}" | sed "s#'#'\"'\"'#g;s#.*#par_short_summary_txt='&'#" ; else echo "# par_short_summary_txt="; fi ) +$( if [ ! -z ${VIASH_PAR_FULL_TABLE+x} ]; then echo "${VIASH_PAR_FULL_TABLE}" | sed "s#'#'\"'\"'#g;s#.*#par_full_table='&'#" ; else echo "# par_full_table="; fi ) +$( if [ ! -z ${VIASH_PAR_MISSING_BUSCO_LIST+x} ]; then echo "${VIASH_PAR_MISSING_BUSCO_LIST}" | sed "s#'#'\"'\"'#g;s#.*#par_missing_busco_list='&'#" ; else echo "# par_missing_busco_list="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_DIR+x} ]; then echo "${VIASH_PAR_OUTPUT_DIR}" | sed "s#'#'\"'\"'#g;s#.*#par_output_dir='&'#" ; else echo "# par_output_dir="; fi ) +$( if [ ! -z ${VIASH_PAR_FORCE+x} ]; then echo "${VIASH_PAR_FORCE}" | sed "s#'#'\"'\"'#g;s#.*#par_force='&'#" ; else echo "# par_force="; fi ) +$( if [ ! -z ${VIASH_PAR_QUIET+x} ]; then echo "${VIASH_PAR_QUIET}" | sed "s#'#'\"'\"'#g;s#.*#par_quiet='&'#" ; else echo "# par_quiet="; fi ) +$( if [ ! -z ${VIASH_PAR_RESTART+x} ]; then echo "${VIASH_PAR_RESTART}" | sed "s#'#'\"'\"'#g;s#.*#par_restart='&'#" ; else echo "# par_restart="; fi ) +$( if [ ! -z ${VIASH_PAR_TAR+x} ]; then echo "${VIASH_PAR_TAR}" | sed "s#'#'\"'\"'#g;s#.*#par_tar='&'#" ; else echo "# par_tar="; fi ) +$( if [ ! -z ${VIASH_PAR_AUTO_LINEAGE+x} ]; then echo "${VIASH_PAR_AUTO_LINEAGE}" | sed "s#'#'\"'\"'#g;s#.*#par_auto_lineage='&'#" ; else echo "# par_auto_lineage="; fi ) +$( if [ ! -z ${VIASH_PAR_AUTO_LINEAGE_EUK+x} ]; then echo "${VIASH_PAR_AUTO_LINEAGE_EUK}" | sed "s#'#'\"'\"'#g;s#.*#par_auto_lineage_euk='&'#" ; else echo "# par_auto_lineage_euk="; fi ) +$( if [ ! -z ${VIASH_PAR_AUTO_LINEAGE_PROK+x} ]; then echo "${VIASH_PAR_AUTO_LINEAGE_PROK}" | sed "s#'#'\"'\"'#g;s#.*#par_auto_lineage_prok='&'#" ; else echo "# par_auto_lineage_prok="; fi ) +$( if [ ! -z ${VIASH_PAR_DATASETS_VERSION+x} ]; then echo "${VIASH_PAR_DATASETS_VERSION}" | sed "s#'#'\"'\"'#g;s#.*#par_datasets_version='&'#" ; else echo "# par_datasets_version="; fi ) +$( if [ ! -z ${VIASH_PAR_AUGUSTUS+x} ]; then echo "${VIASH_PAR_AUGUSTUS}" | sed "s#'#'\"'\"'#g;s#.*#par_augustus='&'#" ; else echo "# par_augustus="; fi ) +$( if [ ! -z ${VIASH_PAR_AUGUSTUS_PARAMETERS+x} ]; then echo "${VIASH_PAR_AUGUSTUS_PARAMETERS}" | sed "s#'#'\"'\"'#g;s#.*#par_augustus_parameters='&'#" ; else echo "# par_augustus_parameters="; fi ) +$( if [ ! -z ${VIASH_PAR_AUGUSTUS_SPECIES+x} ]; then echo "${VIASH_PAR_AUGUSTUS_SPECIES}" | sed "s#'#'\"'\"'#g;s#.*#par_augustus_species='&'#" ; else echo "# par_augustus_species="; fi ) +$( if [ ! -z ${VIASH_PAR_LONG+x} ]; then echo "${VIASH_PAR_LONG}" | sed "s#'#'\"'\"'#g;s#.*#par_long='&'#" ; else echo "# par_long="; fi ) +$( if [ ! -z ${VIASH_PAR_CONTIG_BREAK+x} ]; then echo "${VIASH_PAR_CONTIG_BREAK}" | sed "s#'#'\"'\"'#g;s#.*#par_contig_break='&'#" ; else echo "# par_contig_break="; fi ) +$( if [ ! -z ${VIASH_PAR_LIMIT+x} ]; then echo "${VIASH_PAR_LIMIT}" | sed "s#'#'\"'\"'#g;s#.*#par_limit='&'#" ; else echo "# par_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_SCAFFOLD_COMPOSITION+x} ]; then echo "${VIASH_PAR_SCAFFOLD_COMPOSITION}" | sed "s#'#'\"'\"'#g;s#.*#par_scaffold_composition='&'#" ; else echo "# par_scaffold_composition="; fi ) +$( if [ ! -z ${VIASH_PAR_E_VALUE+x} ]; then echo "${VIASH_PAR_E_VALUE}" | sed "s#'#'\"'\"'#g;s#.*#par_e_value='&'#" ; else echo "# par_e_value="; fi ) +$( if [ ! -z ${VIASH_PAR_MINIPROT+x} ]; then echo "${VIASH_PAR_MINIPROT}" | sed "s#'#'\"'\"'#g;s#.*#par_miniprot='&'#" ; else echo "# par_miniprot="; fi ) +$( if [ ! -z ${VIASH_PAR_METAEUK_PARAMETERS+x} ]; then echo "${VIASH_PAR_METAEUK_PARAMETERS}" | sed "s#'#'\"'\"'#g;s#.*#par_metaeuk_parameters='&'#" ; else echo "# par_metaeuk_parameters="; fi ) +$( if [ ! -z ${VIASH_PAR_METAEUK_RERUN_PARAMETERS+x} ]; then echo "${VIASH_PAR_METAEUK_RERUN_PARAMETERS}" | sed "s#'#'\"'\"'#g;s#.*#par_metaeuk_rerun_parameters='&'#" ; else echo "# par_metaeuk_rerun_parameters="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + + +[[ "\$par_tar" == "false" ]] && unset par_tar +[[ "\$par_force" == "false" ]] && unset par_force +[[ "\$par_quiet" == "false" ]] && unset par_quiet +[[ "\$par_restart" == "false" ]] && unset par_restart +[[ "\$par_auto_lineage" == "false" ]] && unset par_auto_lineage +[[ "\$par_auto_lineage_euk" == "false" ]] && unset par_auto_lineage_euk +[[ "\$par_auto_lineage_prok" == "false" ]] && unset par_auto_lineage_prok +[[ "\$par_augustus" == "false" ]] && unset par_augustus +[[ "\$par_long" == "false" ]] && unset par_long +[[ "\$par_scaffold_composition" == "false" ]] && unset par_scaffold_composition +[[ "\$par_miniprot" == "false" ]] && unset par_miniprot + +tmp_dir=\$(mktemp -d -p "\$meta_temp_dir" busco_XXXXXXXXX) +prefix=\$(openssl rand -hex 8) + +busco \\ + --in "\$par_input" \\ + --mode "\$par_mode" \\ + --out "\$prefix" \\ + --out_path "\$tmp_dir" \\ + --opt-out-run-stats \\ + \${meta_cpus:+--cpu "\${meta_cpus}"} \\ + \${par_lineage_dataset:+--lineage_dataset "\$par_lineage_dataset"} \\ + \${par_augustus:+--augustus} \\ + \${par_augustus_parameters:+--augustus_parameters "\$par_augustus_parameters"} \\ + \${par_augustus_species:+--augustus_species "\$par_augustus_species"} \\ + \${par_auto_lineage:+--auto-lineage} \\ + \${par_auto_lineage_euk:+--auto-lineage-euk} \\ + \${par_auto_lineage_prok:+--auto-lineage-prok} \\ + \${par_contig_break:+--contig_break \$par_contig_break} \\ + \${par_datasets_version:+--datasets_version "\$par_datasets_version"} \\ + \${par_e_value:+--evalue "\$par_e_value"} \\ + \${par_force:+--force} \\ + \${par_limit:+--limit "\$par_limit"} \\ + \${par_long:+--long} \\ + \${par_metaeuk_parameters:+--metaeuk_parameters "\$par_metaeuk_parameters"} \\ + \${par_metaeuk_rerun_parameters:+--metaeuk_rerun_parameters "\$par_metaeuk_rerun_parameters"} \\ + \${par_miniprot:+--miniprot} \\ + \${par_quiet:+--quiet} \\ + \${par_restart:+--restart} \\ + \${par_scaffold_composition:+--scaffold_composition} \\ + \${par_tar:+--tar} \\ + + +out_dir=\$(find "\$tmp_dir/\$prefix" -maxdepth 1 -name 'run_*') + +if [[ -n "\$par_short_summary_json" ]]; then + cp "\$out_dir/short_summary.json" "\$par_short_summary_json" +fi +if [[ -n "\$par_short_summary_txt" ]]; then + cp "\$out_dir/short_summary.txt" "\$par_short_summary_txt" +fi +if [[ -n "\$par_full_table" ]]; then + cp "\$out_dir/full_table.tsv" "\$par_full_table" +fi +if [[ -n "\$par_missing_busco_list" ]]; then + cp "\$out_dir/missing_busco_list.tsv" "\$par_missing_busco_list" +fi +if [[ -n "\$par_output_dir" ]]; then + if [[ -d "\$par_output_dir" ]]; then + rm -r "\$par_output_dir" + fi + cp -r -L "\$out_dir" "\$par_output_dir" +fi +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_SHORT_SUMMARY_JSON" ]; then + VIASH_PAR_SHORT_SUMMARY_JSON=$(ViashDockerStripAutomount "$VIASH_PAR_SHORT_SUMMARY_JSON") + fi + if [ ! -z "$VIASH_PAR_SHORT_SUMMARY_TXT" ]; then + VIASH_PAR_SHORT_SUMMARY_TXT=$(ViashDockerStripAutomount "$VIASH_PAR_SHORT_SUMMARY_TXT") + fi + if [ ! -z "$VIASH_PAR_FULL_TABLE" ]; then + VIASH_PAR_FULL_TABLE=$(ViashDockerStripAutomount "$VIASH_PAR_FULL_TABLE") + fi + if [ ! -z "$VIASH_PAR_MISSING_BUSCO_LIST" ]; then + VIASH_PAR_MISSING_BUSCO_LIST=$(ViashDockerStripAutomount "$VIASH_PAR_MISSING_BUSCO_LIST") + fi + if [ ! -z "$VIASH_PAR_OUTPUT_DIR" ]; then + VIASH_PAR_OUTPUT_DIR=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT_DIR") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_SHORT_SUMMARY_JSON" ] && [ ! -e "$VIASH_PAR_SHORT_SUMMARY_JSON" ]; then + ViashError "Output file '$VIASH_PAR_SHORT_SUMMARY_JSON' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_SHORT_SUMMARY_TXT" ] && [ ! -e "$VIASH_PAR_SHORT_SUMMARY_TXT" ]; then + ViashError "Output file '$VIASH_PAR_SHORT_SUMMARY_TXT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_FULL_TABLE" ] && [ ! -e "$VIASH_PAR_FULL_TABLE" ]; then + ViashError "Output file '$VIASH_PAR_FULL_TABLE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_MISSING_BUSCO_LIST" ] && [ ! -e "$VIASH_PAR_MISSING_BUSCO_LIST" ]; then + ViashError "Output file '$VIASH_PAR_MISSING_BUSCO_LIST' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_OUTPUT_DIR" ] && [ ! -e "$VIASH_PAR_OUTPUT_DIR" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT_DIR' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/cutadapt/.config.vsh.yaml b/target/executable/cutadapt/.config.vsh.yaml new file mode 100644 index 00000000..dff8adbe --- /dev/null +++ b/target/executable/cutadapt/.config.vsh.yaml @@ -0,0 +1,733 @@ +name: "cutadapt" +version: "main" +argument_groups: +- name: "Specify Adapters for R1" + arguments: + - type: "string" + name: "--adapter" + alternatives: + - "-a" + description: "Sequence of an adapter ligated to the 3' end (paired data:\nof the\ + \ first read). The adapter and subsequent bases are\ntrimmed. If a '$' character\ + \ is appended ('anchoring'), the\nadapter is only found if it is a suffix of\ + \ the read.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--front" + alternatives: + - "-g" + description: "Sequence of an adapter ligated to the 5' end (paired data:\nof the\ + \ first read). The adapter and any preceding bases\nare trimmed. Partial matches\ + \ at the 5' end are allowed. If\na '^' character is prepended ('anchoring'),\ + \ the adapter is\nonly found if it is a prefix of the read.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--anywhere" + alternatives: + - "-b" + description: "Sequence of an adapter that may be ligated to the 5' or 3'\nend\ + \ (paired data: of the first read). Both types of\nmatches as described under\ + \ -a and -g are allowed. If the\nfirst base of the read is part of the match,\ + \ the behavior\nis as with -g, otherwise as with -a. This option is mostly\n\ + for rescuing failed library preparations - do not use if\nyou know which end\ + \ your adapter was ligated to!\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Specify Adapters using Fasta files for R1" + arguments: + - type: "file" + name: "--adapter_fasta" + description: "Fasta file containing sequences of an adapter ligated to the 3'\ + \ end (paired data:\nof the first read). The adapter and subsequent bases are\n\ + trimmed. If a '$' character is appended ('anchoring'), the\nadapter is only\ + \ found if it is a suffix of the read.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--front_fasta" + description: "Fasta file containing sequences of an adapter ligated to the 5'\ + \ end (paired data:\nof the first read). The adapter and any preceding bases\n\ + are trimmed. Partial matches at the 5' end are allowed. If\na '^' character\ + \ is prepended ('anchoring'), the adapter is\nonly found if it is a prefix of\ + \ the read.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--anywhere_fasta" + description: "Fasta file containing sequences of an adapter that may be ligated\ + \ to the 5' or 3'\nend (paired data: of the first read). Both types of\nmatches\ + \ as described under -a and -g are allowed. If the\nfirst base of the read is\ + \ part of the match, the behavior\nis as with -g, otherwise as with -a. This\ + \ option is mostly\nfor rescuing failed library preparations - do not use if\n\ + you know which end your adapter was ligated to!\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Specify Adapters for R2" + arguments: + - type: "string" + name: "--adapter_r2" + alternatives: + - "-A" + description: "Sequence of an adapter ligated to the 3' end (paired data:\nof the\ + \ first read). The adapter and subsequent bases are\ntrimmed. If a '$' character\ + \ is appended ('anchoring'), the\nadapter is only found if it is a suffix of\ + \ the read.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--front_r2" + alternatives: + - "-G" + description: "Sequence of an adapter ligated to the 5' end (paired data:\nof the\ + \ first read). The adapter and any preceding bases\nare trimmed. Partial matches\ + \ at the 5' end are allowed. If\na '^' character is prepended ('anchoring'),\ + \ the adapter is\nonly found if it is a prefix of the read.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--anywhere_r2" + alternatives: + - "-B" + description: "Sequence of an adapter that may be ligated to the 5' or 3'\nend\ + \ (paired data: of the first read). Both types of\nmatches as described under\ + \ -a and -g are allowed. If the\nfirst base of the read is part of the match,\ + \ the behavior\nis as with -g, otherwise as with -a. This option is mostly\n\ + for rescuing failed library preparations - do not use if\nyou know which end\ + \ your adapter was ligated to!\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Specify Adapters using Fasta files for R2" + arguments: + - type: "file" + name: "--adapter_r2_fasta" + description: "Fasta file containing sequences of an adapter ligated to the 3'\ + \ end (paired data:\nof the first read). The adapter and subsequent bases are\n\ + trimmed. If a '$' character is appended ('anchoring'), the\nadapter is only\ + \ found if it is a suffix of the read.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--front_r2_fasta" + description: "Fasta file containing sequences of an adapter ligated to the 5'\ + \ end (paired data:\nof the first read). The adapter and any preceding bases\n\ + are trimmed. Partial matches at the 5' end are allowed. If\na '^' character\ + \ is prepended ('anchoring'), the adapter is\nonly found if it is a prefix of\ + \ the read.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--anywhere_r2_fasta" + description: "Fasta file containing sequences of an adapter that may be ligated\ + \ to the 5' or 3'\nend (paired data: of the first read). Both types of\nmatches\ + \ as described under -a and -g are allowed. If the\nfirst base of the read is\ + \ part of the match, the behavior\nis as with -g, otherwise as with -a. This\ + \ option is mostly\nfor rescuing failed library preparations - do not use if\n\ + you know which end your adapter was ligated to!\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Paired-end options" + arguments: + - type: "boolean_true" + name: "--pair_adapters" + description: "Treat adapters given with -a/-A etc. as pairs. Either both\nor none\ + \ are removed from each read pair.\n" + info: null + direction: "input" + - type: "string" + name: "--pair_filter" + description: "Which of the reads in a paired-end read have to match the\nfiltering\ + \ criterion in order for the pair to be filtered.\n" + info: null + required: false + choices: + - "any" + - "both" + - "first" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--interleaved" + description: "Read and/or write interleaved paired-end reads.\n" + info: null + direction: "input" +- name: "Input parameters" + arguments: + - type: "file" + name: "--input" + description: "Input fastq file for single-end reads or R1 for paired-end reads.\n" + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--input_r2" + description: "Input fastq file for R2 in the case of paired-end reads.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--error_rate" + alternatives: + - "-E" + - "--errors" + description: "Maximum allowed error rate (if 0 <= E < 1), or absolute\nnumber\ + \ of errors for full-length adapter match (if E is an\ninteger >= 1). Error\ + \ rate = no. of errors divided by\nlength of matching region. Default: 0.1 (10%).\n" + info: null + example: + - 0.1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_false" + name: "--no_indels" + description: "Allow only mismatches in alignments.\n" + info: null + direction: "input" + - type: "integer" + name: "--times" + alternatives: + - "-n" + description: "Remove up to COUNT adapters from each read. Default: 1.\n" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--overlap" + alternatives: + - "-O" + description: "Require MINLENGTH overlap between read and adapter for an\nadapter\ + \ to be found. The default is 3.\n" + info: null + example: + - 3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--match_read_wildcards" + description: "Interpret IUPAC wildcards in reads.\n" + info: null + direction: "input" + - type: "boolean_false" + name: "--no_match_adapter_wildcards" + description: "Do not interpret IUPAC wildcards in adapters.\n" + info: null + direction: "input" + - type: "string" + name: "--action" + description: "What to do if a match was found. trim: trim adapter and\nup- or\ + \ downstream sequence; retain: trim, but retain\nadapter; mask: replace with\ + \ 'N' characters; lowercase:\nconvert to lowercase; none: leave unchanged.\n\ + The default is trim.\n" + info: null + example: + - "trim" + required: false + choices: + - "trim" + - "retain" + - "mask" + - "lowercase" + - "none" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--revcomp" + alternatives: + - "--rc" + description: "Check both the read and its reverse complement for adapter\nmatches.\ + \ If match is on reverse-complemented version,\noutput that one.\n" + info: null + direction: "input" +- name: "Read modifications" + arguments: + - type: "integer" + name: "--cut" + alternatives: + - "-u" + description: "Remove LEN bases from each read (or R1 if paired; use --cut_r2\n\ + option for R2). If LEN is positive, remove bases from the\nbeginning. If LEN\ + \ is negative, remove bases from the end.\nCan be used twice if LENs have different\ + \ signs. Applied\n*before* adapter trimming.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--cut_r2" + description: "Remove LEN bases from each read (for R2). If LEN is positive, remove\ + \ bases from the\nbeginning. If LEN is negative, remove bases from the end.\n\ + Can be used twice if LENs have different signs. Applied\n*before* adapter trimming.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--nextseq_trim" + description: "NextSeq-specific quality trimming (each read). Trims also\ndark\ + \ cycles appearing as high-quality G bases.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--quality_cutoff" + alternatives: + - "-q" + description: "Trim low-quality bases from 5' and/or 3' ends of each read\nbefore\ + \ adapter removal. Applied to both reads if data is\npaired. If one value is\ + \ given, only the 3' end is trimmed.\nIf two comma-separated cutoffs are given,\ + \ the 5' end is\ntrimmed with the first cutoff, the 3' end with the second.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--quality_cutoff_r2" + alternatives: + - "-Q" + description: "Quality-trimming cutoff for R2. Default: same as for R1\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--quality_base" + description: "Assume that quality values in FASTQ are encoded as\nascii(quality\ + \ + N). This needs to be set to 64 for some\nold Illumina FASTQ files. The default\ + \ is 33.\n" + info: null + example: + - 33 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--poly_a" + description: "Trim poly-A tails" + info: null + direction: "input" + - type: "integer" + name: "--length" + alternatives: + - "-l" + description: "Shorten reads to LENGTH. Positive values remove bases at\nthe end\ + \ while negative ones remove bases at the beginning.\nThis and the following\ + \ modifications are applied after\nadapter trimming.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--trim_n" + description: "Trim N's on ends of reads." + info: null + direction: "input" + - type: "string" + name: "--length_tag" + description: "Search for TAG followed by a decimal number in the\ndescription\ + \ field of the read. Replace the decimal number\nwith the correct length of\ + \ the trimmed read. For example,\nuse --length-tag 'length=' to correct fields\ + \ like\n'length=123'.\n" + info: null + example: + - "length=" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--strip_suffix" + description: "Remove this suffix from read names if present. Can be\ngiven multiple\ + \ times.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--prefix" + alternatives: + - "-x" + description: "Add this prefix to read names. Use {name} to insert the\nname of\ + \ the matching adapter.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--suffix" + alternatives: + - "-y" + description: "Add this suffix to read names; can also include {name}\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--rename" + description: "Rename reads using TEMPLATE containing variables such as\n{id},\ + \ {adapter_name} etc. (see documentation)\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--zero_cap" + alternatives: + - "-z" + description: "Change negative quality values to zero." + info: null + direction: "input" +- name: "Filtering of processed reads" + description: "Filters are applied after above read modifications. Paired-end reads\ + \ are\nalways discarded pairwise (see also --pair_filter).\n" + arguments: + - type: "string" + name: "--minimum_length" + alternatives: + - "-m" + description: "Discard reads shorter than LEN. Default is 0.\nWhen trimming paired-end\ + \ reads, the minimum lengths for R1 and R2 can be specified separately by separating\ + \ them with a colon (:).\nIf the colon syntax is not used, the same minimum\ + \ length applies to both reads, as discussed above.\nAlso, one of the values\ + \ can be omitted to impose no restrictions.\nFor example, with -m 17:, the length\ + \ of R1 must be at least 17, but the length of R2 is ignored.\n" + info: null + example: + - "0" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--maximum_length" + alternatives: + - "-M" + description: "Discard reads longer than LEN. Default: no limit.\nFor paired reads,\ + \ see the remark for --minimum_length\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--max_n" + description: "Discard reads with more than COUNT 'N' bases. If COUNT is\na number\ + \ between 0 and 1, it is interpreted as a fraction\nof the read length.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "long" + name: "--max_expected_errors" + alternatives: + - "--max_ee" + description: "Discard reads whose expected number of errors (computed\nfrom quality\ + \ values) exceeds ERRORS.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "long" + name: "--max_average_error_rate" + alternatives: + - "--max_aer" + description: "as --max_expected_errors (see above), but divided by\nlength to\ + \ account for reads of varying length.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--discard_trimmed" + alternatives: + - "--discard" + description: "Discard reads that contain an adapter. Use also -O to\navoid discarding\ + \ too many randomly matching reads.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--discard_untrimmed" + alternatives: + - "--trimmed_only" + description: "Discard reads that do not contain an adapter.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--discard_casava" + description: "Discard reads that did not pass CASAVA filtering (header\nhas :Y:).\n" + info: null + direction: "input" +- name: "Output parameters" + arguments: + - type: "string" + name: "--report" + description: "Which type of report to print: 'full' (default) or 'minimal'.\n" + info: null + example: + - "full" + required: false + choices: + - "full" + - "minimal" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--json" + description: "Write report in JSON format to this file.\n" + info: null + direction: "input" + - type: "file" + name: "--output" + description: "Glob pattern for matching the expected output files.\nShould include\ + \ `$output_dir`.\n" + info: null + example: + - "fastq/*_001.fast[a,q]" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: true + multiple_sep: ";" + - type: "boolean_true" + name: "--fasta" + description: "Output FASTA to standard output even on FASTQ input.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--info_file" + description: "Write information about each read and its adapter matches\ninto\ + \ info.txt in the output directory.\nSee the documentation for the file format.\n" + info: null + direction: "input" +- name: "Debug" + arguments: + - type: "boolean_true" + name: "--debug" + description: "Print debug information" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Cutadapt removes adapter sequences from high-throughput sequencing reads.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "RNA-seq" +- "scRNA-seq" +- "high-throughput" +license: "MIT" +references: + doi: + - "10.14806/ej.17.1.200" +links: + repository: "https://github.com/marcelm/cutadapt" + homepage: "https://cutadapt.readthedocs.io" + documentation: "https://cutadapt.readthedocs.io" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "python:3.12" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "python" + user: false + pip: + - "cutadapt" + upgrade: true + - type: "docker" + run: + - "cutadapt --version | sed 's/\\(.*\\)/cutadapt: \"\\1\"/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/cutadapt/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/cutadapt" + executable: "target/executable/cutadapt/cutadapt" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/cutadapt/cutadapt b/target/executable/cutadapt/cutadapt new file mode 100755 index 00000000..0d2822b4 --- /dev/null +++ b/target/executable/cutadapt/cutadapt @@ -0,0 +1,2725 @@ +#!/usr/bin/env bash + +# cutadapt main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="cutadapt" +VIASH_META_FUNCTIONALITY_NAME="cutadapt" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "cutadapt main" + echo "" + echo "Cutadapt removes adapter sequences from high-throughput sequencing reads." + echo "" + echo "Specify Adapters for R1:" + echo " -a, --adapter" + echo " type: string, multiple values allowed" + echo " Sequence of an adapter ligated to the 3' end (paired data:" + echo " of the first read). The adapter and subsequent bases are" + echo " trimmed. If a '\$' character is appended ('anchoring'), the" + echo " adapter is only found if it is a suffix of the read." + echo "" + echo " -g, --front" + echo " type: string, multiple values allowed" + echo " Sequence of an adapter ligated to the 5' end (paired data:" + echo " of the first read). The adapter and any preceding bases" + echo " are trimmed. Partial matches at the 5' end are allowed. If" + echo " a '^' character is prepended ('anchoring'), the adapter is" + echo " only found if it is a prefix of the read." + echo "" + echo " -b, --anywhere" + echo " type: string, multiple values allowed" + echo " Sequence of an adapter that may be ligated to the 5' or 3'" + echo " end (paired data: of the first read). Both types of" + echo " matches as described under -a and -g are allowed. If the" + echo " first base of the read is part of the match, the behavior" + echo " is as with -g, otherwise as with -a. This option is mostly" + echo " for rescuing failed library preparations - do not use if" + echo " you know which end your adapter was ligated to!" + echo "" + echo "Specify Adapters using Fasta files for R1:" + echo " --adapter_fasta" + echo " type: file, multiple values allowed, file must exist" + echo " Fasta file containing sequences of an adapter ligated to the 3' end" + echo " (paired data:" + echo " of the first read). The adapter and subsequent bases are" + echo " trimmed. If a '\$' character is appended ('anchoring'), the" + echo " adapter is only found if it is a suffix of the read." + echo "" + echo " --front_fasta" + echo " type: file, file must exist" + echo " Fasta file containing sequences of an adapter ligated to the 5' end" + echo " (paired data:" + echo " of the first read). The adapter and any preceding bases" + echo " are trimmed. Partial matches at the 5' end are allowed. If" + echo " a '^' character is prepended ('anchoring'), the adapter is" + echo " only found if it is a prefix of the read." + echo "" + echo " --anywhere_fasta" + echo " type: file, file must exist" + echo " Fasta file containing sequences of an adapter that may be ligated to the" + echo " 5' or 3'" + echo " end (paired data: of the first read). Both types of" + echo " matches as described under -a and -g are allowed. If the" + echo " first base of the read is part of the match, the behavior" + echo " is as with -g, otherwise as with -a. This option is mostly" + echo " for rescuing failed library preparations - do not use if" + echo " you know which end your adapter was ligated to!" + echo "" + echo "Specify Adapters for R2:" + echo " -A, --adapter_r2" + echo " type: string, multiple values allowed" + echo " Sequence of an adapter ligated to the 3' end (paired data:" + echo " of the first read). The adapter and subsequent bases are" + echo " trimmed. If a '\$' character is appended ('anchoring'), the" + echo " adapter is only found if it is a suffix of the read." + echo "" + echo " -G, --front_r2" + echo " type: string, multiple values allowed" + echo " Sequence of an adapter ligated to the 5' end (paired data:" + echo " of the first read). The adapter and any preceding bases" + echo " are trimmed. Partial matches at the 5' end are allowed. If" + echo " a '^' character is prepended ('anchoring'), the adapter is" + echo " only found if it is a prefix of the read." + echo "" + echo " -B, --anywhere_r2" + echo " type: string, multiple values allowed" + echo " Sequence of an adapter that may be ligated to the 5' or 3'" + echo " end (paired data: of the first read). Both types of" + echo " matches as described under -a and -g are allowed. If the" + echo " first base of the read is part of the match, the behavior" + echo " is as with -g, otherwise as with -a. This option is mostly" + echo " for rescuing failed library preparations - do not use if" + echo " you know which end your adapter was ligated to!" + echo "" + echo "Specify Adapters using Fasta files for R2:" + echo " --adapter_r2_fasta" + echo " type: file, file must exist" + echo " Fasta file containing sequences of an adapter ligated to the 3' end" + echo " (paired data:" + echo " of the first read). The adapter and subsequent bases are" + echo " trimmed. If a '\$' character is appended ('anchoring'), the" + echo " adapter is only found if it is a suffix of the read." + echo "" + echo " --front_r2_fasta" + echo " type: file, file must exist" + echo " Fasta file containing sequences of an adapter ligated to the 5' end" + echo " (paired data:" + echo " of the first read). The adapter and any preceding bases" + echo " are trimmed. Partial matches at the 5' end are allowed. If" + echo " a '^' character is prepended ('anchoring'), the adapter is" + echo " only found if it is a prefix of the read." + echo "" + echo " --anywhere_r2_fasta" + echo " type: file, file must exist" + echo " Fasta file containing sequences of an adapter that may be ligated to the" + echo " 5' or 3'" + echo " end (paired data: of the first read). Both types of" + echo " matches as described under -a and -g are allowed. If the" + echo " first base of the read is part of the match, the behavior" + echo " is as with -g, otherwise as with -a. This option is mostly" + echo " for rescuing failed library preparations - do not use if" + echo " you know which end your adapter was ligated to!" + echo "" + echo "Paired-end options:" + echo " --pair_adapters" + echo " type: boolean_true" + echo " Treat adapters given with -a/-A etc. as pairs. Either both" + echo " or none are removed from each read pair." + echo "" + echo " --pair_filter" + echo " type: string" + echo " choices: [ any, both, first ]" + echo " Which of the reads in a paired-end read have to match the" + echo " filtering criterion in order for the pair to be filtered." + echo "" + echo " --interleaved" + echo " type: boolean_true" + echo " Read and/or write interleaved paired-end reads." + echo "" + echo "Input parameters:" + echo " --input" + echo " type: file, required parameter, file must exist" + echo " Input fastq file for single-end reads or R1 for paired-end reads." + echo "" + echo " --input_r2" + echo " type: file, file must exist" + echo " Input fastq file for R2 in the case of paired-end reads." + echo "" + echo " -E, --errors, --error_rate" + echo " type: double" + echo " example: 0.1" + echo " Maximum allowed error rate (if 0 <= E < 1), or absolute" + echo " number of errors for full-length adapter match (if E is an" + echo " integer >= 1). Error rate = no. of errors divided by" + echo " length of matching region. Default: 0.1 (10%)." + echo "" + echo " --no_indels" + echo " type: boolean_false" + echo " Allow only mismatches in alignments." + echo "" + echo " -n, --times" + echo " type: integer" + echo " example: 1" + echo " Remove up to COUNT adapters from each read. Default: 1." + echo "" + echo " -O, --overlap" + echo " type: integer" + echo " example: 3" + echo " Require MINLENGTH overlap between read and adapter for an" + echo " adapter to be found. The default is 3." + echo "" + echo " --match_read_wildcards" + echo " type: boolean_true" + echo " Interpret IUPAC wildcards in reads." + echo "" + echo " --no_match_adapter_wildcards" + echo " type: boolean_false" + echo " Do not interpret IUPAC wildcards in adapters." + echo "" + echo " --action" + echo " type: string" + echo " example: trim" + echo " choices: [ trim, retain, mask, lowercase, none ]" + echo " What to do if a match was found. trim: trim adapter and" + echo " up- or downstream sequence; retain: trim, but retain" + echo " adapter; mask: replace with 'N' characters; lowercase:" + echo " convert to lowercase; none: leave unchanged." + echo " The default is trim." + echo "" + echo " --rc, --revcomp" + echo " type: boolean_true" + echo " Check both the read and its reverse complement for adapter" + echo " matches. If match is on reverse-complemented version," + echo " output that one." + echo "" + echo "Read modifications:" + echo " -u, --cut" + echo " type: integer, multiple values allowed" + echo " Remove LEN bases from each read (or R1 if paired; use --cut_r2" + echo " option for R2). If LEN is positive, remove bases from the" + echo " beginning. If LEN is negative, remove bases from the end." + echo " Can be used twice if LENs have different signs. Applied" + echo " *before* adapter trimming." + echo "" + echo " --cut_r2" + echo " type: integer, multiple values allowed" + echo " Remove LEN bases from each read (for R2). If LEN is positive, remove" + echo " bases from the" + echo " beginning. If LEN is negative, remove bases from the end." + echo " Can be used twice if LENs have different signs. Applied" + echo " *before* adapter trimming." + echo "" + echo " --nextseq_trim" + echo " type: string" + echo " NextSeq-specific quality trimming (each read). Trims also" + echo " dark cycles appearing as high-quality G bases." + echo "" + echo " -q, --quality_cutoff" + echo " type: string" + echo " Trim low-quality bases from 5' and/or 3' ends of each read" + echo " before adapter removal. Applied to both reads if data is" + echo " paired. If one value is given, only the 3' end is trimmed." + echo " If two comma-separated cutoffs are given, the 5' end is" + echo " trimmed with the first cutoff, the 3' end with the second." + echo "" + echo " -Q, --quality_cutoff_r2" + echo " type: string" + echo " Quality-trimming cutoff for R2. Default: same as for R1" + echo "" + echo " --quality_base" + echo " type: integer" + echo " example: 33" + echo " Assume that quality values in FASTQ are encoded as" + echo " ascii(quality + N). This needs to be set to 64 for some" + echo " old Illumina FASTQ files. The default is 33." + echo "" + echo " --poly_a" + echo " type: boolean_true" + echo " Trim poly-A tails" + echo "" + echo " -l, --length" + echo " type: integer" + echo " Shorten reads to LENGTH. Positive values remove bases at" + echo " the end while negative ones remove bases at the beginning." + echo " This and the following modifications are applied after" + echo " adapter trimming." + echo "" + echo " --trim_n" + echo " type: boolean_true" + echo " Trim N's on ends of reads." + echo "" + echo " --length_tag" + echo " type: string" + echo " example: length=" + echo " Search for TAG followed by a decimal number in the" + echo " description field of the read. Replace the decimal number" + echo " with the correct length of the trimmed read. For example," + echo " use --length-tag 'length=' to correct fields like" + echo " 'length=123'." + echo "" + echo " --strip_suffix" + echo " type: string" + echo " Remove this suffix from read names if present. Can be" + echo " given multiple times." + echo "" + echo " -x, --prefix" + echo " type: string" + echo " Add this prefix to read names. Use {name} to insert the" + echo " name of the matching adapter." + echo "" + echo " -y, --suffix" + echo " type: string" + echo " Add this suffix to read names; can also include {name}" + echo "" + echo " --rename" + echo " type: string" + echo " Rename reads using TEMPLATE containing variables such as" + echo " {id}, {adapter_name} etc. (see documentation)" + echo "" + echo " -z, --zero_cap" + echo " type: boolean_true" + echo " Change negative quality values to zero." + echo "" + echo "Filtering of processed reads:" + echo " Filters are applied after above read modifications. Paired-end reads are" + echo " always discarded pairwise (see also --pair_filter)." + echo "" + echo " -m, --minimum_length" + echo " type: string" + echo " example: 0" + echo " Discard reads shorter than LEN. Default is 0." + echo " When trimming paired-end reads, the minimum lengths for R1 and R2 can be" + echo " specified separately by separating them with a colon (:)." + echo " If the colon syntax is not used, the same minimum length applies to both" + echo " reads, as discussed above." + echo " Also, one of the values can be omitted to impose no restrictions." + echo " For example, with -m 17:, the length of R1 must be at least 17, but the" + echo " length of R2 is ignored." + echo "" + echo " -M, --maximum_length" + echo " type: string" + echo " Discard reads longer than LEN. Default: no limit." + echo " For paired reads, see the remark for --minimum_length" + echo "" + echo " --max_n" + echo " type: string" + echo " Discard reads with more than COUNT 'N' bases. If COUNT is" + echo " a number between 0 and 1, it is interpreted as a fraction" + echo " of the read length." + echo "" + echo " --max_ee, --max_expected_errors" + echo " type: long" + echo " Discard reads whose expected number of errors (computed" + echo " from quality values) exceeds ERRORS." + echo "" + echo " --max_aer, --max_average_error_rate" + echo " type: long" + echo " as --max_expected_errors (see above), but divided by" + echo " length to account for reads of varying length." + echo "" + echo " --discard, --discard_trimmed" + echo " type: boolean_true" + echo " Discard reads that contain an adapter. Use also -O to" + echo " avoid discarding too many randomly matching reads." + echo "" + echo " --trimmed_only, --discard_untrimmed" + echo " type: boolean_true" + echo " Discard reads that do not contain an adapter." + echo "" + echo " --discard_casava" + echo " type: boolean_true" + echo " Discard reads that did not pass CASAVA filtering (header" + echo " has :Y:)." + echo "" + echo "Output parameters:" + echo " --report" + echo " type: string" + echo " example: full" + echo " choices: [ full, minimal ]" + echo " Which type of report to print: 'full' (default) or 'minimal'." + echo "" + echo " --json" + echo " type: boolean_true" + echo " Write report in JSON format to this file." + echo "" + echo " --output" + echo " type: file, required parameter, multiple values allowed, output, file" + echo "must exist" + echo " example: fastq/*_001.fast[a,q]" + echo " Glob pattern for matching the expected output files." + echo " Should include \`\$output_dir\`." + echo "" + echo " --fasta" + echo " type: boolean_true" + echo " Output FASTA to standard output even on FASTQ input." + echo "" + echo " --info_file" + echo " type: boolean_true" + echo " Write information about each read and its adapter matches" + echo " into info.txt in the output directory." + echo " See the documentation for the file format." + echo "" + echo "Debug:" + echo " --debug" + echo " type: boolean_true" + echo " Print debug information" +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM python:3.12 +ENTRYPOINT [] +RUN pip install --upgrade pip && \ + pip install --upgrade --no-cache-dir "cutadapt" + +RUN cutadapt --version | sed 's/\(.*\)/cutadapt: "\1"/' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component cutadapt" +LABEL org.opencontainers.image.created="2024-06-24T08:36:37Z" +LABEL org.opencontainers.image.source="https://github.com/marcelm/cutadapt" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "cutadapt main" + exit + ;; + --adapter) + if [ -z "$VIASH_PAR_ADAPTER" ]; then + VIASH_PAR_ADAPTER="$2" + else + VIASH_PAR_ADAPTER="$VIASH_PAR_ADAPTER;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --adapter. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --adapter=*) + if [ -z "$VIASH_PAR_ADAPTER" ]; then + VIASH_PAR_ADAPTER=$(ViashRemoveFlags "$1") + else + VIASH_PAR_ADAPTER="$VIASH_PAR_ADAPTER;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -a) + if [ -z "$VIASH_PAR_ADAPTER" ]; then + VIASH_PAR_ADAPTER="$2" + else + VIASH_PAR_ADAPTER="$VIASH_PAR_ADAPTER;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -a. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --front) + if [ -z "$VIASH_PAR_FRONT" ]; then + VIASH_PAR_FRONT="$2" + else + VIASH_PAR_FRONT="$VIASH_PAR_FRONT;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --front. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --front=*) + if [ -z "$VIASH_PAR_FRONT" ]; then + VIASH_PAR_FRONT=$(ViashRemoveFlags "$1") + else + VIASH_PAR_FRONT="$VIASH_PAR_FRONT;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -g) + if [ -z "$VIASH_PAR_FRONT" ]; then + VIASH_PAR_FRONT="$2" + else + VIASH_PAR_FRONT="$VIASH_PAR_FRONT;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -g. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --anywhere) + if [ -z "$VIASH_PAR_ANYWHERE" ]; then + VIASH_PAR_ANYWHERE="$2" + else + VIASH_PAR_ANYWHERE="$VIASH_PAR_ANYWHERE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --anywhere. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --anywhere=*) + if [ -z "$VIASH_PAR_ANYWHERE" ]; then + VIASH_PAR_ANYWHERE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_ANYWHERE="$VIASH_PAR_ANYWHERE;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -b) + if [ -z "$VIASH_PAR_ANYWHERE" ]; then + VIASH_PAR_ANYWHERE="$2" + else + VIASH_PAR_ANYWHERE="$VIASH_PAR_ANYWHERE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -b. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --adapter_fasta) + if [ -z "$VIASH_PAR_ADAPTER_FASTA" ]; then + VIASH_PAR_ADAPTER_FASTA="$2" + else + VIASH_PAR_ADAPTER_FASTA="$VIASH_PAR_ADAPTER_FASTA;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --adapter_fasta. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --adapter_fasta=*) + if [ -z "$VIASH_PAR_ADAPTER_FASTA" ]; then + VIASH_PAR_ADAPTER_FASTA=$(ViashRemoveFlags "$1") + else + VIASH_PAR_ADAPTER_FASTA="$VIASH_PAR_ADAPTER_FASTA;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --front_fasta) + [ -n "$VIASH_PAR_FRONT_FASTA" ] && ViashError Bad arguments for option \'--front_fasta\': \'$VIASH_PAR_FRONT_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRONT_FASTA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --front_fasta. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --front_fasta=*) + [ -n "$VIASH_PAR_FRONT_FASTA" ] && ViashError Bad arguments for option \'--front_fasta=*\': \'$VIASH_PAR_FRONT_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRONT_FASTA=$(ViashRemoveFlags "$1") + shift 1 + ;; + --anywhere_fasta) + [ -n "$VIASH_PAR_ANYWHERE_FASTA" ] && ViashError Bad arguments for option \'--anywhere_fasta\': \'$VIASH_PAR_ANYWHERE_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ANYWHERE_FASTA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --anywhere_fasta. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --anywhere_fasta=*) + [ -n "$VIASH_PAR_ANYWHERE_FASTA" ] && ViashError Bad arguments for option \'--anywhere_fasta=*\': \'$VIASH_PAR_ANYWHERE_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ANYWHERE_FASTA=$(ViashRemoveFlags "$1") + shift 1 + ;; + --adapter_r2) + if [ -z "$VIASH_PAR_ADAPTER_R2" ]; then + VIASH_PAR_ADAPTER_R2="$2" + else + VIASH_PAR_ADAPTER_R2="$VIASH_PAR_ADAPTER_R2;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --adapter_r2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --adapter_r2=*) + if [ -z "$VIASH_PAR_ADAPTER_R2" ]; then + VIASH_PAR_ADAPTER_R2=$(ViashRemoveFlags "$1") + else + VIASH_PAR_ADAPTER_R2="$VIASH_PAR_ADAPTER_R2;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -A) + if [ -z "$VIASH_PAR_ADAPTER_R2" ]; then + VIASH_PAR_ADAPTER_R2="$2" + else + VIASH_PAR_ADAPTER_R2="$VIASH_PAR_ADAPTER_R2;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -A. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --front_r2) + if [ -z "$VIASH_PAR_FRONT_R2" ]; then + VIASH_PAR_FRONT_R2="$2" + else + VIASH_PAR_FRONT_R2="$VIASH_PAR_FRONT_R2;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --front_r2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --front_r2=*) + if [ -z "$VIASH_PAR_FRONT_R2" ]; then + VIASH_PAR_FRONT_R2=$(ViashRemoveFlags "$1") + else + VIASH_PAR_FRONT_R2="$VIASH_PAR_FRONT_R2;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -G) + if [ -z "$VIASH_PAR_FRONT_R2" ]; then + VIASH_PAR_FRONT_R2="$2" + else + VIASH_PAR_FRONT_R2="$VIASH_PAR_FRONT_R2;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -G. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --anywhere_r2) + if [ -z "$VIASH_PAR_ANYWHERE_R2" ]; then + VIASH_PAR_ANYWHERE_R2="$2" + else + VIASH_PAR_ANYWHERE_R2="$VIASH_PAR_ANYWHERE_R2;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --anywhere_r2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --anywhere_r2=*) + if [ -z "$VIASH_PAR_ANYWHERE_R2" ]; then + VIASH_PAR_ANYWHERE_R2=$(ViashRemoveFlags "$1") + else + VIASH_PAR_ANYWHERE_R2="$VIASH_PAR_ANYWHERE_R2;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -B) + if [ -z "$VIASH_PAR_ANYWHERE_R2" ]; then + VIASH_PAR_ANYWHERE_R2="$2" + else + VIASH_PAR_ANYWHERE_R2="$VIASH_PAR_ANYWHERE_R2;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -B. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --adapter_r2_fasta) + [ -n "$VIASH_PAR_ADAPTER_R2_FASTA" ] && ViashError Bad arguments for option \'--adapter_r2_fasta\': \'$VIASH_PAR_ADAPTER_R2_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADAPTER_R2_FASTA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --adapter_r2_fasta. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --adapter_r2_fasta=*) + [ -n "$VIASH_PAR_ADAPTER_R2_FASTA" ] && ViashError Bad arguments for option \'--adapter_r2_fasta=*\': \'$VIASH_PAR_ADAPTER_R2_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADAPTER_R2_FASTA=$(ViashRemoveFlags "$1") + shift 1 + ;; + --front_r2_fasta) + [ -n "$VIASH_PAR_FRONT_R2_FASTA" ] && ViashError Bad arguments for option \'--front_r2_fasta\': \'$VIASH_PAR_FRONT_R2_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRONT_R2_FASTA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --front_r2_fasta. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --front_r2_fasta=*) + [ -n "$VIASH_PAR_FRONT_R2_FASTA" ] && ViashError Bad arguments for option \'--front_r2_fasta=*\': \'$VIASH_PAR_FRONT_R2_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRONT_R2_FASTA=$(ViashRemoveFlags "$1") + shift 1 + ;; + --anywhere_r2_fasta) + [ -n "$VIASH_PAR_ANYWHERE_R2_FASTA" ] && ViashError Bad arguments for option \'--anywhere_r2_fasta\': \'$VIASH_PAR_ANYWHERE_R2_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ANYWHERE_R2_FASTA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --anywhere_r2_fasta. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --anywhere_r2_fasta=*) + [ -n "$VIASH_PAR_ANYWHERE_R2_FASTA" ] && ViashError Bad arguments for option \'--anywhere_r2_fasta=*\': \'$VIASH_PAR_ANYWHERE_R2_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ANYWHERE_R2_FASTA=$(ViashRemoveFlags "$1") + shift 1 + ;; + --pair_adapters) + [ -n "$VIASH_PAR_PAIR_ADAPTERS" ] && ViashError Bad arguments for option \'--pair_adapters\': \'$VIASH_PAR_PAIR_ADAPTERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PAIR_ADAPTERS=true + shift 1 + ;; + --pair_filter) + [ -n "$VIASH_PAR_PAIR_FILTER" ] && ViashError Bad arguments for option \'--pair_filter\': \'$VIASH_PAR_PAIR_FILTER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PAIR_FILTER="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --pair_filter. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --pair_filter=*) + [ -n "$VIASH_PAR_PAIR_FILTER" ] && ViashError Bad arguments for option \'--pair_filter=*\': \'$VIASH_PAR_PAIR_FILTER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PAIR_FILTER=$(ViashRemoveFlags "$1") + shift 1 + ;; + --interleaved) + [ -n "$VIASH_PAR_INTERLEAVED" ] && ViashError Bad arguments for option \'--interleaved\': \'$VIASH_PAR_INTERLEAVED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INTERLEAVED=true + shift 1 + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --input_r2) + [ -n "$VIASH_PAR_INPUT_R2" ] && ViashError Bad arguments for option \'--input_r2\': \'$VIASH_PAR_INPUT_R2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_R2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input_r2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input_r2=*) + [ -n "$VIASH_PAR_INPUT_R2" ] && ViashError Bad arguments for option \'--input_r2=*\': \'$VIASH_PAR_INPUT_R2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_R2=$(ViashRemoveFlags "$1") + shift 1 + ;; + --error_rate) + [ -n "$VIASH_PAR_ERROR_RATE" ] && ViashError Bad arguments for option \'--error_rate\': \'$VIASH_PAR_ERROR_RATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ERROR_RATE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --error_rate. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --error_rate=*) + [ -n "$VIASH_PAR_ERROR_RATE" ] && ViashError Bad arguments for option \'--error_rate=*\': \'$VIASH_PAR_ERROR_RATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ERROR_RATE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -E) + [ -n "$VIASH_PAR_ERROR_RATE" ] && ViashError Bad arguments for option \'-E\': \'$VIASH_PAR_ERROR_RATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ERROR_RATE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -E. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --errors) + [ -n "$VIASH_PAR_ERROR_RATE" ] && ViashError Bad arguments for option \'--errors\': \'$VIASH_PAR_ERROR_RATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ERROR_RATE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --errors. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --no_indels) + [ -n "$VIASH_PAR_NO_INDELS" ] && ViashError Bad arguments for option \'--no_indels\': \'$VIASH_PAR_NO_INDELS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_INDELS=false + shift 1 + ;; + --times) + [ -n "$VIASH_PAR_TIMES" ] && ViashError Bad arguments for option \'--times\': \'$VIASH_PAR_TIMES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TIMES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --times. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --times=*) + [ -n "$VIASH_PAR_TIMES" ] && ViashError Bad arguments for option \'--times=*\': \'$VIASH_PAR_TIMES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TIMES=$(ViashRemoveFlags "$1") + shift 1 + ;; + -n) + [ -n "$VIASH_PAR_TIMES" ] && ViashError Bad arguments for option \'-n\': \'$VIASH_PAR_TIMES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TIMES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -n. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --overlap) + [ -n "$VIASH_PAR_OVERLAP" ] && ViashError Bad arguments for option \'--overlap\': \'$VIASH_PAR_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --overlap. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --overlap=*) + [ -n "$VIASH_PAR_OVERLAP" ] && ViashError Bad arguments for option \'--overlap=*\': \'$VIASH_PAR_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAP=$(ViashRemoveFlags "$1") + shift 1 + ;; + -O) + [ -n "$VIASH_PAR_OVERLAP" ] && ViashError Bad arguments for option \'-O\': \'$VIASH_PAR_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -O. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --match_read_wildcards) + [ -n "$VIASH_PAR_MATCH_READ_WILDCARDS" ] && ViashError Bad arguments for option \'--match_read_wildcards\': \'$VIASH_PAR_MATCH_READ_WILDCARDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MATCH_READ_WILDCARDS=true + shift 1 + ;; + --no_match_adapter_wildcards) + [ -n "$VIASH_PAR_NO_MATCH_ADAPTER_WILDCARDS" ] && ViashError Bad arguments for option \'--no_match_adapter_wildcards\': \'$VIASH_PAR_NO_MATCH_ADAPTER_WILDCARDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_MATCH_ADAPTER_WILDCARDS=false + shift 1 + ;; + --action) + [ -n "$VIASH_PAR_ACTION" ] && ViashError Bad arguments for option \'--action\': \'$VIASH_PAR_ACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ACTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --action. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --action=*) + [ -n "$VIASH_PAR_ACTION" ] && ViashError Bad arguments for option \'--action=*\': \'$VIASH_PAR_ACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ACTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --revcomp) + [ -n "$VIASH_PAR_REVCOMP" ] && ViashError Bad arguments for option \'--revcomp\': \'$VIASH_PAR_REVCOMP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REVCOMP=true + shift 1 + ;; + --rc) + [ -n "$VIASH_PAR_REVCOMP" ] && ViashError Bad arguments for option \'--rc\': \'$VIASH_PAR_REVCOMP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REVCOMP=true + shift 1 + ;; + --cut) + if [ -z "$VIASH_PAR_CUT" ]; then + VIASH_PAR_CUT="$2" + else + VIASH_PAR_CUT="$VIASH_PAR_CUT;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut=*) + if [ -z "$VIASH_PAR_CUT" ]; then + VIASH_PAR_CUT=$(ViashRemoveFlags "$1") + else + VIASH_PAR_CUT="$VIASH_PAR_CUT;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -u) + if [ -z "$VIASH_PAR_CUT" ]; then + VIASH_PAR_CUT="$2" + else + VIASH_PAR_CUT="$VIASH_PAR_CUT;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -u. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_r2) + if [ -z "$VIASH_PAR_CUT_R2" ]; then + VIASH_PAR_CUT_R2="$2" + else + VIASH_PAR_CUT_R2="$VIASH_PAR_CUT_R2;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_r2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_r2=*) + if [ -z "$VIASH_PAR_CUT_R2" ]; then + VIASH_PAR_CUT_R2=$(ViashRemoveFlags "$1") + else + VIASH_PAR_CUT_R2="$VIASH_PAR_CUT_R2;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --nextseq_trim) + [ -n "$VIASH_PAR_NEXTSEQ_TRIM" ] && ViashError Bad arguments for option \'--nextseq_trim\': \'$VIASH_PAR_NEXTSEQ_TRIM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NEXTSEQ_TRIM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --nextseq_trim. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --nextseq_trim=*) + [ -n "$VIASH_PAR_NEXTSEQ_TRIM" ] && ViashError Bad arguments for option \'--nextseq_trim=*\': \'$VIASH_PAR_NEXTSEQ_TRIM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NEXTSEQ_TRIM=$(ViashRemoveFlags "$1") + shift 1 + ;; + --quality_cutoff) + [ -n "$VIASH_PAR_QUALITY_CUTOFF" ] && ViashError Bad arguments for option \'--quality_cutoff\': \'$VIASH_PAR_QUALITY_CUTOFF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_CUTOFF="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --quality_cutoff. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quality_cutoff=*) + [ -n "$VIASH_PAR_QUALITY_CUTOFF" ] && ViashError Bad arguments for option \'--quality_cutoff=*\': \'$VIASH_PAR_QUALITY_CUTOFF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_CUTOFF=$(ViashRemoveFlags "$1") + shift 1 + ;; + -q) + [ -n "$VIASH_PAR_QUALITY_CUTOFF" ] && ViashError Bad arguments for option \'-q\': \'$VIASH_PAR_QUALITY_CUTOFF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_CUTOFF="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -q. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quality_cutoff_r2) + [ -n "$VIASH_PAR_QUALITY_CUTOFF_R2" ] && ViashError Bad arguments for option \'--quality_cutoff_r2\': \'$VIASH_PAR_QUALITY_CUTOFF_R2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_CUTOFF_R2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --quality_cutoff_r2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quality_cutoff_r2=*) + [ -n "$VIASH_PAR_QUALITY_CUTOFF_R2" ] && ViashError Bad arguments for option \'--quality_cutoff_r2=*\': \'$VIASH_PAR_QUALITY_CUTOFF_R2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_CUTOFF_R2=$(ViashRemoveFlags "$1") + shift 1 + ;; + -Q) + [ -n "$VIASH_PAR_QUALITY_CUTOFF_R2" ] && ViashError Bad arguments for option \'-Q\': \'$VIASH_PAR_QUALITY_CUTOFF_R2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_CUTOFF_R2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -Q. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quality_base) + [ -n "$VIASH_PAR_QUALITY_BASE" ] && ViashError Bad arguments for option \'--quality_base\': \'$VIASH_PAR_QUALITY_BASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_BASE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --quality_base. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quality_base=*) + [ -n "$VIASH_PAR_QUALITY_BASE" ] && ViashError Bad arguments for option \'--quality_base=*\': \'$VIASH_PAR_QUALITY_BASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_BASE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --poly_a) + [ -n "$VIASH_PAR_POLY_A" ] && ViashError Bad arguments for option \'--poly_a\': \'$VIASH_PAR_POLY_A\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_POLY_A=true + shift 1 + ;; + --length) + [ -n "$VIASH_PAR_LENGTH" ] && ViashError Bad arguments for option \'--length\': \'$VIASH_PAR_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --length=*) + [ -n "$VIASH_PAR_LENGTH" ] && ViashError Bad arguments for option \'--length=*\': \'$VIASH_PAR_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -l) + [ -n "$VIASH_PAR_LENGTH" ] && ViashError Bad arguments for option \'-l\': \'$VIASH_PAR_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -l. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --trim_n) + [ -n "$VIASH_PAR_TRIM_N" ] && ViashError Bad arguments for option \'--trim_n\': \'$VIASH_PAR_TRIM_N\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_N=true + shift 1 + ;; + --length_tag) + [ -n "$VIASH_PAR_LENGTH_TAG" ] && ViashError Bad arguments for option \'--length_tag\': \'$VIASH_PAR_LENGTH_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --length_tag. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --length_tag=*) + [ -n "$VIASH_PAR_LENGTH_TAG" ] && ViashError Bad arguments for option \'--length_tag=*\': \'$VIASH_PAR_LENGTH_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH_TAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + --strip_suffix) + [ -n "$VIASH_PAR_STRIP_SUFFIX" ] && ViashError Bad arguments for option \'--strip_suffix\': \'$VIASH_PAR_STRIP_SUFFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRIP_SUFFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --strip_suffix. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --strip_suffix=*) + [ -n "$VIASH_PAR_STRIP_SUFFIX" ] && ViashError Bad arguments for option \'--strip_suffix=*\': \'$VIASH_PAR_STRIP_SUFFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRIP_SUFFIX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --prefix) + [ -n "$VIASH_PAR_PREFIX" ] && ViashError Bad arguments for option \'--prefix\': \'$VIASH_PAR_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --prefix. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --prefix=*) + [ -n "$VIASH_PAR_PREFIX" ] && ViashError Bad arguments for option \'--prefix=*\': \'$VIASH_PAR_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PREFIX=$(ViashRemoveFlags "$1") + shift 1 + ;; + -x) + [ -n "$VIASH_PAR_PREFIX" ] && ViashError Bad arguments for option \'-x\': \'$VIASH_PAR_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -x. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --suffix) + [ -n "$VIASH_PAR_SUFFIX" ] && ViashError Bad arguments for option \'--suffix\': \'$VIASH_PAR_SUFFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUFFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --suffix. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --suffix=*) + [ -n "$VIASH_PAR_SUFFIX" ] && ViashError Bad arguments for option \'--suffix=*\': \'$VIASH_PAR_SUFFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUFFIX=$(ViashRemoveFlags "$1") + shift 1 + ;; + -y) + [ -n "$VIASH_PAR_SUFFIX" ] && ViashError Bad arguments for option \'-y\': \'$VIASH_PAR_SUFFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUFFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -y. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --rename) + [ -n "$VIASH_PAR_RENAME" ] && ViashError Bad arguments for option \'--rename\': \'$VIASH_PAR_RENAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RENAME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --rename. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --rename=*) + [ -n "$VIASH_PAR_RENAME" ] && ViashError Bad arguments for option \'--rename=*\': \'$VIASH_PAR_RENAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RENAME=$(ViashRemoveFlags "$1") + shift 1 + ;; + --zero_cap) + [ -n "$VIASH_PAR_ZERO_CAP" ] && ViashError Bad arguments for option \'--zero_cap\': \'$VIASH_PAR_ZERO_CAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ZERO_CAP=true + shift 1 + ;; + -z) + [ -n "$VIASH_PAR_ZERO_CAP" ] && ViashError Bad arguments for option \'-z\': \'$VIASH_PAR_ZERO_CAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ZERO_CAP=true + shift 1 + ;; + --minimum_length) + [ -n "$VIASH_PAR_MINIMUM_LENGTH" ] && ViashError Bad arguments for option \'--minimum_length\': \'$VIASH_PAR_MINIMUM_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MINIMUM_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --minimum_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --minimum_length=*) + [ -n "$VIASH_PAR_MINIMUM_LENGTH" ] && ViashError Bad arguments for option \'--minimum_length=*\': \'$VIASH_PAR_MINIMUM_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MINIMUM_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -m) + [ -n "$VIASH_PAR_MINIMUM_LENGTH" ] && ViashError Bad arguments for option \'-m\': \'$VIASH_PAR_MINIMUM_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MINIMUM_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -m. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --maximum_length) + [ -n "$VIASH_PAR_MAXIMUM_LENGTH" ] && ViashError Bad arguments for option \'--maximum_length\': \'$VIASH_PAR_MAXIMUM_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAXIMUM_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --maximum_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --maximum_length=*) + [ -n "$VIASH_PAR_MAXIMUM_LENGTH" ] && ViashError Bad arguments for option \'--maximum_length=*\': \'$VIASH_PAR_MAXIMUM_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAXIMUM_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -M) + [ -n "$VIASH_PAR_MAXIMUM_LENGTH" ] && ViashError Bad arguments for option \'-M\': \'$VIASH_PAR_MAXIMUM_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAXIMUM_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -M. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_n) + [ -n "$VIASH_PAR_MAX_N" ] && ViashError Bad arguments for option \'--max_n\': \'$VIASH_PAR_MAX_N\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_N="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_n. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_n=*) + [ -n "$VIASH_PAR_MAX_N" ] && ViashError Bad arguments for option \'--max_n=*\': \'$VIASH_PAR_MAX_N\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_N=$(ViashRemoveFlags "$1") + shift 1 + ;; + --max_expected_errors) + [ -n "$VIASH_PAR_MAX_EXPECTED_ERRORS" ] && ViashError Bad arguments for option \'--max_expected_errors\': \'$VIASH_PAR_MAX_EXPECTED_ERRORS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_EXPECTED_ERRORS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_expected_errors. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_expected_errors=*) + [ -n "$VIASH_PAR_MAX_EXPECTED_ERRORS" ] && ViashError Bad arguments for option \'--max_expected_errors=*\': \'$VIASH_PAR_MAX_EXPECTED_ERRORS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_EXPECTED_ERRORS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --max_ee) + [ -n "$VIASH_PAR_MAX_EXPECTED_ERRORS" ] && ViashError Bad arguments for option \'--max_ee\': \'$VIASH_PAR_MAX_EXPECTED_ERRORS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_EXPECTED_ERRORS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_ee. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_average_error_rate) + [ -n "$VIASH_PAR_MAX_AVERAGE_ERROR_RATE" ] && ViashError Bad arguments for option \'--max_average_error_rate\': \'$VIASH_PAR_MAX_AVERAGE_ERROR_RATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_AVERAGE_ERROR_RATE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_average_error_rate. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_average_error_rate=*) + [ -n "$VIASH_PAR_MAX_AVERAGE_ERROR_RATE" ] && ViashError Bad arguments for option \'--max_average_error_rate=*\': \'$VIASH_PAR_MAX_AVERAGE_ERROR_RATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_AVERAGE_ERROR_RATE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --max_aer) + [ -n "$VIASH_PAR_MAX_AVERAGE_ERROR_RATE" ] && ViashError Bad arguments for option \'--max_aer\': \'$VIASH_PAR_MAX_AVERAGE_ERROR_RATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_AVERAGE_ERROR_RATE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_aer. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --discard_trimmed) + [ -n "$VIASH_PAR_DISCARD_TRIMMED" ] && ViashError Bad arguments for option \'--discard_trimmed\': \'$VIASH_PAR_DISCARD_TRIMMED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISCARD_TRIMMED=true + shift 1 + ;; + --discard) + [ -n "$VIASH_PAR_DISCARD_TRIMMED" ] && ViashError Bad arguments for option \'--discard\': \'$VIASH_PAR_DISCARD_TRIMMED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISCARD_TRIMMED=true + shift 1 + ;; + --discard_untrimmed) + [ -n "$VIASH_PAR_DISCARD_UNTRIMMED" ] && ViashError Bad arguments for option \'--discard_untrimmed\': \'$VIASH_PAR_DISCARD_UNTRIMMED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISCARD_UNTRIMMED=true + shift 1 + ;; + --trimmed_only) + [ -n "$VIASH_PAR_DISCARD_UNTRIMMED" ] && ViashError Bad arguments for option \'--trimmed_only\': \'$VIASH_PAR_DISCARD_UNTRIMMED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISCARD_UNTRIMMED=true + shift 1 + ;; + --discard_casava) + [ -n "$VIASH_PAR_DISCARD_CASAVA" ] && ViashError Bad arguments for option \'--discard_casava\': \'$VIASH_PAR_DISCARD_CASAVA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISCARD_CASAVA=true + shift 1 + ;; + --report) + [ -n "$VIASH_PAR_REPORT" ] && ViashError Bad arguments for option \'--report\': \'$VIASH_PAR_REPORT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REPORT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --report. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --report=*) + [ -n "$VIASH_PAR_REPORT" ] && ViashError Bad arguments for option \'--report=*\': \'$VIASH_PAR_REPORT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REPORT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --json) + [ -n "$VIASH_PAR_JSON" ] && ViashError Bad arguments for option \'--json\': \'$VIASH_PAR_JSON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_JSON=true + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --fasta) + [ -n "$VIASH_PAR_FASTA" ] && ViashError Bad arguments for option \'--fasta\': \'$VIASH_PAR_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FASTA=true + shift 1 + ;; + --info_file) + [ -n "$VIASH_PAR_INFO_FILE" ] && ViashError Bad arguments for option \'--info_file\': \'$VIASH_PAR_INFO_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INFO_FILE=true + shift 1 + ;; + --debug) + [ -n "$VIASH_PAR_DEBUG" ] && ViashError Bad arguments for option \'--debug\': \'$VIASH_PAR_DEBUG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEBUG=true + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/cutadapt:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_PAIR_ADAPTERS+x} ]; then + VIASH_PAR_PAIR_ADAPTERS="false" +fi +if [ -z ${VIASH_PAR_INTERLEAVED+x} ]; then + VIASH_PAR_INTERLEAVED="false" +fi +if [ -z ${VIASH_PAR_NO_INDELS+x} ]; then + VIASH_PAR_NO_INDELS="true" +fi +if [ -z ${VIASH_PAR_MATCH_READ_WILDCARDS+x} ]; then + VIASH_PAR_MATCH_READ_WILDCARDS="false" +fi +if [ -z ${VIASH_PAR_NO_MATCH_ADAPTER_WILDCARDS+x} ]; then + VIASH_PAR_NO_MATCH_ADAPTER_WILDCARDS="true" +fi +if [ -z ${VIASH_PAR_REVCOMP+x} ]; then + VIASH_PAR_REVCOMP="false" +fi +if [ -z ${VIASH_PAR_POLY_A+x} ]; then + VIASH_PAR_POLY_A="false" +fi +if [ -z ${VIASH_PAR_TRIM_N+x} ]; then + VIASH_PAR_TRIM_N="false" +fi +if [ -z ${VIASH_PAR_ZERO_CAP+x} ]; then + VIASH_PAR_ZERO_CAP="false" +fi +if [ -z ${VIASH_PAR_DISCARD_TRIMMED+x} ]; then + VIASH_PAR_DISCARD_TRIMMED="false" +fi +if [ -z ${VIASH_PAR_DISCARD_UNTRIMMED+x} ]; then + VIASH_PAR_DISCARD_UNTRIMMED="false" +fi +if [ -z ${VIASH_PAR_DISCARD_CASAVA+x} ]; then + VIASH_PAR_DISCARD_CASAVA="false" +fi +if [ -z ${VIASH_PAR_JSON+x} ]; then + VIASH_PAR_JSON="false" +fi +if [ -z ${VIASH_PAR_FASTA+x} ]; then + VIASH_PAR_FASTA="false" +fi +if [ -z ${VIASH_PAR_INFO_FILE+x} ]; then + VIASH_PAR_INFO_FILE="false" +fi +if [ -z ${VIASH_PAR_DEBUG+x} ]; then + VIASH_PAR_DEBUG="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_ADAPTER_FASTA" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_ADAPTER_FASTA; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi +if [ ! -z "$VIASH_PAR_FRONT_FASTA" ] && [ ! -e "$VIASH_PAR_FRONT_FASTA" ]; then + ViashError "Input file '$VIASH_PAR_FRONT_FASTA' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_ANYWHERE_FASTA" ] && [ ! -e "$VIASH_PAR_ANYWHERE_FASTA" ]; then + ViashError "Input file '$VIASH_PAR_ANYWHERE_FASTA' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_ADAPTER_R2_FASTA" ] && [ ! -e "$VIASH_PAR_ADAPTER_R2_FASTA" ]; then + ViashError "Input file '$VIASH_PAR_ADAPTER_R2_FASTA' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_FRONT_R2_FASTA" ] && [ ! -e "$VIASH_PAR_FRONT_R2_FASTA" ]; then + ViashError "Input file '$VIASH_PAR_FRONT_R2_FASTA' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_ANYWHERE_R2_FASTA" ] && [ ! -e "$VIASH_PAR_ANYWHERE_R2_FASTA" ]; then + ViashError "Input file '$VIASH_PAR_ANYWHERE_R2_FASTA' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_INPUT_R2" ] && [ ! -e "$VIASH_PAR_INPUT_R2" ]; then + ViashError "Input file '$VIASH_PAR_INPUT_R2' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_PAIR_ADAPTERS" ]]; then + if ! [[ "$VIASH_PAR_PAIR_ADAPTERS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--pair_adapters' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_INTERLEAVED" ]]; then + if ! [[ "$VIASH_PAR_INTERLEAVED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--interleaved' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ERROR_RATE" ]]; then + if ! [[ "$VIASH_PAR_ERROR_RATE" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--error_rate' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_INDELS" ]]; then + if ! [[ "$VIASH_PAR_NO_INDELS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_indels' has to be a boolean_false. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TIMES" ]]; then + if ! [[ "$VIASH_PAR_TIMES" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--times' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OVERLAP" ]]; then + if ! [[ "$VIASH_PAR_OVERLAP" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--overlap' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MATCH_READ_WILDCARDS" ]]; then + if ! [[ "$VIASH_PAR_MATCH_READ_WILDCARDS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--match_read_wildcards' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_MATCH_ADAPTER_WILDCARDS" ]]; then + if ! [[ "$VIASH_PAR_NO_MATCH_ADAPTER_WILDCARDS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_match_adapter_wildcards' has to be a boolean_false. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_REVCOMP" ]]; then + if ! [[ "$VIASH_PAR_REVCOMP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--revcomp' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [ -n "$VIASH_PAR_CUT" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_CUT; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [ -n "$VIASH_PAR_CUT_R2" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_CUT_R2; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_r2' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [[ -n "$VIASH_PAR_QUALITY_BASE" ]]; then + if ! [[ "$VIASH_PAR_QUALITY_BASE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--quality_base' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_POLY_A" ]]; then + if ! [[ "$VIASH_PAR_POLY_A" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--poly_a' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TRIM_N" ]]; then + if ! [[ "$VIASH_PAR_TRIM_N" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--trim_n' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ZERO_CAP" ]]; then + if ! [[ "$VIASH_PAR_ZERO_CAP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--zero_cap' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_EXPECTED_ERRORS" ]]; then + if ! [[ "$VIASH_PAR_MAX_EXPECTED_ERRORS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_expected_errors' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_AVERAGE_ERROR_RATE" ]]; then + if ! [[ "$VIASH_PAR_MAX_AVERAGE_ERROR_RATE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_average_error_rate' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DISCARD_TRIMMED" ]]; then + if ! [[ "$VIASH_PAR_DISCARD_TRIMMED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--discard_trimmed' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DISCARD_UNTRIMMED" ]]; then + if ! [[ "$VIASH_PAR_DISCARD_UNTRIMMED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--discard_untrimmed' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DISCARD_CASAVA" ]]; then + if ! [[ "$VIASH_PAR_DISCARD_CASAVA" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--discard_casava' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_JSON" ]]; then + if ! [[ "$VIASH_PAR_JSON" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--json' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTPUT" ]]; then + if ! [[ "$VIASH_PAR_OUTPUT" =~ \* ]]; then + ViashError '--output' has to be a path containing a wildcard, e.g. 'output_*.txt'. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FASTA" ]]; then + if ! [[ "$VIASH_PAR_FASTA" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--fasta' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_INFO_FILE" ]]; then + if ! [[ "$VIASH_PAR_INFO_FILE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--info_file' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DEBUG" ]]; then + if ! [[ "$VIASH_PAR_DEBUG" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--debug' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# check whether value is belongs to a set of choices +if [ ! -z "$VIASH_PAR_PAIR_FILTER" ]; then + VIASH_PAR_PAIR_FILTER_CHOICES=("any;both;first") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_PAIR_FILTER_CHOICES[*]};" =~ ";$VIASH_PAR_PAIR_FILTER;" ]]; then + ViashError '--pair_filter' specified value of \'$VIASH_PAR_PAIR_FILTER\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +if [ ! -z "$VIASH_PAR_ACTION" ]; then + VIASH_PAR_ACTION_CHOICES=("trim;retain;mask;lowercase;none") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_ACTION_CHOICES[*]};" =~ ";$VIASH_PAR_ACTION;" ]]; then + ViashError '--action' specified value of \'$VIASH_PAR_ACTION\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +if [ ! -z "$VIASH_PAR_REPORT" ]; then + VIASH_PAR_REPORT_CHOICES=("full;minimal") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_REPORT_CHOICES[*]};" =~ ";$VIASH_PAR_REPORT;" ]]; then + ViashError '--report' specified value of \'$VIASH_PAR_REPORT\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_ADAPTER_FASTA" ]; then + VIASH_TEST_ADAPTER_FASTA=() + IFS=';' + for var in $VIASH_PAR_ADAPTER_FASTA; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_ADAPTER_FASTA+=( "$var" ) + done + VIASH_PAR_ADAPTER_FASTA=$(IFS=';' ; echo "${VIASH_TEST_ADAPTER_FASTA[*]}") +fi +if [ ! -z "$VIASH_PAR_FRONT_FASTA" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FRONT_FASTA")" ) + VIASH_PAR_FRONT_FASTA=$(ViashDockerAutodetectMount "$VIASH_PAR_FRONT_FASTA") +fi +if [ ! -z "$VIASH_PAR_ANYWHERE_FASTA" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_ANYWHERE_FASTA")" ) + VIASH_PAR_ANYWHERE_FASTA=$(ViashDockerAutodetectMount "$VIASH_PAR_ANYWHERE_FASTA") +fi +if [ ! -z "$VIASH_PAR_ADAPTER_R2_FASTA" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_ADAPTER_R2_FASTA")" ) + VIASH_PAR_ADAPTER_R2_FASTA=$(ViashDockerAutodetectMount "$VIASH_PAR_ADAPTER_R2_FASTA") +fi +if [ ! -z "$VIASH_PAR_FRONT_R2_FASTA" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FRONT_R2_FASTA")" ) + VIASH_PAR_FRONT_R2_FASTA=$(ViashDockerAutodetectMount "$VIASH_PAR_FRONT_R2_FASTA") +fi +if [ ! -z "$VIASH_PAR_ANYWHERE_R2_FASTA" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_ANYWHERE_R2_FASTA")" ) + VIASH_PAR_ANYWHERE_R2_FASTA=$(ViashDockerAutodetectMount "$VIASH_PAR_ANYWHERE_R2_FASTA") +fi +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_INPUT_R2" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT_R2")" ) + VIASH_PAR_INPUT_R2=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT_R2") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_TEST_OUTPUT=() + IFS=';' + for var in $VIASH_PAR_OUTPUT; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_OUTPUT+=( "$var" ) + VIASH_CHOWN_VARS+=( "$var" ) + done + VIASH_PAR_OUTPUT=$(IFS=';' ; echo "${VIASH_TEST_OUTPUT[*]}") +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-cutadapt-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_ADAPTER+x} ]; then echo "${VIASH_PAR_ADAPTER}" | sed "s#'#'\"'\"'#g;s#.*#par_adapter='&'#" ; else echo "# par_adapter="; fi ) +$( if [ ! -z ${VIASH_PAR_FRONT+x} ]; then echo "${VIASH_PAR_FRONT}" | sed "s#'#'\"'\"'#g;s#.*#par_front='&'#" ; else echo "# par_front="; fi ) +$( if [ ! -z ${VIASH_PAR_ANYWHERE+x} ]; then echo "${VIASH_PAR_ANYWHERE}" | sed "s#'#'\"'\"'#g;s#.*#par_anywhere='&'#" ; else echo "# par_anywhere="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_FASTA+x} ]; then echo "${VIASH_PAR_ADAPTER_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_adapter_fasta='&'#" ; else echo "# par_adapter_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_FRONT_FASTA+x} ]; then echo "${VIASH_PAR_FRONT_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_front_fasta='&'#" ; else echo "# par_front_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_ANYWHERE_FASTA+x} ]; then echo "${VIASH_PAR_ANYWHERE_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_anywhere_fasta='&'#" ; else echo "# par_anywhere_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_R2+x} ]; then echo "${VIASH_PAR_ADAPTER_R2}" | sed "s#'#'\"'\"'#g;s#.*#par_adapter_r2='&'#" ; else echo "# par_adapter_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_FRONT_R2+x} ]; then echo "${VIASH_PAR_FRONT_R2}" | sed "s#'#'\"'\"'#g;s#.*#par_front_r2='&'#" ; else echo "# par_front_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_ANYWHERE_R2+x} ]; then echo "${VIASH_PAR_ANYWHERE_R2}" | sed "s#'#'\"'\"'#g;s#.*#par_anywhere_r2='&'#" ; else echo "# par_anywhere_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_R2_FASTA+x} ]; then echo "${VIASH_PAR_ADAPTER_R2_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_adapter_r2_fasta='&'#" ; else echo "# par_adapter_r2_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_FRONT_R2_FASTA+x} ]; then echo "${VIASH_PAR_FRONT_R2_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_front_r2_fasta='&'#" ; else echo "# par_front_r2_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_ANYWHERE_R2_FASTA+x} ]; then echo "${VIASH_PAR_ANYWHERE_R2_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_anywhere_r2_fasta='&'#" ; else echo "# par_anywhere_r2_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_PAIR_ADAPTERS+x} ]; then echo "${VIASH_PAR_PAIR_ADAPTERS}" | sed "s#'#'\"'\"'#g;s#.*#par_pair_adapters='&'#" ; else echo "# par_pair_adapters="; fi ) +$( if [ ! -z ${VIASH_PAR_PAIR_FILTER+x} ]; then echo "${VIASH_PAR_PAIR_FILTER}" | sed "s#'#'\"'\"'#g;s#.*#par_pair_filter='&'#" ; else echo "# par_pair_filter="; fi ) +$( if [ ! -z ${VIASH_PAR_INTERLEAVED+x} ]; then echo "${VIASH_PAR_INTERLEAVED}" | sed "s#'#'\"'\"'#g;s#.*#par_interleaved='&'#" ; else echo "# par_interleaved="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_R2+x} ]; then echo "${VIASH_PAR_INPUT_R2}" | sed "s#'#'\"'\"'#g;s#.*#par_input_r2='&'#" ; else echo "# par_input_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_ERROR_RATE+x} ]; then echo "${VIASH_PAR_ERROR_RATE}" | sed "s#'#'\"'\"'#g;s#.*#par_error_rate='&'#" ; else echo "# par_error_rate="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_INDELS+x} ]; then echo "${VIASH_PAR_NO_INDELS}" | sed "s#'#'\"'\"'#g;s#.*#par_no_indels='&'#" ; else echo "# par_no_indels="; fi ) +$( if [ ! -z ${VIASH_PAR_TIMES+x} ]; then echo "${VIASH_PAR_TIMES}" | sed "s#'#'\"'\"'#g;s#.*#par_times='&'#" ; else echo "# par_times="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAP+x} ]; then echo "${VIASH_PAR_OVERLAP}" | sed "s#'#'\"'\"'#g;s#.*#par_overlap='&'#" ; else echo "# par_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_MATCH_READ_WILDCARDS+x} ]; then echo "${VIASH_PAR_MATCH_READ_WILDCARDS}" | sed "s#'#'\"'\"'#g;s#.*#par_match_read_wildcards='&'#" ; else echo "# par_match_read_wildcards="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_MATCH_ADAPTER_WILDCARDS+x} ]; then echo "${VIASH_PAR_NO_MATCH_ADAPTER_WILDCARDS}" | sed "s#'#'\"'\"'#g;s#.*#par_no_match_adapter_wildcards='&'#" ; else echo "# par_no_match_adapter_wildcards="; fi ) +$( if [ ! -z ${VIASH_PAR_ACTION+x} ]; then echo "${VIASH_PAR_ACTION}" | sed "s#'#'\"'\"'#g;s#.*#par_action='&'#" ; else echo "# par_action="; fi ) +$( if [ ! -z ${VIASH_PAR_REVCOMP+x} ]; then echo "${VIASH_PAR_REVCOMP}" | sed "s#'#'\"'\"'#g;s#.*#par_revcomp='&'#" ; else echo "# par_revcomp="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT+x} ]; then echo "${VIASH_PAR_CUT}" | sed "s#'#'\"'\"'#g;s#.*#par_cut='&'#" ; else echo "# par_cut="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_R2+x} ]; then echo "${VIASH_PAR_CUT_R2}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_r2='&'#" ; else echo "# par_cut_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_NEXTSEQ_TRIM+x} ]; then echo "${VIASH_PAR_NEXTSEQ_TRIM}" | sed "s#'#'\"'\"'#g;s#.*#par_nextseq_trim='&'#" ; else echo "# par_nextseq_trim="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALITY_CUTOFF+x} ]; then echo "${VIASH_PAR_QUALITY_CUTOFF}" | sed "s#'#'\"'\"'#g;s#.*#par_quality_cutoff='&'#" ; else echo "# par_quality_cutoff="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALITY_CUTOFF_R2+x} ]; then echo "${VIASH_PAR_QUALITY_CUTOFF_R2}" | sed "s#'#'\"'\"'#g;s#.*#par_quality_cutoff_r2='&'#" ; else echo "# par_quality_cutoff_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALITY_BASE+x} ]; then echo "${VIASH_PAR_QUALITY_BASE}" | sed "s#'#'\"'\"'#g;s#.*#par_quality_base='&'#" ; else echo "# par_quality_base="; fi ) +$( if [ ! -z ${VIASH_PAR_POLY_A+x} ]; then echo "${VIASH_PAR_POLY_A}" | sed "s#'#'\"'\"'#g;s#.*#par_poly_a='&'#" ; else echo "# par_poly_a="; fi ) +$( if [ ! -z ${VIASH_PAR_LENGTH+x} ]; then echo "${VIASH_PAR_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_length='&'#" ; else echo "# par_length="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_N+x} ]; then echo "${VIASH_PAR_TRIM_N}" | sed "s#'#'\"'\"'#g;s#.*#par_trim_n='&'#" ; else echo "# par_trim_n="; fi ) +$( if [ ! -z ${VIASH_PAR_LENGTH_TAG+x} ]; then echo "${VIASH_PAR_LENGTH_TAG}" | sed "s#'#'\"'\"'#g;s#.*#par_length_tag='&'#" ; else echo "# par_length_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_STRIP_SUFFIX+x} ]; then echo "${VIASH_PAR_STRIP_SUFFIX}" | sed "s#'#'\"'\"'#g;s#.*#par_strip_suffix='&'#" ; else echo "# par_strip_suffix="; fi ) +$( if [ ! -z ${VIASH_PAR_PREFIX+x} ]; then echo "${VIASH_PAR_PREFIX}" | sed "s#'#'\"'\"'#g;s#.*#par_prefix='&'#" ; else echo "# par_prefix="; fi ) +$( if [ ! -z ${VIASH_PAR_SUFFIX+x} ]; then echo "${VIASH_PAR_SUFFIX}" | sed "s#'#'\"'\"'#g;s#.*#par_suffix='&'#" ; else echo "# par_suffix="; fi ) +$( if [ ! -z ${VIASH_PAR_RENAME+x} ]; then echo "${VIASH_PAR_RENAME}" | sed "s#'#'\"'\"'#g;s#.*#par_rename='&'#" ; else echo "# par_rename="; fi ) +$( if [ ! -z ${VIASH_PAR_ZERO_CAP+x} ]; then echo "${VIASH_PAR_ZERO_CAP}" | sed "s#'#'\"'\"'#g;s#.*#par_zero_cap='&'#" ; else echo "# par_zero_cap="; fi ) +$( if [ ! -z ${VIASH_PAR_MINIMUM_LENGTH+x} ]; then echo "${VIASH_PAR_MINIMUM_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_minimum_length='&'#" ; else echo "# par_minimum_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MAXIMUM_LENGTH+x} ]; then echo "${VIASH_PAR_MAXIMUM_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_maximum_length='&'#" ; else echo "# par_maximum_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_N+x} ]; then echo "${VIASH_PAR_MAX_N}" | sed "s#'#'\"'\"'#g;s#.*#par_max_n='&'#" ; else echo "# par_max_n="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_EXPECTED_ERRORS+x} ]; then echo "${VIASH_PAR_MAX_EXPECTED_ERRORS}" | sed "s#'#'\"'\"'#g;s#.*#par_max_expected_errors='&'#" ; else echo "# par_max_expected_errors="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_AVERAGE_ERROR_RATE+x} ]; then echo "${VIASH_PAR_MAX_AVERAGE_ERROR_RATE}" | sed "s#'#'\"'\"'#g;s#.*#par_max_average_error_rate='&'#" ; else echo "# par_max_average_error_rate="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARD_TRIMMED+x} ]; then echo "${VIASH_PAR_DISCARD_TRIMMED}" | sed "s#'#'\"'\"'#g;s#.*#par_discard_trimmed='&'#" ; else echo "# par_discard_trimmed="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARD_UNTRIMMED+x} ]; then echo "${VIASH_PAR_DISCARD_UNTRIMMED}" | sed "s#'#'\"'\"'#g;s#.*#par_discard_untrimmed='&'#" ; else echo "# par_discard_untrimmed="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARD_CASAVA+x} ]; then echo "${VIASH_PAR_DISCARD_CASAVA}" | sed "s#'#'\"'\"'#g;s#.*#par_discard_casava='&'#" ; else echo "# par_discard_casava="; fi ) +$( if [ ! -z ${VIASH_PAR_REPORT+x} ]; then echo "${VIASH_PAR_REPORT}" | sed "s#'#'\"'\"'#g;s#.*#par_report='&'#" ; else echo "# par_report="; fi ) +$( if [ ! -z ${VIASH_PAR_JSON+x} ]; then echo "${VIASH_PAR_JSON}" | sed "s#'#'\"'\"'#g;s#.*#par_json='&'#" ; else echo "# par_json="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_FASTA+x} ]; then echo "${VIASH_PAR_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_fasta='&'#" ; else echo "# par_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_INFO_FILE+x} ]; then echo "${VIASH_PAR_INFO_FILE}" | sed "s#'#'\"'\"'#g;s#.*#par_info_file='&'#" ; else echo "# par_info_file="; fi ) +$( if [ ! -z ${VIASH_PAR_DEBUG+x} ]; then echo "${VIASH_PAR_DEBUG}" | sed "s#'#'\"'\"'#g;s#.*#par_debug='&'#" ; else echo "# par_debug="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +function debug { + [[ "\$par_debug" == "true" ]] && echo "DEBUG: \$@" +} + +output_dir=\$(dirname \$par_output) +[[ ! -d \$output_dir ]] && mkdir -p \$output_dir + +# Init +########################################################### + +echo ">> Paired-end data or not?" + +mode="" +if [[ -z \$par_input_r2 ]]; then + mode="se" + echo " Single end" + input="\$par_input" +else + echo " Paired end" + mode="pe" + input="\$par_input \$par_input_r2" +fi + +# Adapter arguments +# - paired and single-end +# - string and fasta +########################################################### + +function add_flags { + local arg=\$1 + local flag=\$2 + local prefix=\$3 + [[ -z \$prefix ]] && prefix="" + + # This function should not be called if the input is empty + # but check for it just in case + if [[ -z \$arg ]]; then + return + fi + + local output="" + IFS=';' read -r -a array <<< "\$arg" + for a in "\${array[@]}"; do + output="\$output \$flag \$prefix\$a" + done + echo \$output +} + +debug ">> Parsing arguments dealing with adapters" +adapter_args=\$(echo \\ + \${par_adapter:+\$(add_flags "\$par_adapter" "--adapter")} \\ + \${par_adapter_fasta:+\$(add_flags "\$par_adapter_fasta" "--adapter" "file:")} \\ + \${par_front:+\$(add_flags "\$par_front" "--front")} \\ + \${par_front_fasta:+\$(add_flags "\$par_front_fasta" "--front" "file:")} \\ + \${par_anywhere:+\$(add_flags "\$par_anywhere" "--anywhere")} \\ + \${par_anywhere_fasta:+\$(add_flags "\$par_anywhere_fasta" "--anywhere" "file:")} \\ + \${par_adapter_r2:+\$(add_flags "\$par_adapter_r2" "-A")} \\ + \${par_adapter_fasta_r2:+\$(add_flags "\$par_adapter_fasta_r2" "-A" "file:")} \\ + \${par_front_r2:+\$(add_flags "\$par_front_r2" "-G")} \\ + \${par_front_fasta_r2:+\$(add_flags "\$par_front_fasta_r2" "-G" "file:")} \\ + \${par_anywhere_r2:+\$(add_flags "\$par_anywhere_r2" "-B")} \\ + \${par_anywhere_fasta_r2:+\$(add_flags "\$par_anywhere_fasta_r2" "-B" "file:")} \\ +) + +debug "Arguments to cutadapt:" +debug "\$adapter_args" +debug + +# Paired-end options +########################################################### +echo ">> Parsing arguments for paired-end reads" +[[ "\$par_pair_adapters" == "false" ]] && unset par_pair_adapters +[[ "\$par_interleaved" == "false" ]] && unset par_interleaved + +paired_args=\$(echo \\ + \${par_pair_adapters:+--pair-adapters} \\ + \${par_pair_filter:+--pair-filter "\${par_pair_filter}"} \\ + \${par_interleaved:+--interleaved} +) +debug "Arguments to cutadapt:" +debug \$paired_args +debug + +# Input arguments +########################################################### +echo ">> Parsing input arguments" +[[ "\$par_no_indels" == "true" ]] && unset par_no_indels +[[ "\$par_match_read_wildcards" == "false" ]] && unset par_match_read_wildcards +[[ "\$par_no_match_adapter_wildcards" == "true" ]] && unset par_no_match_adapter_wildcards +[[ "\$par_revcomp" == "false" ]] && unset par_revcomp + +input_args=\$(echo \\ + \${par_error_rate:+--error-rate "\${par_error_rate}"} \\ + \${par_no_indels:+--no-indels} \\ + \${par_times:+--times "\${par_times}"} \\ + \${par_overlap:+--overlap "\${par_overlap}"} \\ + \${par_match_read_wildcards:+--match-read-wildcards} \\ + \${par_no_match_adapter_wildcards:+--no-match-adapter-wildcards} \\ + \${par_action:+--action "\${par_action}"} \\ + \${par_revcomp:+--revcomp} \\ +) +debug "Arguments to cutadapt:" +debug \$input_args +debug + +# Read modifications +########################################################### +echo ">> Parsing read modification arguments" +[[ "\$par_poly_a" == "false" ]] && unset par_poly_a +[[ "\$par_trim_n" == "false" ]] && unset par_trim_n +[[ "\$par_zero_cap" == "false" ]] && unset par_zero_cap + +mod_args=\$(echo \\ + \${par_cut:+--cut "\${par_cut}"} \\ + \${par_cut_r2:+--cut_r2 "\${par_cut_r2}"} \\ + \${par_nextseq_trim:+--nextseq-trim "\${par_nextseq_trim}"} \\ + \${par_quality_cutoff:+--quality-cutoff "\${par_quality_cutoff}"} \\ + \${par_quality_cutoff_r2:+--quality-cutoff_r2 "\${par_quality_cutoff_r2}"} \\ + \${par_quality_base:+--quality-base "\${par_quality_base}"} \\ + \${par_poly_a:+--poly-a} \\ + \${par_length:+--length "\${par_length}"} \\ + \${par_trim_n:+--trim-n} \\ + \${par_length_tag:+--length-tag "\${par_length_tag}"} \\ + \${par_strip_suffix:+--strip-suffix "\${par_strip_suffix}"} \\ + \${par_prefix:+--prefix "\${par_prefix}"} \\ + \${par_suffix:+--suffix "\${par_suffix}"} \\ + \${par_rename:+--rename "\${par_rename}"} \\ + \${par_zero_cap:+--zero-cap} \\ +) +debug "Arguments to cutadapt:" +debug \$mod_args +debug + +# Filtering of processed reads arguments +########################################################### +echo ">> Filtering of processed reads arguments" +[[ "\$par_discard_trimmed" == "false" ]] && unset par_discard_trimmed +[[ "\$par_discard_untrimmed" == "false" ]] && unset par_discard_untrimmed +[[ "\$par_discard_casava" == "false" ]] && unset par_discard_casava + +# Parse and transform the minimum and maximum length arguments +[[ -z \$par_minimum_length ]] + +filter_args=\$(echo \\ + \${par_minimum_length:+--minimum-length "\${par_minimum_length}"} \\ + \${par_maximum_length:+--maximum-length "\${par_maximum_length}"} \\ + \${par_max_n:+--max-n "\${par_max_n}"} \\ + \${par_max_expected_errors:+--max-expected-errors "\${par_max_expected_errors}"} \\ + \${par_max_average_error_rate:+--max-average-error-rate "\${par_max_average_error_rate}"} \\ + \${par_discard_trimmed:+--discard-trimmed} \\ + \${par_discard_untrimmed:+--discard-untrimmed} \\ + \${par_discard_casava:+--discard-casava} \\ +) +debug "Arguments to cutadapt:" +debug \$filter_args +debug + +# Optional output arguments +########################################################### +echo ">> Optional arguments" +[[ "\$par_json" == "false" ]] && unset par_json +[[ "\$par_fasta" == "false" ]] && unset par_fasta +[[ "\$par_info_file" == "false" ]] && unset par_info_file + +optional_output_args=\$(echo \\ + \${par_report:+--report "\${par_report}"} \\ + \${par_json:+--json "report.json"} \\ + \${par_fasta:+--fasta} \\ + \${par_info_file:+--info-file "info.txt"} \\ +) + +debug "Arguments to cutadapt:" +debug \$optional_output_args +debug + +# Output arguments +# We write the output to a directory rather than +# individual files. +########################################################### + +if [[ -z \$par_fasta ]]; then + ext="fastq" +else + ext="fasta" +fi + +if [ \$mode = "se" ]; then + output_args=\$(echo \\ + --output "\$output_dir/{name}_001.\$ext" \\ + ) +else + output_args=\$(echo \\ + --output "\$output_dir/{name}_R1_001.\$ext" \\ + --paired-output "\$output_dir/{name}_R2_001.\$ext" \\ + ) +fi + +debug "Arguments to cutadapt:" +debug \$output_args +debug + +# Full CLI +# Set the --cores argument to 0 unless meta_cpus is set +########################################################### +echo ">> Running cutadapt" +par_cpus=0 +[[ ! -z \$meta_cpus ]] && par_cpus=\$meta_cpus + +cli=\$(echo \\ + \$input \\ + \$adapter_args \\ + \$paired_args \\ + \$input_args \\ + \$mod_args \\ + \$filter_args \\ + \$optional_output_args \\ + \$output_args \\ + --cores \$par_cpus +) + +debug ">> Full CLI to be run:" +debug cutadapt \$cli | sed -e 's/--/\\r\\n --/g' +debug + +cutadapt \$cli +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_ADAPTER_FASTA" ]; then + unset VIASH_TEST_ADAPTER_FASTA + IFS=';' + for var in $VIASH_PAR_ADAPTER_FASTA; do + unset IFS + if [ -z "$VIASH_TEST_ADAPTER_FASTA" ]; then + VIASH_TEST_ADAPTER_FASTA="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_ADAPTER_FASTA="$VIASH_TEST_ADAPTER_FASTA;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_ADAPTER_FASTA="$VIASH_TEST_ADAPTER_FASTA" + fi + if [ ! -z "$VIASH_PAR_FRONT_FASTA" ]; then + VIASH_PAR_FRONT_FASTA=$(ViashDockerStripAutomount "$VIASH_PAR_FRONT_FASTA") + fi + if [ ! -z "$VIASH_PAR_ANYWHERE_FASTA" ]; then + VIASH_PAR_ANYWHERE_FASTA=$(ViashDockerStripAutomount "$VIASH_PAR_ANYWHERE_FASTA") + fi + if [ ! -z "$VIASH_PAR_ADAPTER_R2_FASTA" ]; then + VIASH_PAR_ADAPTER_R2_FASTA=$(ViashDockerStripAutomount "$VIASH_PAR_ADAPTER_R2_FASTA") + fi + if [ ! -z "$VIASH_PAR_FRONT_R2_FASTA" ]; then + VIASH_PAR_FRONT_R2_FASTA=$(ViashDockerStripAutomount "$VIASH_PAR_FRONT_R2_FASTA") + fi + if [ ! -z "$VIASH_PAR_ANYWHERE_R2_FASTA" ]; then + VIASH_PAR_ANYWHERE_R2_FASTA=$(ViashDockerStripAutomount "$VIASH_PAR_ANYWHERE_R2_FASTA") + fi + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_INPUT_R2" ]; then + VIASH_PAR_INPUT_R2=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT_R2") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && ! compgen -G "$VIASH_PAR_OUTPUT" > /dev/null; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/falco/.config.vsh.yaml b/target/executable/falco/.config.vsh.yaml new file mode 100644 index 00000000..c27d4bdd --- /dev/null +++ b/target/executable/falco/.config.vsh.yaml @@ -0,0 +1,330 @@ +name: "falco" +version: "main" +argument_groups: +- name: "Input arguments" + arguments: + - type: "file" + name: "--input" + description: "input fastq files" + info: null + example: + - "input1.fastq;input2.fastq" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Run arguments" + arguments: + - type: "boolean_true" + name: "--nogroup" + description: "Disable grouping of bases for reads >50bp. \nAll reports will show\ + \ data for every base in \nthe read. WARNING: When using this option, \nyour\ + \ plots may end up a ridiculous size. You \nhave been warned!\n" + info: null + direction: "input" + - type: "file" + name: "--contaminents" + description: "Specifies a non-default file which contains \nthe list of contaminants\ + \ to screen \noverrepresented sequences against. The file \nmust contain sets\ + \ of named contaminants in \nthe form name[tab]sequence. Lines prefixed \nwith\ + \ a hash will be ignored. Default: \nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/contaminant_list.txt\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--adapters" + description: "Specifies a non-default file which contains \nthe list of adapter\ + \ sequences which will be \nexplicity searched against the library. The \nfile\ + \ must contain sets of named adapters in \nthe form name[tab]sequence. Lines\ + \ prefixed \nwith a hash will be ignored. Default:\nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/adapter_list.txt\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--limits" + description: "Specifies a non-default file which contains \na set of criteria\ + \ which will be used to \ndetermine the warn/error limits for the \nvarious\ + \ modules. This file can also be used \nto selectively remove some modules from\ + \ the \noutput all together. The format needs to \nmirror the default limits.txt\ + \ file found in \nthe Configuration folder. Default: \nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/limits.txt\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--subsample" + alternatives: + - "-s" + description: "[Falco only] makes falco faster (but \npossibly less accurate) by\ + \ only processing \nreads that are a multiple of this value (using \n0-based\ + \ indexing to number reads).\n" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--bisulfite" + alternatives: + - "-b" + description: "[Falco only] reads are whole genome \nbisulfite sequencing, and\ + \ more Ts and fewer \nCs are therefore expected and will be \naccounted for\ + \ in base content.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--reverse_complliment" + alternatives: + - "-r" + description: "[Falco only] The input is a \nreverse-complement. All modules will\ + \ be \ntested by swapping A/T and C/G\n" + info: null + direction: "input" +- name: "Output arguments" + arguments: + - type: "file" + name: "--outdir" + alternatives: + - "-o" + description: "Create all output files in the specified \noutput directory. FALCO-SPECIFIC:\ + \ If the \ndirectory does not exists, the program will \ncreate it.\n" + info: null + example: + - "output" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--format" + alternatives: + - "-f" + description: "Bypasses the normal sequence file format \ndetection and forces\ + \ the program to use the \nspecified format. Validformats are bam, sam, \nbam_mapped,\ + \ sam_mapped, fastq, fq, fastq.gz \nor fq.gz.\n" + info: null + required: false + choices: + - "bam" + - "sam" + - "bam_mapped" + - "sam_mapped" + - "fastq" + - "fq" + - "fastq.gz" + - "fq.gz" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--data_filename" + alternatives: + - "-D" + description: "[Falco only] Specify filename for FastQC \ndata output (TXT). If\ + \ not specified, it will \nbe called fastq_data.txt in either the input \nfile's\ + \ directory or the one specified in the \n--output flag. Only available when\ + \ running \nfalco with a single input.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--report_filename" + alternatives: + - "-R" + description: "[Falco only] Specify filename for FastQC \nreport output (HTML).\ + \ If not specified, it \nwill be called fastq_report.html in either \nthe input\ + \ file's directory or the one \nspecified in the --output flag. Only \navailable\ + \ when running falco with a single \ninput.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--summary_filename" + alternatives: + - "-S" + description: "[Falco only] Specify filename for the short \nsummary output (TXT).\ + \ If not specified, it \nwill be called fastq_report.html in either \nthe input\ + \ file's directory or the one \nspecified in the --output flag. Only \navailable\ + \ when running falco with a single \ninput.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "A C++ drop-in replacement of FastQC to assess the quality of sequence\ + \ read data" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "qc" +- "fastqc" +- "sequencing" +license: "GPL-3.0" +references: + doi: + - "10.12688/f1000research.21142.2" +links: + repository: "https://github.com/smithlabcode/falco" + documentation: "https://falco.readthedocs.io/en/latest/" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "debian:trixie-slim" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "apt" + packages: + - "wget" + - "build-essential" + - "g++" + - "zlib1g-dev" + - "procps" + interactive: false + - type: "docker" + run: + - "wget https://github.com/smithlabcode/falco/releases/download/v1.2.2/falco-1.2.2.tar.gz\ + \ -O /tmp/falco.tar.gz && \\\ncd /tmp && \\\ntar xvf falco.tar.gz && \\\ncd\ + \ falco-1.2.2 && \\\n./configure && \\\nmake all && \\\nmake install\n" + - type: "docker" + run: + - "echo \"falco: \\\"$(falco -v | sed -n 's/^falco //p')\\\"\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/falco/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/falco" + executable: "target/executable/falco/falco" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/falco/falco b/target/executable/falco/falco new file mode 100755 index 00000000..7b0c1af7 --- /dev/null +++ b/target/executable/falco/falco @@ -0,0 +1,1503 @@ +#!/usr/bin/env bash + +# falco main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="falco" +VIASH_META_FUNCTIONALITY_NAME="falco" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "falco main" + echo "" + echo "A C++ drop-in replacement of FastQC to assess the quality of sequence read data" + echo "" + echo "Input arguments:" + echo " --input" + echo " type: file, required parameter, multiple values allowed, file must exist" + echo " example: input1.fastq;input2.fastq" + echo " input fastq files" + echo "" + echo "Run arguments:" + echo " --nogroup" + echo " type: boolean_true" + echo " Disable grouping of bases for reads >50bp." + echo " All reports will show data for every base in" + echo " the read. WARNING: When using this option," + echo " your plots may end up a ridiculous size. You" + echo " have been warned!" + echo "" + echo " --contaminents" + echo " type: file, file must exist" + echo " Specifies a non-default file which contains" + echo " the list of contaminants to screen" + echo " overrepresented sequences against. The file" + echo " must contain sets of named contaminants in" + echo " the form name[tab]sequence. Lines prefixed" + echo " with a hash will be ignored. Default:" + echo " " + echo "https://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/contaminant_list.txt" + echo "" + echo " --adapters" + echo " type: file, file must exist" + echo " Specifies a non-default file which contains" + echo " the list of adapter sequences which will be" + echo " explicity searched against the library. The" + echo " file must contain sets of named adapters in" + echo " the form name[tab]sequence. Lines prefixed" + echo " with a hash will be ignored. Default:" + echo " " + echo "https://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/adapter_list.txt" + echo "" + echo " --limits" + echo " type: file, file must exist" + echo " Specifies a non-default file which contains" + echo " a set of criteria which will be used to" + echo " determine the warn/error limits for the" + echo " various modules. This file can also be used" + echo " to selectively remove some modules from the" + echo " output all together. The format needs to" + echo " mirror the default limits.txt file found in" + echo " the Configuration folder. Default:" + echo " " + echo "https://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/limits.txt" + echo "" + echo " -s, --subsample" + echo " type: integer" + echo " example: 10" + echo " [Falco only] makes falco faster (but" + echo " possibly less accurate) by only processing" + echo " reads that are a multiple of this value (using" + echo " 0-based indexing to number reads)." + echo "" + echo " -b, --bisulfite" + echo " type: boolean_true" + echo " [Falco only] reads are whole genome" + echo " bisulfite sequencing, and more Ts and fewer" + echo " Cs are therefore expected and will be" + echo " accounted for in base content." + echo "" + echo " -r, --reverse_complliment" + echo " type: boolean_true" + echo " [Falco only] The input is a" + echo " reverse-complement. All modules will be" + echo " tested by swapping A/T and C/G" + echo "" + echo "Output arguments:" + echo " -o, --outdir" + echo " type: file, required parameter, output, file must exist" + echo " example: output" + echo " Create all output files in the specified" + echo " output directory. FALCO-SPECIFIC: If the" + echo " directory does not exists, the program will" + echo " create it." + echo "" + echo " -f, --format" + echo " type: string" + echo " choices: [ bam, sam, bam_mapped, sam_mapped, fastq, fq, fastq.gz, fq.gz" + echo "]" + echo " Bypasses the normal sequence file format" + echo " detection and forces the program to use the" + echo " specified format. Validformats are bam, sam," + echo " bam_mapped, sam_mapped, fastq, fq, fastq.gz" + echo " or fq.gz." + echo "" + echo " -D, --data_filename" + echo " type: file, output, file must exist" + echo " [Falco only] Specify filename for FastQC" + echo " data output (TXT). If not specified, it will" + echo " be called fastq_data.txt in either the input" + echo " file's directory or the one specified in the" + echo " --output flag. Only available when running" + echo " falco with a single input." + echo "" + echo " -R, --report_filename" + echo " type: file, output, file must exist" + echo " [Falco only] Specify filename for FastQC" + echo " report output (HTML). If not specified, it" + echo " will be called fastq_report.html in either" + echo " the input file's directory or the one" + echo " specified in the --output flag. Only" + echo " available when running falco with a single" + echo " input." + echo "" + echo " -S, --summary_filename" + echo " type: file, output, file must exist" + echo " [Falco only] Specify filename for the short" + echo " summary output (TXT). If not specified, it" + echo " will be called fastq_report.html in either" + echo " the input file's directory or the one" + echo " specified in the --output flag. Only" + echo " available when running falco with a single" + echo " input." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM debian:trixie-slim +ENTRYPOINT [] +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y wget build-essential g++ zlib1g-dev procps && \ + rm -rf /var/lib/apt/lists/* + +RUN wget https://github.com/smithlabcode/falco/releases/download/v1.2.2/falco-1.2.2.tar.gz -O /tmp/falco.tar.gz && \ +cd /tmp && \ +tar xvf falco.tar.gz && \ +cd falco-1.2.2 && \ +./configure && \ +make all && \ +make install + +RUN echo "falco: \"$(falco -v | sed -n 's/^falco //p')\"" > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component falco" +LABEL org.opencontainers.image.created="2024-06-24T08:36:47Z" +LABEL org.opencontainers.image.source="https://github.com/smithlabcode/falco" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "falco main" + exit + ;; + --input) + if [ -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT="$2" + else + VIASH_PAR_INPUT="$VIASH_PAR_INPUT;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + if [ -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + else + VIASH_PAR_INPUT="$VIASH_PAR_INPUT;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --nogroup) + [ -n "$VIASH_PAR_NOGROUP" ] && ViashError Bad arguments for option \'--nogroup\': \'$VIASH_PAR_NOGROUP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NOGROUP=true + shift 1 + ;; + --contaminents) + [ -n "$VIASH_PAR_CONTAMINENTS" ] && ViashError Bad arguments for option \'--contaminents\': \'$VIASH_PAR_CONTAMINENTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CONTAMINENTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --contaminents. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --contaminents=*) + [ -n "$VIASH_PAR_CONTAMINENTS" ] && ViashError Bad arguments for option \'--contaminents=*\': \'$VIASH_PAR_CONTAMINENTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CONTAMINENTS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --adapters) + [ -n "$VIASH_PAR_ADAPTERS" ] && ViashError Bad arguments for option \'--adapters\': \'$VIASH_PAR_ADAPTERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADAPTERS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --adapters. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --adapters=*) + [ -n "$VIASH_PAR_ADAPTERS" ] && ViashError Bad arguments for option \'--adapters=*\': \'$VIASH_PAR_ADAPTERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADAPTERS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --limits) + [ -n "$VIASH_PAR_LIMITS" ] && ViashError Bad arguments for option \'--limits\': \'$VIASH_PAR_LIMITS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --limits. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --limits=*) + [ -n "$VIASH_PAR_LIMITS" ] && ViashError Bad arguments for option \'--limits=*\': \'$VIASH_PAR_LIMITS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --subsample) + [ -n "$VIASH_PAR_SUBSAMPLE" ] && ViashError Bad arguments for option \'--subsample\': \'$VIASH_PAR_SUBSAMPLE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUBSAMPLE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --subsample. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --subsample=*) + [ -n "$VIASH_PAR_SUBSAMPLE" ] && ViashError Bad arguments for option \'--subsample=*\': \'$VIASH_PAR_SUBSAMPLE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUBSAMPLE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -s) + [ -n "$VIASH_PAR_SUBSAMPLE" ] && ViashError Bad arguments for option \'-s\': \'$VIASH_PAR_SUBSAMPLE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUBSAMPLE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -s. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bisulfite) + [ -n "$VIASH_PAR_BISULFITE" ] && ViashError Bad arguments for option \'--bisulfite\': \'$VIASH_PAR_BISULFITE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BISULFITE=true + shift 1 + ;; + -b) + [ -n "$VIASH_PAR_BISULFITE" ] && ViashError Bad arguments for option \'-b\': \'$VIASH_PAR_BISULFITE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BISULFITE=true + shift 1 + ;; + --reverse_complliment) + [ -n "$VIASH_PAR_REVERSE_COMPLLIMENT" ] && ViashError Bad arguments for option \'--reverse_complliment\': \'$VIASH_PAR_REVERSE_COMPLLIMENT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REVERSE_COMPLLIMENT=true + shift 1 + ;; + -r) + [ -n "$VIASH_PAR_REVERSE_COMPLLIMENT" ] && ViashError Bad arguments for option \'-r\': \'$VIASH_PAR_REVERSE_COMPLLIMENT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REVERSE_COMPLLIMENT=true + shift 1 + ;; + --outdir) + [ -n "$VIASH_PAR_OUTDIR" ] && ViashError Bad arguments for option \'--outdir\': \'$VIASH_PAR_OUTDIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTDIR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outdir. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outdir=*) + [ -n "$VIASH_PAR_OUTDIR" ] && ViashError Bad arguments for option \'--outdir=*\': \'$VIASH_PAR_OUTDIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTDIR=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTDIR" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTDIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTDIR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --format) + [ -n "$VIASH_PAR_FORMAT" ] && ViashError Bad arguments for option \'--format\': \'$VIASH_PAR_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORMAT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --format. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --format=*) + [ -n "$VIASH_PAR_FORMAT" ] && ViashError Bad arguments for option \'--format=*\': \'$VIASH_PAR_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORMAT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_FORMAT" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORMAT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -f. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --data_filename) + [ -n "$VIASH_PAR_DATA_FILENAME" ] && ViashError Bad arguments for option \'--data_filename\': \'$VIASH_PAR_DATA_FILENAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DATA_FILENAME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --data_filename. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --data_filename=*) + [ -n "$VIASH_PAR_DATA_FILENAME" ] && ViashError Bad arguments for option \'--data_filename=*\': \'$VIASH_PAR_DATA_FILENAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DATA_FILENAME=$(ViashRemoveFlags "$1") + shift 1 + ;; + -D) + [ -n "$VIASH_PAR_DATA_FILENAME" ] && ViashError Bad arguments for option \'-D\': \'$VIASH_PAR_DATA_FILENAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DATA_FILENAME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -D. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --report_filename) + [ -n "$VIASH_PAR_REPORT_FILENAME" ] && ViashError Bad arguments for option \'--report_filename\': \'$VIASH_PAR_REPORT_FILENAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REPORT_FILENAME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --report_filename. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --report_filename=*) + [ -n "$VIASH_PAR_REPORT_FILENAME" ] && ViashError Bad arguments for option \'--report_filename=*\': \'$VIASH_PAR_REPORT_FILENAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REPORT_FILENAME=$(ViashRemoveFlags "$1") + shift 1 + ;; + -R) + [ -n "$VIASH_PAR_REPORT_FILENAME" ] && ViashError Bad arguments for option \'-R\': \'$VIASH_PAR_REPORT_FILENAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REPORT_FILENAME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -R. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --summary_filename) + [ -n "$VIASH_PAR_SUMMARY_FILENAME" ] && ViashError Bad arguments for option \'--summary_filename\': \'$VIASH_PAR_SUMMARY_FILENAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUMMARY_FILENAME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --summary_filename. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --summary_filename=*) + [ -n "$VIASH_PAR_SUMMARY_FILENAME" ] && ViashError Bad arguments for option \'--summary_filename=*\': \'$VIASH_PAR_SUMMARY_FILENAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUMMARY_FILENAME=$(ViashRemoveFlags "$1") + shift 1 + ;; + -S) + [ -n "$VIASH_PAR_SUMMARY_FILENAME" ] && ViashError Bad arguments for option \'-S\': \'$VIASH_PAR_SUMMARY_FILENAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUMMARY_FILENAME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -S. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/falco:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUTDIR+x} ]; then + ViashError '--outdir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_NOGROUP+x} ]; then + VIASH_PAR_NOGROUP="false" +fi +if [ -z ${VIASH_PAR_BISULFITE+x} ]; then + VIASH_PAR_BISULFITE="false" +fi +if [ -z ${VIASH_PAR_REVERSE_COMPLLIMENT+x} ]; then + VIASH_PAR_REVERSE_COMPLLIMENT="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_INPUT; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi +if [ ! -z "$VIASH_PAR_CONTAMINENTS" ] && [ ! -e "$VIASH_PAR_CONTAMINENTS" ]; then + ViashError "Input file '$VIASH_PAR_CONTAMINENTS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_ADAPTERS" ] && [ ! -e "$VIASH_PAR_ADAPTERS" ]; then + ViashError "Input file '$VIASH_PAR_ADAPTERS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_LIMITS" ] && [ ! -e "$VIASH_PAR_LIMITS" ]; then + ViashError "Input file '$VIASH_PAR_LIMITS' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_NOGROUP" ]]; then + if ! [[ "$VIASH_PAR_NOGROUP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--nogroup' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SUBSAMPLE" ]]; then + if ! [[ "$VIASH_PAR_SUBSAMPLE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--subsample' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BISULFITE" ]]; then + if ! [[ "$VIASH_PAR_BISULFITE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--bisulfite' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_REVERSE_COMPLLIMENT" ]]; then + if ! [[ "$VIASH_PAR_REVERSE_COMPLLIMENT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--reverse_complliment' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# check whether value is belongs to a set of choices +if [ ! -z "$VIASH_PAR_FORMAT" ]; then + VIASH_PAR_FORMAT_CHOICES=("bam;sam;bam_mapped;sam_mapped;fastq;fq;fastq.gz;fq.gz") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_FORMAT_CHOICES[*]};" =~ ";$VIASH_PAR_FORMAT;" ]]; then + ViashError '--format' specified value of \'$VIASH_PAR_FORMAT\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTDIR" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTDIR")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTDIR")" +fi +if [ ! -z "$VIASH_PAR_DATA_FILENAME" ] && [ ! -d "$(dirname "$VIASH_PAR_DATA_FILENAME")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_DATA_FILENAME")" +fi +if [ ! -z "$VIASH_PAR_REPORT_FILENAME" ] && [ ! -d "$(dirname "$VIASH_PAR_REPORT_FILENAME")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_REPORT_FILENAME")" +fi +if [ ! -z "$VIASH_PAR_SUMMARY_FILENAME" ] && [ ! -d "$(dirname "$VIASH_PAR_SUMMARY_FILENAME")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_SUMMARY_FILENAME")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_TEST_INPUT=() + IFS=';' + for var in $VIASH_PAR_INPUT; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_INPUT+=( "$var" ) + done + VIASH_PAR_INPUT=$(IFS=';' ; echo "${VIASH_TEST_INPUT[*]}") +fi +if [ ! -z "$VIASH_PAR_CONTAMINENTS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_CONTAMINENTS")" ) + VIASH_PAR_CONTAMINENTS=$(ViashDockerAutodetectMount "$VIASH_PAR_CONTAMINENTS") +fi +if [ ! -z "$VIASH_PAR_ADAPTERS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_ADAPTERS")" ) + VIASH_PAR_ADAPTERS=$(ViashDockerAutodetectMount "$VIASH_PAR_ADAPTERS") +fi +if [ ! -z "$VIASH_PAR_LIMITS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_LIMITS")" ) + VIASH_PAR_LIMITS=$(ViashDockerAutodetectMount "$VIASH_PAR_LIMITS") +fi +if [ ! -z "$VIASH_PAR_OUTDIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTDIR")" ) + VIASH_PAR_OUTDIR=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTDIR") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTDIR" ) +fi +if [ ! -z "$VIASH_PAR_DATA_FILENAME" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_DATA_FILENAME")" ) + VIASH_PAR_DATA_FILENAME=$(ViashDockerAutodetectMount "$VIASH_PAR_DATA_FILENAME") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_DATA_FILENAME" ) +fi +if [ ! -z "$VIASH_PAR_REPORT_FILENAME" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REPORT_FILENAME")" ) + VIASH_PAR_REPORT_FILENAME=$(ViashDockerAutodetectMount "$VIASH_PAR_REPORT_FILENAME") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_REPORT_FILENAME" ) +fi +if [ ! -z "$VIASH_PAR_SUMMARY_FILENAME" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SUMMARY_FILENAME")" ) + VIASH_PAR_SUMMARY_FILENAME=$(ViashDockerAutodetectMount "$VIASH_PAR_SUMMARY_FILENAME") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_SUMMARY_FILENAME" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-falco-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_NOGROUP+x} ]; then echo "${VIASH_PAR_NOGROUP}" | sed "s#'#'\"'\"'#g;s#.*#par_nogroup='&'#" ; else echo "# par_nogroup="; fi ) +$( if [ ! -z ${VIASH_PAR_CONTAMINENTS+x} ]; then echo "${VIASH_PAR_CONTAMINENTS}" | sed "s#'#'\"'\"'#g;s#.*#par_contaminents='&'#" ; else echo "# par_contaminents="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTERS+x} ]; then echo "${VIASH_PAR_ADAPTERS}" | sed "s#'#'\"'\"'#g;s#.*#par_adapters='&'#" ; else echo "# par_adapters="; fi ) +$( if [ ! -z ${VIASH_PAR_LIMITS+x} ]; then echo "${VIASH_PAR_LIMITS}" | sed "s#'#'\"'\"'#g;s#.*#par_limits='&'#" ; else echo "# par_limits="; fi ) +$( if [ ! -z ${VIASH_PAR_SUBSAMPLE+x} ]; then echo "${VIASH_PAR_SUBSAMPLE}" | sed "s#'#'\"'\"'#g;s#.*#par_subsample='&'#" ; else echo "# par_subsample="; fi ) +$( if [ ! -z ${VIASH_PAR_BISULFITE+x} ]; then echo "${VIASH_PAR_BISULFITE}" | sed "s#'#'\"'\"'#g;s#.*#par_bisulfite='&'#" ; else echo "# par_bisulfite="; fi ) +$( if [ ! -z ${VIASH_PAR_REVERSE_COMPLLIMENT+x} ]; then echo "${VIASH_PAR_REVERSE_COMPLLIMENT}" | sed "s#'#'\"'\"'#g;s#.*#par_reverse_complliment='&'#" ; else echo "# par_reverse_complliment="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTDIR+x} ]; then echo "${VIASH_PAR_OUTDIR}" | sed "s#'#'\"'\"'#g;s#.*#par_outdir='&'#" ; else echo "# par_outdir="; fi ) +$( if [ ! -z ${VIASH_PAR_FORMAT+x} ]; then echo "${VIASH_PAR_FORMAT}" | sed "s#'#'\"'\"'#g;s#.*#par_format='&'#" ; else echo "# par_format="; fi ) +$( if [ ! -z ${VIASH_PAR_DATA_FILENAME+x} ]; then echo "${VIASH_PAR_DATA_FILENAME}" | sed "s#'#'\"'\"'#g;s#.*#par_data_filename='&'#" ; else echo "# par_data_filename="; fi ) +$( if [ ! -z ${VIASH_PAR_REPORT_FILENAME+x} ]; then echo "${VIASH_PAR_REPORT_FILENAME}" | sed "s#'#'\"'\"'#g;s#.*#par_report_filename='&'#" ; else echo "# par_report_filename="; fi ) +$( if [ ! -z ${VIASH_PAR_SUMMARY_FILENAME+x} ]; then echo "${VIASH_PAR_SUMMARY_FILENAME}" | sed "s#'#'\"'\"'#g;s#.*#par_summary_filename='&'#" ; else echo "# par_summary_filename="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END +#!/bin/bash + +set -eo pipefail + +[[ "\$par_nogroup" == "false" ]] && unset par_nogroup +[[ "\$par_bisulfite" == "false" ]] && unset par_bisulfite +[[ "\$par_reverse_compliment" == "false" ]] && unset par_reverse_compliment + +IFS=";" read -ra input <<< \$par_input + +\$(which falco) \\ + \${par_nogroup:+--nogroup} \\ + \${par_contaminants:+--contaminants "\$par_contaminants"} \\ + \${par_adapters:+--adapters "\$par_adapters"} \\ + \${par_limits:+--limits "\$par_limits"} \\ + \${par_subsample:+-subsample \$par_subsample} \\ + \${par_bisulfite:+-bisulfite} \\ + \${par_reverse_compliment:+-reverse-compliment} \\ + \${par_outdir:+--outdir "\$par_outdir"} \\ + \${par_format:+--format "\$par_format"} \\ + \${par_data_filename:+-data-filename "\$par_data_filename"} \\ + \${par_report_filename:+-report-filename "\$par_report_filename"} \\ + \${par_summary_filename:+-summary-filename "\$par_summary_filename"} \\ + \${input[*]} +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + unset VIASH_TEST_INPUT + IFS=';' + for var in $VIASH_PAR_INPUT; do + unset IFS + if [ -z "$VIASH_TEST_INPUT" ]; then + VIASH_TEST_INPUT="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_INPUT="$VIASH_TEST_INPUT;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_INPUT="$VIASH_TEST_INPUT" + fi + if [ ! -z "$VIASH_PAR_CONTAMINENTS" ]; then + VIASH_PAR_CONTAMINENTS=$(ViashDockerStripAutomount "$VIASH_PAR_CONTAMINENTS") + fi + if [ ! -z "$VIASH_PAR_ADAPTERS" ]; then + VIASH_PAR_ADAPTERS=$(ViashDockerStripAutomount "$VIASH_PAR_ADAPTERS") + fi + if [ ! -z "$VIASH_PAR_LIMITS" ]; then + VIASH_PAR_LIMITS=$(ViashDockerStripAutomount "$VIASH_PAR_LIMITS") + fi + if [ ! -z "$VIASH_PAR_OUTDIR" ]; then + VIASH_PAR_OUTDIR=$(ViashDockerStripAutomount "$VIASH_PAR_OUTDIR") + fi + if [ ! -z "$VIASH_PAR_DATA_FILENAME" ]; then + VIASH_PAR_DATA_FILENAME=$(ViashDockerStripAutomount "$VIASH_PAR_DATA_FILENAME") + fi + if [ ! -z "$VIASH_PAR_REPORT_FILENAME" ]; then + VIASH_PAR_REPORT_FILENAME=$(ViashDockerStripAutomount "$VIASH_PAR_REPORT_FILENAME") + fi + if [ ! -z "$VIASH_PAR_SUMMARY_FILENAME" ]; then + VIASH_PAR_SUMMARY_FILENAME=$(ViashDockerStripAutomount "$VIASH_PAR_SUMMARY_FILENAME") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTDIR" ] && [ ! -e "$VIASH_PAR_OUTDIR" ]; then + ViashError "Output file '$VIASH_PAR_OUTDIR' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_DATA_FILENAME" ] && [ ! -e "$VIASH_PAR_DATA_FILENAME" ]; then + ViashError "Output file '$VIASH_PAR_DATA_FILENAME' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REPORT_FILENAME" ] && [ ! -e "$VIASH_PAR_REPORT_FILENAME" ]; then + ViashError "Output file '$VIASH_PAR_REPORT_FILENAME' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_SUMMARY_FILENAME" ] && [ ! -e "$VIASH_PAR_SUMMARY_FILENAME" ]; then + ViashError "Output file '$VIASH_PAR_SUMMARY_FILENAME' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/fastp/.config.vsh.yaml b/target/executable/fastp/.config.vsh.yaml new file mode 100644 index 00000000..def2d877 --- /dev/null +++ b/target/executable/fastp/.config.vsh.yaml @@ -0,0 +1,1091 @@ +name: "fastp" +version: "main" +argument_groups: +- name: "Inputs" + description: "`fastp` supports both single-end (SE) and paired-end (PE) input.\n\ + \n- for SE data, you only have to specify read1 input by `-i` or `--in1`.\n- for\ + \ PE data, you should also specify read2 input by `-I` or `--in2`.\n" + arguments: + - type: "file" + name: "--in1" + alternatives: + - "-i" + description: "Input FastQ file. Must be single-end or paired-end R1. Can be gzipped." + info: null + example: + - "in.R1.fq.gz" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--in2" + alternatives: + - "-I" + description: "Input FastQ file. Must be paired-end R2. Can be gzipped." + info: null + example: + - "in.R2.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + description: "\n- for SE data, you only have to specify read1 output by `-o` or\ + \ `--out1`.\n- for PE data, you should also specify read2 output by `-O` or `--out2`.\n\ + - if you don't specify the output file names, no output files will be written,\ + \ but the QC will still be done for both data before and after filtering.\n- the\ + \ output will be gzip-compressed if its file name ends with `.gz`\n" + arguments: + - type: "file" + name: "--out1" + alternatives: + - "-o" + description: "The single-end or paired-end R1 reads that pass QC. Will be gzipped\ + \ if its file name ends with `.gz`." + info: null + example: + - "out.R1.fq.gz" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--out2" + alternatives: + - "-O" + description: "The paired-end R2 reads that pass QC. Will be gzipped if its file\ + \ name ends with `.gz`." + info: null + example: + - "out.R2.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unpaired1" + description: "Store the reads that `read1` passes filters but its paired `read2`\ + \ doesn't." + info: null + example: + - "unpaired.R1.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unpaired2" + description: "Store the reads that `read2` passes filters but its paired `read1`\ + \ doesn't." + info: null + example: + - "unpaired.R2.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--failed_out" + description: "Store the reads that fail filters.\n\nIf one read failed and is\ + \ written to --failed_out, its failure reason will be appended to its read name.\ + \ For example, failed_quality_filter, failed_too_short etc.\nFor PE data, if\ + \ unpaired reads are not stored (by giving --unpaired1 or --unpaired2), the\ + \ failed pair of reads will be put together. If one read passes the filters\ + \ but its pair doesn't, the failure reason will be paired_read_is_failing.\n" + info: null + example: + - "failed.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--overlapped_out" + description: "For each read pair, output the overlapped region if it has no any\ + \ mismatched base.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Report output arguments" + arguments: + - type: "file" + name: "--json" + alternatives: + - "-j" + description: "The json format report file name\n" + info: null + example: + - "out.json" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--html" + description: "The html format report file name\n" + info: null + example: + - "out.html" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--report_title" + description: "The title of the html report, default is \"fastp report\".\n" + info: null + example: + - "fastp report" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Adapter trimming" + description: "Adapter trimming is enabled by default, but you can disable it by\ + \ `-A` or `--disable_adapter_trimming`. Adapter sequences can be automatically\ + \ detected for both PE/SE data.\n\n- For SE data, the adapters are evaluated by\ + \ analyzing the tails of first ~1M reads. This evaluation may be inacurrate, and\ + \ you can specify the adapter sequence by `-a` or `--adapter_sequence` option.\ + \ If adapter sequence is specified, the auto detection for SE data will be disabled.\n\ + - For PE data, the adapters can be detected by per-read overlap analysis, which\ + \ seeks for the overlap of each pair of reads. This method is robust and fast,\ + \ so normally you don't have to input the adapter sequence even you know it. But\ + \ you can still specify the adapter sequences for read1 by `--adapter_sequence`,\ + \ and for read2 by `--adapter_sequence_r2`. If `fastp` fails to find an overlap\ + \ (i.e. due to low quality bases), it will use these sequences to trim adapters\ + \ for read1 and read2 respectively.\n- For PE data, the adapter sequence auto-detection\ + \ is disabled by default since the adapters can be trimmed by overlap analysis.\ + \ However, you can specify `--detect_adapter_for_pe` to enable it.\n- For PE data,\ + \ `fastp` will run a little slower if you specify the sequence adapters or enable\ + \ adapter auto-detection, but usually result in a slightly cleaner output, since\ + \ the overlap analysis may fail due to sequencing errors or adapter dimers.\n\ + - The most widely used adapter is the Illumina TruSeq adapters. If your data is\ + \ from the TruSeq library, you can add `--adapter_sequence=AGATCGGAAGAGCACACGTCTGAACTCCAGTCA\ + \ --adapter_sequence_r2=AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT` to your command lines,\ + \ or enable auto detection for PE data by specifing `detect_adapter_for_pe`.\n\ + - `fastp` contains some built-in known adapter sequences for better auto-detection.\ + \ If you want to make some adapters to be a part of the built-in adapters, please\ + \ file an issue.\n\nYou can also specify --adapter_fasta to give a FASTA file\ + \ to tell fastp to trim multiple adapters in this FASTA file. Here is a sample\ + \ of such adapter FASTA file:\n\n```\n>Illumina TruSeq Adapter Read 1\nAGATCGGAAGAGCACACGTCTGAACTCCAGTCA\n\ + >Illumina TruSeq Adapter Read 2\nAGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT\n>polyA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n\ + ```\n\nThe adapter sequence in this file should be at least 6bp long, otherwise\ + \ it will be skipped. And you can give whatever you want to trim, rather than\ + \ regular sequencing adapters (i.e. polyA).\n\n`fastp` first trims the auto-detected\ + \ adapter or the adapter sequences given by `--adapter_sequence | --adapter_sequence_r2`,\ + \ then trims the adapters given by `--adapter_fasta` one by one.\n\nThe sequence\ + \ distribution of trimmed adapters can be found at the HTML/JSON reports.\n" + arguments: + - type: "boolean_true" + name: "--disable_adapter_trimming" + alternatives: + - "-A" + description: "Disable adapter trimming.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--detect_adapter_for_pe" + description: "By default, the auto-detection for adapter is for SE data input\ + \ only, turn on this option to enable it for PE data.\n" + info: null + direction: "input" + - type: "string" + name: "--adapter_sequence" + alternatives: + - "-a" + description: "The adapter sequences to be trimmed. For SE data, if not specified,\ + \ the adapters will be auto-detected. For PE data, this is used if R1/R2 are\ + \ found not overlapped\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--adapter_sequence_r2" + description: "The adapter sequences to be trimmed for R2. This is used for PE\ + \ data if R1/R2 are found overlapped.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--adapter_fasta" + description: "A FASTA file containing all the adapter sequences to be trimmed.\ + \ For SE data, if not specified, the adapters will be auto-detected. For PE\ + \ data, this is used if R1/R2 are found not overlapped.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Base trimming" + arguments: + - type: "integer" + name: "--trim_front1" + alternatives: + - "-f" + description: "Trimming how many bases in front for read1, default is 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--trim_tail1" + alternatives: + - "-t" + description: "Trimming how many bases in tail for read1, default is 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_len1" + alternatives: + - "-b" + description: "If read1 is longer than max_len1, then trim read1 at its tail to\ + \ make it as long as max_len1. Default 0 means no limitation.\n" + info: null + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--trim_front2" + alternatives: + - "-F" + description: "Trimming how many bases in front for read2, default is 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--trim_tail2" + alternatives: + - "-T" + description: "Trimming how many bases in tail for read2, default is 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_len2" + alternatives: + - "-B" + description: "If read2 is longer than max_len2, then trim read2 at its tail to\ + \ make it as long as max_len2. Default 0 means no limitation.\n" + info: null + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Merging mode" + description: "Allows merging paired-end reads into a single longer read if they\ + \ are overlapping." + arguments: + - type: "boolean_true" + name: "--merge" + alternatives: + - "-m" + description: "For paired-end input, merge each pair of reads into a single read\ + \ if they are overlapped. The merged reads will be written to the file given\ + \ by --merged_out, the unmerged reads will be written to the files specified\ + \ by --out1 and --out2. The merging mode is disabled by default.\n" + info: null + direction: "input" + - type: "file" + name: "--merged_out" + description: "In the merging mode, specify the file name to store merged output,\ + \ or specify --stdout to stream the merged output.\n" + info: null + example: + - "merged.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--include_unmerged" + description: "In the merging mode, write the unmerged or unpaired reads to the\ + \ file specified by --merge. Disabled by default.\n" + info: null + direction: "input" +- name: "Additional input arguments" + description: "Affects how the input is read." + arguments: + - type: "boolean_true" + name: "--interleaved_in" + description: "Indicate that is an interleaved FASTQ which contains both\ + \ read1 and read2. Disabled by default.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--fix_mgi_id" + description: "The MGI FASTQ ID format is not compatible with many BAM operation\ + \ tools, enable this option to fix it.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--phred64" + alternatives: + - "-6" + description: "Indicate the input is using phred64 scoring (it'll be converted\ + \ to phred33, so the output will still be phred33)\n" + info: null + direction: "input" +- name: "Additional output arguments" + description: "Affects how the output is written." + arguments: + - type: "integer" + name: "--compression" + alternatives: + - "-z" + description: "Compression level for gzip output (1 ~ 9). 1 is fastest, 9 is smallest,\ + \ default is 4.\n" + info: null + example: + - 4 + required: false + min: 1 + max: 9 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--dont_overwrite" + description: "Don't overwrite existing files. Overwritting is allowed by default.\n" + info: null + direction: "input" +- name: "Logging arguments" + arguments: + - type: "boolean_true" + name: "--verbose" + alternatives: + - "-V" + description: "Output verbose log information (i.e. when every 1M reads are processed)." + info: null + direction: "input" +- name: "Processing arguments" + arguments: + - type: "long" + name: "--reads_to_process" + description: "Specify how many reads/pairs to be processed. Default 0 means process\ + \ all reads.\n" + info: null + example: + - 1000000 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Deduplication arguments" + arguments: + - type: "boolean_true" + name: "--dedup" + description: "Enable deduplication to drop the duplicated reads/pairs\n" + info: null + direction: "input" + - type: "integer" + name: "--dup_calc_accuracy" + description: "Accuracy level to calculate duplication (1~6). Higher level uses\ + \ more memory (1G, 2G, 4G, 8G, 16G, 24G). Default 1 for no-dedup mode, and 3\ + \ for dedup mode.\n" + info: null + example: + - 3 + required: false + min: 1 + max: 6 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--dont_eval_duplication" + description: "Don't evaluate duplication rate to save time and use less memory.\n" + info: null + direction: "input" +- name: "PolyG tail trimming arguments" + arguments: + - type: "boolean_true" + name: "--trim_poly_g" + alternatives: + - "-g" + description: "Force polyG tail trimming, by default trimming is automatically\ + \ enabled for Illumina NextSeq/NovaSeq data\n" + info: null + direction: "input" + - type: "integer" + name: "--poly_g_min_len" + description: "The minimum length to detect polyG in the read tail. 10 by default.\n" + info: null + example: + - 10 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--disable_trim_poly_g" + alternatives: + - "-G" + description: "Disable polyG tail trimming, by default trimming is automatically\ + \ enabled for Illumina NextSeq/NovaSeq data\n" + info: null + direction: "input" +- name: "PolyX tail trimming arguments" + arguments: + - type: "boolean_true" + name: "--trim_poly_x" + alternatives: + - "-x" + description: "Enable polyX trimming in 3' ends.\n" + info: null + direction: "input" + - type: "integer" + name: "--poly_x_min_len" + description: "The minimum length to detect polyX in the read tail. 10 by default.\n" + info: null + example: + - 10 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Cut arguments" + arguments: + - type: "integer" + name: "--cut_front" + alternatives: + - "-5" + description: "Move a sliding window from front (5') to tail, drop the bases in\ + \ the window if its mean quality < threshold, stop otherwise.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_tail" + alternatives: + - "-3" + description: "Move a sliding window from tail (3') to front, drop the bases in\ + \ the window if its mean quality < threshold, stop otherwise.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_right" + alternatives: + - "-r" + description: "Move a sliding window from front to tail, if meet one window with\ + \ mean quality < threshold, drop the bases in the window and the right part,\ + \ and then stop.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_window_size" + alternatives: + - "-W" + description: "The window size option shared by cut_front, cut_tail or cut_sliding.\ + \ Range: 1~1000, default: 4.\n" + info: null + example: + - 4 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_mean_quality" + alternatives: + - "-M" + description: "The mean quality requirement option shared by cut_front, cut_tail\ + \ or cut_sliding. Range: 1~36 default: 20 (Q20)\n" + info: null + example: + - 20 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_front_window_size" + description: "The window size option of cut_front, default to cut_window_size\ + \ if not specified.\n" + info: null + example: + - 4 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_front_mean_quality" + description: "The mean quality requirement option of cut_front, default to cut_mean_quality\ + \ if not specified.\n" + info: null + example: + - 20 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_tail_window_size" + description: "The window size option of cut_tail, default to cut_window_size if\ + \ not specified.\n" + info: null + example: + - 4 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_tail_mean_quality" + description: "The mean quality requirement option of cut_tail, default to cut_mean_quality\ + \ if not specified.\n" + info: null + example: + - 20 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_right_window_size" + description: "The window size option of cut_right, default to cut_window_size\ + \ if not specified.\n" + info: null + example: + - 4 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_right_mean_quality" + description: "The mean quality requirement option of cut_right, default to cut_mean_quality\ + \ if not specified.\n" + info: null + example: + - 20 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Quality filtering arguments" + arguments: + - type: "boolean_true" + name: "--disable_quality_filtering" + alternatives: + - "-Q" + description: "Quality filtering is enabled by default. If this option is specified,\ + \ quality filtering is disabled.\n" + info: null + direction: "input" + - type: "integer" + name: "--qualified_quality_phred" + alternatives: + - "-q" + description: "The quality value that a base is qualified. Default 15 means phred\ + \ quality >=Q15 is qualified.\n" + info: null + example: + - 15 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--unqualified_percent_limit" + alternatives: + - "-u" + description: "How many percents of bases are allowed to be unqualified (0~100).\ + \ Default 40 means 40%.\n" + info: null + example: + - 40 + required: false + min: 0 + max: 100 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--n_base_limit" + alternatives: + - "-n" + description: "If one read's number of N base is >n_base_limit, then this read/pair\ + \ is discarded. Default is 5.\n" + info: null + example: + - 5 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--average_qual" + alternatives: + - "-e" + description: "If one read's average quality score &1 | sed 's# #: \"#;s#$#\"#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/fastp/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/fastp" + executable: "target/executable/fastp/fastp" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/fastp/fastp b/target/executable/fastp/fastp new file mode 100755 index 00000000..3405dc37 --- /dev/null +++ b/target/executable/fastp/fastp @@ -0,0 +1,3388 @@ +#!/usr/bin/env bash + +# fastp main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="fastp" +VIASH_META_FUNCTIONALITY_NAME="fastp" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "fastp main" + echo "" + echo "An ultra-fast all-in-one FASTQ preprocessor" + echo "(QC/adapters/trimming/filtering/splitting/merging...)." + echo "" + echo "Features:" + echo "" + echo " - comprehensive quality profiling for both before and after filtering data" + echo "(quality curves, base contents, KMER, Q20/Q30, GC Ratio, duplication, adapter" + echo "contents...)" + echo " - filter out bad reads (too low quality, too short, or too many N...)" + echo " - cut low quality bases for per read in its 5' and 3' by evaluating the mean" + echo "quality from a sliding window (like Trimmomatic but faster)." + echo " - trim all reads in front and tail" + echo " - cut adapters. Adapter sequences can be automatically detected, which means" + echo "you don't have to input the adapter sequences to trim them." + echo " - correct mismatched base pairs in overlapped regions of paired end reads, if" + echo "one base is with high quality while the other is with ultra low quality" + echo " - trim polyG in 3' ends, which is commonly seen in NovaSeq/NextSeq data. Trim" + echo "polyX in 3' ends to remove unwanted polyX tailing (i.e. polyA tailing for" + echo "mRNA-Seq data)" + echo " - preprocess unique molecular identifier (UMI) enabled data, shift UMI to" + echo "sequence name." + echo " - report JSON format result for further interpreting." + echo " - visualize quality control and filtering results on a single HTML page (like" + echo "FASTQC but faster and more informative)." + echo " - split the output to multiple files (0001.R1.gz, 0002.R1.gz...) to support" + echo "parallel processing. Two modes can be used, limiting the total split file" + echo "number, or limitting the lines of each split file." + echo " - support long reads (data from PacBio / Nanopore devices)." + echo " - support reading from STDIN and writing to STDOUT" + echo " - support interleaved input" + echo " - support ultra-fast FASTQ-level deduplication" + echo "" + echo "Inputs:" + echo " \`fastp\` supports both single-end (SE) and paired-end (PE) input." + echo " - for SE data, you only have to specify read1 input by \`-i\` or \`--in1\`." + echo " - for PE data, you should also specify read2 input by \`-I\` or \`--in2\`." + echo "" + echo " -i, --in1" + echo " type: file, required parameter, file must exist" + echo " example: in.R1.fq.gz" + echo " Input FastQ file. Must be single-end or paired-end R1. Can be gzipped." + echo "" + echo " -I, --in2" + echo " type: file, file must exist" + echo " example: in.R2.fq.gz" + echo " Input FastQ file. Must be paired-end R2. Can be gzipped." + echo "" + echo "Outputs:" + echo " - for SE data, you only have to specify read1 output by \`-o\` or \`--out1\`." + echo " - for PE data, you should also specify read2 output by \`-O\` or \`--out2\`." + echo " - if you don't specify the output file names, no output files will be" + echo " written, but the QC will still be done for both data before and after" + echo " filtering." + echo " - the output will be gzip-compressed if its file name ends with \`.gz\`" + echo "" + echo " -o, --out1" + echo " type: file, required parameter, output, file must exist" + echo " example: out.R1.fq.gz" + echo " The single-end or paired-end R1 reads that pass QC. Will be gzipped if" + echo " its file name ends with \`.gz\`." + echo "" + echo " -O, --out2" + echo " type: file, output, file must exist" + echo " example: out.R2.fq.gz" + echo " The paired-end R2 reads that pass QC. Will be gzipped if its file name" + echo " ends with \`.gz\`." + echo "" + echo " --unpaired1" + echo " type: file, output, file must exist" + echo " example: unpaired.R1.fq.gz" + echo " Store the reads that \`read1\` passes filters but its paired \`read2\`" + echo " doesn't." + echo "" + echo " --unpaired2" + echo " type: file, output, file must exist" + echo " example: unpaired.R2.fq.gz" + echo " Store the reads that \`read2\` passes filters but its paired \`read1\`" + echo " doesn't." + echo "" + echo " --failed_out" + echo " type: file, output, file must exist" + echo " example: failed.fq.gz" + echo " Store the reads that fail filters." + echo " If one read failed and is written to --failed_out, its failure reason" + echo " will be appended to its read name. For example, failed_quality_filter," + echo " failed_too_short etc." + echo " For PE data, if unpaired reads are not stored (by giving --unpaired1 or" + echo " --unpaired2), the failed pair of reads will be put together. If one read" + echo " passes the filters but its pair doesn't, the failure reason will be" + echo " paired_read_is_failing." + echo "" + echo " --overlapped_out" + echo " type: file, output, file must exist" + echo " For each read pair, output the overlapped region if it has no any" + echo " mismatched base." + echo "" + echo "Report output arguments:" + echo " -j, --json" + echo " type: file, output, file must exist" + echo " example: out.json" + echo " The json format report file name" + echo "" + echo " --html" + echo " type: file, output, file must exist" + echo " example: out.html" + echo " The html format report file name" + echo "" + echo " --report_title" + echo " type: string" + echo " example: fastp report" + echo " The title of the html report, default is \"fastp report\"." + echo "" + echo "Adapter trimming:" + echo " Adapter trimming is enabled by default, but you can disable it by \`-A\` or" + echo " \`--disable_adapter_trimming\`. Adapter sequences can be automatically" + echo " detected for both PE/SE data." + echo " - For SE data, the adapters are evaluated by analyzing the tails of first" + echo " ~1M reads. This evaluation may be inacurrate, and you can specify the" + echo " adapter sequence by \`-a\` or \`--adapter_sequence\` option. If adapter sequence" + echo " is specified, the auto detection for SE data will be disabled." + echo " - For PE data, the adapters can be detected by per-read overlap analysis," + echo " which seeks for the overlap of each pair of reads. This method is robust and" + echo " fast, so normally you don't have to input the adapter sequence even you know" + echo " it. But you can still specify the adapter sequences for read1 by" + echo " \`--adapter_sequence\`, and for read2 by \`--adapter_sequence_r2\`. If \`fastp\`" + echo " fails to find an overlap (i.e. due to low quality bases), it will use these" + echo " sequences to trim adapters for read1 and read2 respectively." + echo " - For PE data, the adapter sequence auto-detection is disabled by default" + echo " since the adapters can be trimmed by overlap analysis. However, you can" + echo " specify \`--detect_adapter_for_pe\` to enable it." + echo " - For PE data, \`fastp\` will run a little slower if you specify the sequence" + echo " adapters or enable adapter auto-detection, but usually result in a slightly" + echo " cleaner output, since the overlap analysis may fail due to sequencing errors" + echo " or adapter dimers." + echo " - The most widely used adapter is the Illumina TruSeq adapters. If your data" + echo " is from the TruSeq library, you can add" + echo " \`--adapter_sequence=AGATCGGAAGAGCACACGTCTGAACTCCAGTCA" + echo " --adapter_sequence_r2=AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT\` to your command" + echo " lines, or enable auto detection for PE data by specifing" + echo " \`detect_adapter_for_pe\`." + echo " - \`fastp\` contains some built-in known adapter sequences for better" + echo " auto-detection. If you want to make some adapters to be a part of the" + echo " built-in adapters, please file an issue." + echo " You can also specify --adapter_fasta to give a FASTA file to tell fastp to" + echo " trim multiple adapters in this FASTA file. Here is a sample of such adapter" + echo " FASTA file:" + echo " \`\`\`" + echo " >Illumina TruSeq Adapter Read 1" + echo " AGATCGGAAGAGCACACGTCTGAACTCCAGTCA" + echo " >Illumina TruSeq Adapter Read 2" + echo " AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT" + echo " >polyA" + echo " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + echo " \`\`\`" + echo " The adapter sequence in this file should be at least 6bp long, otherwise it" + echo " will be skipped. And you can give whatever you want to trim, rather than" + echo " regular sequencing adapters (i.e. polyA)." + echo " \`fastp\` first trims the auto-detected adapter or the adapter sequences given" + echo " by \`--adapter_sequence | --adapter_sequence_r2\`, then trims the adapters" + echo " given by \`--adapter_fasta\` one by one." + echo " The sequence distribution of trimmed adapters can be found at the HTML/JSON" + echo " reports." + echo "" + echo " -A, --disable_adapter_trimming" + echo " type: boolean_true" + echo " Disable adapter trimming." + echo "" + echo " --detect_adapter_for_pe" + echo " type: boolean_true" + echo " By default, the auto-detection for adapter is for SE data input only," + echo " turn on this option to enable it for PE data." + echo "" + echo " -a, --adapter_sequence" + echo " type: string" + echo " The adapter sequences to be trimmed. For SE data, if not specified, the" + echo " adapters will be auto-detected. For PE data, this is used if R1/R2 are" + echo " found not overlapped" + echo "" + echo " --adapter_sequence_r2" + echo " type: string" + echo " The adapter sequences to be trimmed for R2. This is used for PE data if" + echo " R1/R2 are found overlapped." + echo "" + echo " --adapter_fasta" + echo " type: file, file must exist" + echo " A FASTA file containing all the adapter sequences to be trimmed. For SE" + echo " data, if not specified, the adapters will be auto-detected. For PE data," + echo " this is used if R1/R2 are found not overlapped." + echo "" + echo "Base trimming:" + echo " -f, --trim_front1" + echo " type: integer" + echo " example: 0" + echo " Trimming how many bases in front for read1, default is 0." + echo "" + echo " -t, --trim_tail1" + echo " type: integer" + echo " example: 0" + echo " Trimming how many bases in tail for read1, default is 0." + echo "" + echo " -b, --max_len1" + echo " type: integer" + echo " min: 0" + echo " If read1 is longer than max_len1, then trim read1 at its tail to make it" + echo " as long as max_len1. Default 0 means no limitation." + echo "" + echo " -F, --trim_front2" + echo " type: integer" + echo " example: 0" + echo " Trimming how many bases in front for read2, default is 0." + echo "" + echo " -T, --trim_tail2" + echo " type: integer" + echo " example: 0" + echo " Trimming how many bases in tail for read2, default is 0." + echo "" + echo " -B, --max_len2" + echo " type: integer" + echo " min: 0" + echo " If read2 is longer than max_len2, then trim read2 at its tail to make it" + echo " as long as max_len2. Default 0 means no limitation." + echo "" + echo "Merging mode:" + echo " Allows merging paired-end reads into a single longer read if they are" + echo " overlapping." + echo "" + echo " -m, --merge" + echo " type: boolean_true" + echo " For paired-end input, merge each pair of reads into a single read if" + echo " they are overlapped. The merged reads will be written to the file given" + echo " by --merged_out, the unmerged reads will be written to the files" + echo " specified by --out1 and --out2. The merging mode is disabled by default." + echo "" + echo " --merged_out" + echo " type: file, output, file must exist" + echo " example: merged.fq.gz" + echo " In the merging mode, specify the file name to store merged output, or" + echo " specify --stdout to stream the merged output." + echo "" + echo " --include_unmerged" + echo " type: boolean_true" + echo " In the merging mode, write the unmerged or unpaired reads to the file" + echo " specified by --merge. Disabled by default." + echo "" + echo "Additional input arguments:" + echo " Affects how the input is read." + echo "" + echo " --interleaved_in" + echo " type: boolean_true" + echo " Indicate that is an interleaved FASTQ which contains both read1" + echo " and read2. Disabled by default." + echo "" + echo " --fix_mgi_id" + echo " type: boolean_true" + echo " The MGI FASTQ ID format is not compatible with many BAM operation tools," + echo " enable this option to fix it." + echo "" + echo " -6, --phred64" + echo " type: boolean_true" + echo " Indicate the input is using phred64 scoring (it'll be converted to" + echo " phred33, so the output will still be phred33)" + echo "" + echo "Additional output arguments:" + echo " Affects how the output is written." + echo "" + echo " -z, --compression" + echo " type: integer" + echo " example: 4" + echo " min: 1" + echo " max: 9" + echo " Compression level for gzip output (1 ~ 9). 1 is fastest, 9 is smallest," + echo " default is 4." + echo "" + echo " --dont_overwrite" + echo " type: boolean_true" + echo " Don't overwrite existing files. Overwritting is allowed by default." + echo "" + echo "Logging arguments:" + echo " -V, --verbose" + echo " type: boolean_true" + echo " Output verbose log information (i.e. when every 1M reads are processed)." + echo "" + echo "Processing arguments:" + echo " --reads_to_process" + echo " type: long" + echo " example: 1000000" + echo " min: 0" + echo " Specify how many reads/pairs to be processed. Default 0 means process" + echo " all reads." + echo "" + echo "Deduplication arguments:" + echo " --dedup" + echo " type: boolean_true" + echo " Enable deduplication to drop the duplicated reads/pairs" + echo "" + echo " --dup_calc_accuracy" + echo " type: integer" + echo " example: 3" + echo " min: 1" + echo " max: 6" + echo " Accuracy level to calculate duplication (1~6). Higher level uses more" + echo " memory (1G, 2G, 4G, 8G, 16G, 24G). Default 1 for no-dedup mode, and 3" + echo " for dedup mode." + echo "" + echo " --dont_eval_duplication" + echo " type: boolean_true" + echo " Don't evaluate duplication rate to save time and use less memory." + echo "" + echo "PolyG tail trimming arguments:" + echo " -g, --trim_poly_g" + echo " type: boolean_true" + echo " Force polyG tail trimming, by default trimming is automatically enabled" + echo " for Illumina NextSeq/NovaSeq data" + echo "" + echo " --poly_g_min_len" + echo " type: integer" + echo " example: 10" + echo " min: 1" + echo " The minimum length to detect polyG in the read tail. 10 by default." + echo "" + echo " -G, --disable_trim_poly_g" + echo " type: boolean_true" + echo " Disable polyG tail trimming, by default trimming is automatically" + echo " enabled for Illumina NextSeq/NovaSeq data" + echo "" + echo "PolyX tail trimming arguments:" + echo " -x, --trim_poly_x" + echo " type: boolean_true" + echo " Enable polyX trimming in 3' ends." + echo "" + echo " --poly_x_min_len" + echo " type: integer" + echo " example: 10" + echo " min: 1" + echo " The minimum length to detect polyX in the read tail. 10 by default." + echo "" + echo "Cut arguments:" + echo " -5, --cut_front" + echo " type: integer" + echo " Move a sliding window from front (5') to tail, drop the bases in the" + echo " window if its mean quality < threshold, stop otherwise." + echo "" + echo " -3, --cut_tail" + echo " type: integer" + echo " Move a sliding window from tail (3') to front, drop the bases in the" + echo " window if its mean quality < threshold, stop otherwise." + echo "" + echo " -r, --cut_right" + echo " type: integer" + echo " Move a sliding window from front to tail, if meet one window with mean" + echo " quality < threshold, drop the bases in the window and the right part," + echo " and then stop." + echo "" + echo " -W, --cut_window_size" + echo " type: integer" + echo " example: 4" + echo " min: 1" + echo " The window size option shared by cut_front, cut_tail or cut_sliding." + echo " Range: 1~1000, default: 4." + echo "" + echo " -M, --cut_mean_quality" + echo " type: integer" + echo " example: 20" + echo " min: 0" + echo " The mean quality requirement option shared by cut_front, cut_tail or" + echo " cut_sliding. Range: 1~36 default: 20 (Q20)" + echo "" + echo " --cut_front_window_size" + echo " type: integer" + echo " example: 4" + echo " min: 1" + echo " The window size option of cut_front, default to cut_window_size if not" + echo " specified." + echo "" + echo " --cut_front_mean_quality" + echo " type: integer" + echo " example: 20" + echo " min: 0" + echo " The mean quality requirement option of cut_front, default to" + echo " cut_mean_quality if not specified." + echo "" + echo " --cut_tail_window_size" + echo " type: integer" + echo " example: 4" + echo " min: 1" + echo " The window size option of cut_tail, default to cut_window_size if not" + echo " specified." + echo "" + echo " --cut_tail_mean_quality" + echo " type: integer" + echo " example: 20" + echo " min: 0" + echo " The mean quality requirement option of cut_tail, default to" + echo " cut_mean_quality if not specified." + echo "" + echo " --cut_right_window_size" + echo " type: integer" + echo " example: 4" + echo " min: 1" + echo " The window size option of cut_right, default to cut_window_size if not" + echo " specified." + echo "" + echo " --cut_right_mean_quality" + echo " type: integer" + echo " example: 20" + echo " min: 0" + echo " The mean quality requirement option of cut_right, default to" + echo " cut_mean_quality if not specified." + echo "" + echo "Quality filtering arguments:" + echo " -Q, --disable_quality_filtering" + echo " type: boolean_true" + echo " Quality filtering is enabled by default. If this option is specified," + echo " quality filtering is disabled." + echo "" + echo " -q, --qualified_quality_phred" + echo " type: integer" + echo " example: 15" + echo " min: 0" + echo " The quality value that a base is qualified. Default 15 means phred" + echo " quality >=Q15 is qualified." + echo "" + echo " -u, --unqualified_percent_limit" + echo " type: integer" + echo " example: 40" + echo " min: 0" + echo " max: 100" + echo " How many percents of bases are allowed to be unqualified (0~100)." + echo " Default 40 means 40%." + echo "" + echo " -n, --n_base_limit" + echo " type: integer" + echo " example: 5" + echo " min: 0" + echo " If one read's number of N base is >n_base_limit, then this read/pair is" + echo " discarded. Default is 5." + echo "" + echo " -e, --average_qual" + echo " type: integer" + echo " example: 0" + echo " min: 0" + echo " If one read's average quality score /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/fastp:0.23.4--hadf994f_2 +ENTRYPOINT [] +RUN fastp --version 2>&1 | sed 's# #: "#;s#$#"#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component fastp" +LABEL org.opencontainers.image.created="2024-06-24T08:36:44Z" +LABEL org.opencontainers.image.source="https://github.com/OpenGene/fastp" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "fastp main" + exit + ;; + --in1) + [ -n "$VIASH_PAR_IN1" ] && ViashError Bad arguments for option \'--in1\': \'$VIASH_PAR_IN1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IN1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --in1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --in1=*) + [ -n "$VIASH_PAR_IN1" ] && ViashError Bad arguments for option \'--in1=*\': \'$VIASH_PAR_IN1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IN1=$(ViashRemoveFlags "$1") + shift 1 + ;; + -i) + [ -n "$VIASH_PAR_IN1" ] && ViashError Bad arguments for option \'-i\': \'$VIASH_PAR_IN1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IN1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -i. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --in2) + [ -n "$VIASH_PAR_IN2" ] && ViashError Bad arguments for option \'--in2\': \'$VIASH_PAR_IN2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IN2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --in2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --in2=*) + [ -n "$VIASH_PAR_IN2" ] && ViashError Bad arguments for option \'--in2=*\': \'$VIASH_PAR_IN2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IN2=$(ViashRemoveFlags "$1") + shift 1 + ;; + -I) + [ -n "$VIASH_PAR_IN2" ] && ViashError Bad arguments for option \'-I\': \'$VIASH_PAR_IN2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IN2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -I. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --out1) + [ -n "$VIASH_PAR_OUT1" ] && ViashError Bad arguments for option \'--out1\': \'$VIASH_PAR_OUT1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --out1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --out1=*) + [ -n "$VIASH_PAR_OUT1" ] && ViashError Bad arguments for option \'--out1=*\': \'$VIASH_PAR_OUT1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT1=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUT1" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUT1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --out2) + [ -n "$VIASH_PAR_OUT2" ] && ViashError Bad arguments for option \'--out2\': \'$VIASH_PAR_OUT2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --out2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --out2=*) + [ -n "$VIASH_PAR_OUT2" ] && ViashError Bad arguments for option \'--out2=*\': \'$VIASH_PAR_OUT2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT2=$(ViashRemoveFlags "$1") + shift 1 + ;; + -O) + [ -n "$VIASH_PAR_OUT2" ] && ViashError Bad arguments for option \'-O\': \'$VIASH_PAR_OUT2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -O. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unpaired1) + [ -n "$VIASH_PAR_UNPAIRED1" ] && ViashError Bad arguments for option \'--unpaired1\': \'$VIASH_PAR_UNPAIRED1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNPAIRED1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --unpaired1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unpaired1=*) + [ -n "$VIASH_PAR_UNPAIRED1" ] && ViashError Bad arguments for option \'--unpaired1=*\': \'$VIASH_PAR_UNPAIRED1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNPAIRED1=$(ViashRemoveFlags "$1") + shift 1 + ;; + --unpaired2) + [ -n "$VIASH_PAR_UNPAIRED2" ] && ViashError Bad arguments for option \'--unpaired2\': \'$VIASH_PAR_UNPAIRED2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNPAIRED2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --unpaired2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unpaired2=*) + [ -n "$VIASH_PAR_UNPAIRED2" ] && ViashError Bad arguments for option \'--unpaired2=*\': \'$VIASH_PAR_UNPAIRED2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNPAIRED2=$(ViashRemoveFlags "$1") + shift 1 + ;; + --failed_out) + [ -n "$VIASH_PAR_FAILED_OUT" ] && ViashError Bad arguments for option \'--failed_out\': \'$VIASH_PAR_FAILED_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FAILED_OUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --failed_out. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --failed_out=*) + [ -n "$VIASH_PAR_FAILED_OUT" ] && ViashError Bad arguments for option \'--failed_out=*\': \'$VIASH_PAR_FAILED_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FAILED_OUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --overlapped_out) + [ -n "$VIASH_PAR_OVERLAPPED_OUT" ] && ViashError Bad arguments for option \'--overlapped_out\': \'$VIASH_PAR_OVERLAPPED_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAPPED_OUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --overlapped_out. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --overlapped_out=*) + [ -n "$VIASH_PAR_OVERLAPPED_OUT" ] && ViashError Bad arguments for option \'--overlapped_out=*\': \'$VIASH_PAR_OVERLAPPED_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAPPED_OUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --json) + [ -n "$VIASH_PAR_JSON" ] && ViashError Bad arguments for option \'--json\': \'$VIASH_PAR_JSON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_JSON="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --json. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --json=*) + [ -n "$VIASH_PAR_JSON" ] && ViashError Bad arguments for option \'--json=*\': \'$VIASH_PAR_JSON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_JSON=$(ViashRemoveFlags "$1") + shift 1 + ;; + -j) + [ -n "$VIASH_PAR_JSON" ] && ViashError Bad arguments for option \'-j\': \'$VIASH_PAR_JSON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_JSON="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -j. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --html) + [ -n "$VIASH_PAR_HTML" ] && ViashError Bad arguments for option \'--html\': \'$VIASH_PAR_HTML\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HTML="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --html. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --html=*) + [ -n "$VIASH_PAR_HTML" ] && ViashError Bad arguments for option \'--html=*\': \'$VIASH_PAR_HTML\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HTML=$(ViashRemoveFlags "$1") + shift 1 + ;; + --report_title) + [ -n "$VIASH_PAR_REPORT_TITLE" ] && ViashError Bad arguments for option \'--report_title\': \'$VIASH_PAR_REPORT_TITLE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REPORT_TITLE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --report_title. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --report_title=*) + [ -n "$VIASH_PAR_REPORT_TITLE" ] && ViashError Bad arguments for option \'--report_title=*\': \'$VIASH_PAR_REPORT_TITLE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REPORT_TITLE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --disable_adapter_trimming) + [ -n "$VIASH_PAR_DISABLE_ADAPTER_TRIMMING" ] && ViashError Bad arguments for option \'--disable_adapter_trimming\': \'$VIASH_PAR_DISABLE_ADAPTER_TRIMMING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISABLE_ADAPTER_TRIMMING=true + shift 1 + ;; + -A) + [ -n "$VIASH_PAR_DISABLE_ADAPTER_TRIMMING" ] && ViashError Bad arguments for option \'-A\': \'$VIASH_PAR_DISABLE_ADAPTER_TRIMMING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISABLE_ADAPTER_TRIMMING=true + shift 1 + ;; + --detect_adapter_for_pe) + [ -n "$VIASH_PAR_DETECT_ADAPTER_FOR_PE" ] && ViashError Bad arguments for option \'--detect_adapter_for_pe\': \'$VIASH_PAR_DETECT_ADAPTER_FOR_PE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DETECT_ADAPTER_FOR_PE=true + shift 1 + ;; + --adapter_sequence) + [ -n "$VIASH_PAR_ADAPTER_SEQUENCE" ] && ViashError Bad arguments for option \'--adapter_sequence\': \'$VIASH_PAR_ADAPTER_SEQUENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADAPTER_SEQUENCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --adapter_sequence. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --adapter_sequence=*) + [ -n "$VIASH_PAR_ADAPTER_SEQUENCE" ] && ViashError Bad arguments for option \'--adapter_sequence=*\': \'$VIASH_PAR_ADAPTER_SEQUENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADAPTER_SEQUENCE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -a) + [ -n "$VIASH_PAR_ADAPTER_SEQUENCE" ] && ViashError Bad arguments for option \'-a\': \'$VIASH_PAR_ADAPTER_SEQUENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADAPTER_SEQUENCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -a. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --adapter_sequence_r2) + [ -n "$VIASH_PAR_ADAPTER_SEQUENCE_R2" ] && ViashError Bad arguments for option \'--adapter_sequence_r2\': \'$VIASH_PAR_ADAPTER_SEQUENCE_R2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADAPTER_SEQUENCE_R2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --adapter_sequence_r2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --adapter_sequence_r2=*) + [ -n "$VIASH_PAR_ADAPTER_SEQUENCE_R2" ] && ViashError Bad arguments for option \'--adapter_sequence_r2=*\': \'$VIASH_PAR_ADAPTER_SEQUENCE_R2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADAPTER_SEQUENCE_R2=$(ViashRemoveFlags "$1") + shift 1 + ;; + --adapter_fasta) + [ -n "$VIASH_PAR_ADAPTER_FASTA" ] && ViashError Bad arguments for option \'--adapter_fasta\': \'$VIASH_PAR_ADAPTER_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADAPTER_FASTA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --adapter_fasta. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --adapter_fasta=*) + [ -n "$VIASH_PAR_ADAPTER_FASTA" ] && ViashError Bad arguments for option \'--adapter_fasta=*\': \'$VIASH_PAR_ADAPTER_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADAPTER_FASTA=$(ViashRemoveFlags "$1") + shift 1 + ;; + --trim_front1) + [ -n "$VIASH_PAR_TRIM_FRONT1" ] && ViashError Bad arguments for option \'--trim_front1\': \'$VIASH_PAR_TRIM_FRONT1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_FRONT1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --trim_front1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --trim_front1=*) + [ -n "$VIASH_PAR_TRIM_FRONT1" ] && ViashError Bad arguments for option \'--trim_front1=*\': \'$VIASH_PAR_TRIM_FRONT1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_FRONT1=$(ViashRemoveFlags "$1") + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_TRIM_FRONT1" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_TRIM_FRONT1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_FRONT1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -f. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --trim_tail1) + [ -n "$VIASH_PAR_TRIM_TAIL1" ] && ViashError Bad arguments for option \'--trim_tail1\': \'$VIASH_PAR_TRIM_TAIL1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_TAIL1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --trim_tail1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --trim_tail1=*) + [ -n "$VIASH_PAR_TRIM_TAIL1" ] && ViashError Bad arguments for option \'--trim_tail1=*\': \'$VIASH_PAR_TRIM_TAIL1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_TAIL1=$(ViashRemoveFlags "$1") + shift 1 + ;; + -t) + [ -n "$VIASH_PAR_TRIM_TAIL1" ] && ViashError Bad arguments for option \'-t\': \'$VIASH_PAR_TRIM_TAIL1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_TAIL1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -t. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_len1) + [ -n "$VIASH_PAR_MAX_LEN1" ] && ViashError Bad arguments for option \'--max_len1\': \'$VIASH_PAR_MAX_LEN1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_LEN1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_len1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_len1=*) + [ -n "$VIASH_PAR_MAX_LEN1" ] && ViashError Bad arguments for option \'--max_len1=*\': \'$VIASH_PAR_MAX_LEN1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_LEN1=$(ViashRemoveFlags "$1") + shift 1 + ;; + -b) + [ -n "$VIASH_PAR_MAX_LEN1" ] && ViashError Bad arguments for option \'-b\': \'$VIASH_PAR_MAX_LEN1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_LEN1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -b. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --trim_front2) + [ -n "$VIASH_PAR_TRIM_FRONT2" ] && ViashError Bad arguments for option \'--trim_front2\': \'$VIASH_PAR_TRIM_FRONT2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_FRONT2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --trim_front2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --trim_front2=*) + [ -n "$VIASH_PAR_TRIM_FRONT2" ] && ViashError Bad arguments for option \'--trim_front2=*\': \'$VIASH_PAR_TRIM_FRONT2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_FRONT2=$(ViashRemoveFlags "$1") + shift 1 + ;; + -F) + [ -n "$VIASH_PAR_TRIM_FRONT2" ] && ViashError Bad arguments for option \'-F\': \'$VIASH_PAR_TRIM_FRONT2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_FRONT2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -F. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --trim_tail2) + [ -n "$VIASH_PAR_TRIM_TAIL2" ] && ViashError Bad arguments for option \'--trim_tail2\': \'$VIASH_PAR_TRIM_TAIL2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_TAIL2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --trim_tail2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --trim_tail2=*) + [ -n "$VIASH_PAR_TRIM_TAIL2" ] && ViashError Bad arguments for option \'--trim_tail2=*\': \'$VIASH_PAR_TRIM_TAIL2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_TAIL2=$(ViashRemoveFlags "$1") + shift 1 + ;; + -T) + [ -n "$VIASH_PAR_TRIM_TAIL2" ] && ViashError Bad arguments for option \'-T\': \'$VIASH_PAR_TRIM_TAIL2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_TAIL2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -T. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_len2) + [ -n "$VIASH_PAR_MAX_LEN2" ] && ViashError Bad arguments for option \'--max_len2\': \'$VIASH_PAR_MAX_LEN2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_LEN2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_len2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_len2=*) + [ -n "$VIASH_PAR_MAX_LEN2" ] && ViashError Bad arguments for option \'--max_len2=*\': \'$VIASH_PAR_MAX_LEN2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_LEN2=$(ViashRemoveFlags "$1") + shift 1 + ;; + -B) + [ -n "$VIASH_PAR_MAX_LEN2" ] && ViashError Bad arguments for option \'-B\': \'$VIASH_PAR_MAX_LEN2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_LEN2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -B. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --merge) + [ -n "$VIASH_PAR_MERGE" ] && ViashError Bad arguments for option \'--merge\': \'$VIASH_PAR_MERGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MERGE=true + shift 1 + ;; + -m) + [ -n "$VIASH_PAR_MERGE" ] && ViashError Bad arguments for option \'-m\': \'$VIASH_PAR_MERGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MERGE=true + shift 1 + ;; + --merged_out) + [ -n "$VIASH_PAR_MERGED_OUT" ] && ViashError Bad arguments for option \'--merged_out\': \'$VIASH_PAR_MERGED_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MERGED_OUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --merged_out. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --merged_out=*) + [ -n "$VIASH_PAR_MERGED_OUT" ] && ViashError Bad arguments for option \'--merged_out=*\': \'$VIASH_PAR_MERGED_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MERGED_OUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --include_unmerged) + [ -n "$VIASH_PAR_INCLUDE_UNMERGED" ] && ViashError Bad arguments for option \'--include_unmerged\': \'$VIASH_PAR_INCLUDE_UNMERGED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INCLUDE_UNMERGED=true + shift 1 + ;; + --interleaved_in) + [ -n "$VIASH_PAR_INTERLEAVED_IN" ] && ViashError Bad arguments for option \'--interleaved_in\': \'$VIASH_PAR_INTERLEAVED_IN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INTERLEAVED_IN=true + shift 1 + ;; + --fix_mgi_id) + [ -n "$VIASH_PAR_FIX_MGI_ID" ] && ViashError Bad arguments for option \'--fix_mgi_id\': \'$VIASH_PAR_FIX_MGI_ID\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FIX_MGI_ID=true + shift 1 + ;; + --phred64) + [ -n "$VIASH_PAR_PHRED64" ] && ViashError Bad arguments for option \'--phred64\': \'$VIASH_PAR_PHRED64\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PHRED64=true + shift 1 + ;; + -6) + [ -n "$VIASH_PAR_PHRED64" ] && ViashError Bad arguments for option \'-6\': \'$VIASH_PAR_PHRED64\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PHRED64=true + shift 1 + ;; + --compression) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'--compression\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --compression. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --compression=*) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'--compression=*\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION=$(ViashRemoveFlags "$1") + shift 1 + ;; + -z) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'-z\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -z. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --dont_overwrite) + [ -n "$VIASH_PAR_DONT_OVERWRITE" ] && ViashError Bad arguments for option \'--dont_overwrite\': \'$VIASH_PAR_DONT_OVERWRITE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DONT_OVERWRITE=true + shift 1 + ;; + --verbose) + [ -n "$VIASH_PAR_VERBOSE" ] && ViashError Bad arguments for option \'--verbose\': \'$VIASH_PAR_VERBOSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_VERBOSE=true + shift 1 + ;; + -V) + [ -n "$VIASH_PAR_VERBOSE" ] && ViashError Bad arguments for option \'-V\': \'$VIASH_PAR_VERBOSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_VERBOSE=true + shift 1 + ;; + --reads_to_process) + [ -n "$VIASH_PAR_READS_TO_PROCESS" ] && ViashError Bad arguments for option \'--reads_to_process\': \'$VIASH_PAR_READS_TO_PROCESS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READS_TO_PROCESS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --reads_to_process. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --reads_to_process=*) + [ -n "$VIASH_PAR_READS_TO_PROCESS" ] && ViashError Bad arguments for option \'--reads_to_process=*\': \'$VIASH_PAR_READS_TO_PROCESS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READS_TO_PROCESS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --dedup) + [ -n "$VIASH_PAR_DEDUP" ] && ViashError Bad arguments for option \'--dedup\': \'$VIASH_PAR_DEDUP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEDUP=true + shift 1 + ;; + --dup_calc_accuracy) + [ -n "$VIASH_PAR_DUP_CALC_ACCURACY" ] && ViashError Bad arguments for option \'--dup_calc_accuracy\': \'$VIASH_PAR_DUP_CALC_ACCURACY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DUP_CALC_ACCURACY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --dup_calc_accuracy. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --dup_calc_accuracy=*) + [ -n "$VIASH_PAR_DUP_CALC_ACCURACY" ] && ViashError Bad arguments for option \'--dup_calc_accuracy=*\': \'$VIASH_PAR_DUP_CALC_ACCURACY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DUP_CALC_ACCURACY=$(ViashRemoveFlags "$1") + shift 1 + ;; + --dont_eval_duplication) + [ -n "$VIASH_PAR_DONT_EVAL_DUPLICATION" ] && ViashError Bad arguments for option \'--dont_eval_duplication\': \'$VIASH_PAR_DONT_EVAL_DUPLICATION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DONT_EVAL_DUPLICATION=true + shift 1 + ;; + --trim_poly_g) + [ -n "$VIASH_PAR_TRIM_POLY_G" ] && ViashError Bad arguments for option \'--trim_poly_g\': \'$VIASH_PAR_TRIM_POLY_G\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_POLY_G=true + shift 1 + ;; + -g) + [ -n "$VIASH_PAR_TRIM_POLY_G" ] && ViashError Bad arguments for option \'-g\': \'$VIASH_PAR_TRIM_POLY_G\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_POLY_G=true + shift 1 + ;; + --poly_g_min_len) + [ -n "$VIASH_PAR_POLY_G_MIN_LEN" ] && ViashError Bad arguments for option \'--poly_g_min_len\': \'$VIASH_PAR_POLY_G_MIN_LEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_POLY_G_MIN_LEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --poly_g_min_len. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --poly_g_min_len=*) + [ -n "$VIASH_PAR_POLY_G_MIN_LEN" ] && ViashError Bad arguments for option \'--poly_g_min_len=*\': \'$VIASH_PAR_POLY_G_MIN_LEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_POLY_G_MIN_LEN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --disable_trim_poly_g) + [ -n "$VIASH_PAR_DISABLE_TRIM_POLY_G" ] && ViashError Bad arguments for option \'--disable_trim_poly_g\': \'$VIASH_PAR_DISABLE_TRIM_POLY_G\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISABLE_TRIM_POLY_G=true + shift 1 + ;; + -G) + [ -n "$VIASH_PAR_DISABLE_TRIM_POLY_G" ] && ViashError Bad arguments for option \'-G\': \'$VIASH_PAR_DISABLE_TRIM_POLY_G\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISABLE_TRIM_POLY_G=true + shift 1 + ;; + --trim_poly_x) + [ -n "$VIASH_PAR_TRIM_POLY_X" ] && ViashError Bad arguments for option \'--trim_poly_x\': \'$VIASH_PAR_TRIM_POLY_X\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_POLY_X=true + shift 1 + ;; + -x) + [ -n "$VIASH_PAR_TRIM_POLY_X" ] && ViashError Bad arguments for option \'-x\': \'$VIASH_PAR_TRIM_POLY_X\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_POLY_X=true + shift 1 + ;; + --poly_x_min_len) + [ -n "$VIASH_PAR_POLY_X_MIN_LEN" ] && ViashError Bad arguments for option \'--poly_x_min_len\': \'$VIASH_PAR_POLY_X_MIN_LEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_POLY_X_MIN_LEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --poly_x_min_len. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --poly_x_min_len=*) + [ -n "$VIASH_PAR_POLY_X_MIN_LEN" ] && ViashError Bad arguments for option \'--poly_x_min_len=*\': \'$VIASH_PAR_POLY_X_MIN_LEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_POLY_X_MIN_LEN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --cut_front) + [ -n "$VIASH_PAR_CUT_FRONT" ] && ViashError Bad arguments for option \'--cut_front\': \'$VIASH_PAR_CUT_FRONT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_FRONT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_front. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_front=*) + [ -n "$VIASH_PAR_CUT_FRONT" ] && ViashError Bad arguments for option \'--cut_front=*\': \'$VIASH_PAR_CUT_FRONT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_FRONT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -5) + [ -n "$VIASH_PAR_CUT_FRONT" ] && ViashError Bad arguments for option \'-5\': \'$VIASH_PAR_CUT_FRONT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_FRONT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -5. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_tail) + [ -n "$VIASH_PAR_CUT_TAIL" ] && ViashError Bad arguments for option \'--cut_tail\': \'$VIASH_PAR_CUT_TAIL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_TAIL="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_tail. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_tail=*) + [ -n "$VIASH_PAR_CUT_TAIL" ] && ViashError Bad arguments for option \'--cut_tail=*\': \'$VIASH_PAR_CUT_TAIL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_TAIL=$(ViashRemoveFlags "$1") + shift 1 + ;; + -3) + [ -n "$VIASH_PAR_CUT_TAIL" ] && ViashError Bad arguments for option \'-3\': \'$VIASH_PAR_CUT_TAIL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_TAIL="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -3. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_right) + [ -n "$VIASH_PAR_CUT_RIGHT" ] && ViashError Bad arguments for option \'--cut_right\': \'$VIASH_PAR_CUT_RIGHT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_RIGHT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_right. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_right=*) + [ -n "$VIASH_PAR_CUT_RIGHT" ] && ViashError Bad arguments for option \'--cut_right=*\': \'$VIASH_PAR_CUT_RIGHT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_RIGHT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -r) + [ -n "$VIASH_PAR_CUT_RIGHT" ] && ViashError Bad arguments for option \'-r\': \'$VIASH_PAR_CUT_RIGHT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_RIGHT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -r. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_window_size) + [ -n "$VIASH_PAR_CUT_WINDOW_SIZE" ] && ViashError Bad arguments for option \'--cut_window_size\': \'$VIASH_PAR_CUT_WINDOW_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_WINDOW_SIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_window_size. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_window_size=*) + [ -n "$VIASH_PAR_CUT_WINDOW_SIZE" ] && ViashError Bad arguments for option \'--cut_window_size=*\': \'$VIASH_PAR_CUT_WINDOW_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_WINDOW_SIZE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -W) + [ -n "$VIASH_PAR_CUT_WINDOW_SIZE" ] && ViashError Bad arguments for option \'-W\': \'$VIASH_PAR_CUT_WINDOW_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_WINDOW_SIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -W. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_mean_quality) + [ -n "$VIASH_PAR_CUT_MEAN_QUALITY" ] && ViashError Bad arguments for option \'--cut_mean_quality\': \'$VIASH_PAR_CUT_MEAN_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_MEAN_QUALITY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_mean_quality. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_mean_quality=*) + [ -n "$VIASH_PAR_CUT_MEAN_QUALITY" ] && ViashError Bad arguments for option \'--cut_mean_quality=*\': \'$VIASH_PAR_CUT_MEAN_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_MEAN_QUALITY=$(ViashRemoveFlags "$1") + shift 1 + ;; + -M) + [ -n "$VIASH_PAR_CUT_MEAN_QUALITY" ] && ViashError Bad arguments for option \'-M\': \'$VIASH_PAR_CUT_MEAN_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_MEAN_QUALITY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -M. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_front_window_size) + [ -n "$VIASH_PAR_CUT_FRONT_WINDOW_SIZE" ] && ViashError Bad arguments for option \'--cut_front_window_size\': \'$VIASH_PAR_CUT_FRONT_WINDOW_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_FRONT_WINDOW_SIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_front_window_size. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_front_window_size=*) + [ -n "$VIASH_PAR_CUT_FRONT_WINDOW_SIZE" ] && ViashError Bad arguments for option \'--cut_front_window_size=*\': \'$VIASH_PAR_CUT_FRONT_WINDOW_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_FRONT_WINDOW_SIZE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --cut_front_mean_quality) + [ -n "$VIASH_PAR_CUT_FRONT_MEAN_QUALITY" ] && ViashError Bad arguments for option \'--cut_front_mean_quality\': \'$VIASH_PAR_CUT_FRONT_MEAN_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_FRONT_MEAN_QUALITY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_front_mean_quality. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_front_mean_quality=*) + [ -n "$VIASH_PAR_CUT_FRONT_MEAN_QUALITY" ] && ViashError Bad arguments for option \'--cut_front_mean_quality=*\': \'$VIASH_PAR_CUT_FRONT_MEAN_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_FRONT_MEAN_QUALITY=$(ViashRemoveFlags "$1") + shift 1 + ;; + --cut_tail_window_size) + [ -n "$VIASH_PAR_CUT_TAIL_WINDOW_SIZE" ] && ViashError Bad arguments for option \'--cut_tail_window_size\': \'$VIASH_PAR_CUT_TAIL_WINDOW_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_TAIL_WINDOW_SIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_tail_window_size. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_tail_window_size=*) + [ -n "$VIASH_PAR_CUT_TAIL_WINDOW_SIZE" ] && ViashError Bad arguments for option \'--cut_tail_window_size=*\': \'$VIASH_PAR_CUT_TAIL_WINDOW_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_TAIL_WINDOW_SIZE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --cut_tail_mean_quality) + [ -n "$VIASH_PAR_CUT_TAIL_MEAN_QUALITY" ] && ViashError Bad arguments for option \'--cut_tail_mean_quality\': \'$VIASH_PAR_CUT_TAIL_MEAN_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_TAIL_MEAN_QUALITY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_tail_mean_quality. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_tail_mean_quality=*) + [ -n "$VIASH_PAR_CUT_TAIL_MEAN_QUALITY" ] && ViashError Bad arguments for option \'--cut_tail_mean_quality=*\': \'$VIASH_PAR_CUT_TAIL_MEAN_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_TAIL_MEAN_QUALITY=$(ViashRemoveFlags "$1") + shift 1 + ;; + --cut_right_window_size) + [ -n "$VIASH_PAR_CUT_RIGHT_WINDOW_SIZE" ] && ViashError Bad arguments for option \'--cut_right_window_size\': \'$VIASH_PAR_CUT_RIGHT_WINDOW_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_RIGHT_WINDOW_SIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_right_window_size. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_right_window_size=*) + [ -n "$VIASH_PAR_CUT_RIGHT_WINDOW_SIZE" ] && ViashError Bad arguments for option \'--cut_right_window_size=*\': \'$VIASH_PAR_CUT_RIGHT_WINDOW_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_RIGHT_WINDOW_SIZE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --cut_right_mean_quality) + [ -n "$VIASH_PAR_CUT_RIGHT_MEAN_QUALITY" ] && ViashError Bad arguments for option \'--cut_right_mean_quality\': \'$VIASH_PAR_CUT_RIGHT_MEAN_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_RIGHT_MEAN_QUALITY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cut_right_mean_quality. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cut_right_mean_quality=*) + [ -n "$VIASH_PAR_CUT_RIGHT_MEAN_QUALITY" ] && ViashError Bad arguments for option \'--cut_right_mean_quality=*\': \'$VIASH_PAR_CUT_RIGHT_MEAN_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUT_RIGHT_MEAN_QUALITY=$(ViashRemoveFlags "$1") + shift 1 + ;; + --disable_quality_filtering) + [ -n "$VIASH_PAR_DISABLE_QUALITY_FILTERING" ] && ViashError Bad arguments for option \'--disable_quality_filtering\': \'$VIASH_PAR_DISABLE_QUALITY_FILTERING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISABLE_QUALITY_FILTERING=true + shift 1 + ;; + -Q) + [ -n "$VIASH_PAR_DISABLE_QUALITY_FILTERING" ] && ViashError Bad arguments for option \'-Q\': \'$VIASH_PAR_DISABLE_QUALITY_FILTERING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISABLE_QUALITY_FILTERING=true + shift 1 + ;; + --qualified_quality_phred) + [ -n "$VIASH_PAR_QUALIFIED_QUALITY_PHRED" ] && ViashError Bad arguments for option \'--qualified_quality_phred\': \'$VIASH_PAR_QUALIFIED_QUALITY_PHRED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALIFIED_QUALITY_PHRED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --qualified_quality_phred. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --qualified_quality_phred=*) + [ -n "$VIASH_PAR_QUALIFIED_QUALITY_PHRED" ] && ViashError Bad arguments for option \'--qualified_quality_phred=*\': \'$VIASH_PAR_QUALIFIED_QUALITY_PHRED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALIFIED_QUALITY_PHRED=$(ViashRemoveFlags "$1") + shift 1 + ;; + -q) + [ -n "$VIASH_PAR_QUALIFIED_QUALITY_PHRED" ] && ViashError Bad arguments for option \'-q\': \'$VIASH_PAR_QUALIFIED_QUALITY_PHRED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALIFIED_QUALITY_PHRED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -q. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unqualified_percent_limit) + [ -n "$VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT" ] && ViashError Bad arguments for option \'--unqualified_percent_limit\': \'$VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --unqualified_percent_limit. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unqualified_percent_limit=*) + [ -n "$VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT" ] && ViashError Bad arguments for option \'--unqualified_percent_limit=*\': \'$VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -u) + [ -n "$VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT" ] && ViashError Bad arguments for option \'-u\': \'$VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -u. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --n_base_limit) + [ -n "$VIASH_PAR_N_BASE_LIMIT" ] && ViashError Bad arguments for option \'--n_base_limit\': \'$VIASH_PAR_N_BASE_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_N_BASE_LIMIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --n_base_limit. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --n_base_limit=*) + [ -n "$VIASH_PAR_N_BASE_LIMIT" ] && ViashError Bad arguments for option \'--n_base_limit=*\': \'$VIASH_PAR_N_BASE_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_N_BASE_LIMIT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -n) + [ -n "$VIASH_PAR_N_BASE_LIMIT" ] && ViashError Bad arguments for option \'-n\': \'$VIASH_PAR_N_BASE_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_N_BASE_LIMIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -n. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --average_qual) + [ -n "$VIASH_PAR_AVERAGE_QUAL" ] && ViashError Bad arguments for option \'--average_qual\': \'$VIASH_PAR_AVERAGE_QUAL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AVERAGE_QUAL="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --average_qual. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --average_qual=*) + [ -n "$VIASH_PAR_AVERAGE_QUAL" ] && ViashError Bad arguments for option \'--average_qual=*\': \'$VIASH_PAR_AVERAGE_QUAL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AVERAGE_QUAL=$(ViashRemoveFlags "$1") + shift 1 + ;; + -e) + [ -n "$VIASH_PAR_AVERAGE_QUAL" ] && ViashError Bad arguments for option \'-e\': \'$VIASH_PAR_AVERAGE_QUAL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AVERAGE_QUAL="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -e. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --disable_length_filtering) + [ -n "$VIASH_PAR_DISABLE_LENGTH_FILTERING" ] && ViashError Bad arguments for option \'--disable_length_filtering\': \'$VIASH_PAR_DISABLE_LENGTH_FILTERING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISABLE_LENGTH_FILTERING=true + shift 1 + ;; + -L) + [ -n "$VIASH_PAR_DISABLE_LENGTH_FILTERING" ] && ViashError Bad arguments for option \'-L\': \'$VIASH_PAR_DISABLE_LENGTH_FILTERING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISABLE_LENGTH_FILTERING=true + shift 1 + ;; + --length_required) + [ -n "$VIASH_PAR_LENGTH_REQUIRED" ] && ViashError Bad arguments for option \'--length_required\': \'$VIASH_PAR_LENGTH_REQUIRED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH_REQUIRED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --length_required. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --length_required=*) + [ -n "$VIASH_PAR_LENGTH_REQUIRED" ] && ViashError Bad arguments for option \'--length_required=*\': \'$VIASH_PAR_LENGTH_REQUIRED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH_REQUIRED=$(ViashRemoveFlags "$1") + shift 1 + ;; + -l) + [ -n "$VIASH_PAR_LENGTH_REQUIRED" ] && ViashError Bad arguments for option \'-l\': \'$VIASH_PAR_LENGTH_REQUIRED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH_REQUIRED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -l. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --length_limit) + [ -n "$VIASH_PAR_LENGTH_LIMIT" ] && ViashError Bad arguments for option \'--length_limit\': \'$VIASH_PAR_LENGTH_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH_LIMIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --length_limit. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --length_limit=*) + [ -n "$VIASH_PAR_LENGTH_LIMIT" ] && ViashError Bad arguments for option \'--length_limit=*\': \'$VIASH_PAR_LENGTH_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH_LIMIT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --low_complexity_filter) + [ -n "$VIASH_PAR_LOW_COMPLEXITY_FILTER" ] && ViashError Bad arguments for option \'--low_complexity_filter\': \'$VIASH_PAR_LOW_COMPLEXITY_FILTER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LOW_COMPLEXITY_FILTER=true + shift 1 + ;; + -y) + [ -n "$VIASH_PAR_LOW_COMPLEXITY_FILTER" ] && ViashError Bad arguments for option \'-y\': \'$VIASH_PAR_LOW_COMPLEXITY_FILTER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LOW_COMPLEXITY_FILTER=true + shift 1 + ;; + --complexity_threshold) + [ -n "$VIASH_PAR_COMPLEXITY_THRESHOLD" ] && ViashError Bad arguments for option \'--complexity_threshold\': \'$VIASH_PAR_COMPLEXITY_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPLEXITY_THRESHOLD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --complexity_threshold. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --complexity_threshold=*) + [ -n "$VIASH_PAR_COMPLEXITY_THRESHOLD" ] && ViashError Bad arguments for option \'--complexity_threshold=*\': \'$VIASH_PAR_COMPLEXITY_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPLEXITY_THRESHOLD=$(ViashRemoveFlags "$1") + shift 1 + ;; + -Y) + [ -n "$VIASH_PAR_COMPLEXITY_THRESHOLD" ] && ViashError Bad arguments for option \'-Y\': \'$VIASH_PAR_COMPLEXITY_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPLEXITY_THRESHOLD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -Y. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --filter_by_index1) + [ -n "$VIASH_PAR_FILTER_BY_INDEX1" ] && ViashError Bad arguments for option \'--filter_by_index1\': \'$VIASH_PAR_FILTER_BY_INDEX1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_BY_INDEX1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --filter_by_index1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --filter_by_index1=*) + [ -n "$VIASH_PAR_FILTER_BY_INDEX1" ] && ViashError Bad arguments for option \'--filter_by_index1=*\': \'$VIASH_PAR_FILTER_BY_INDEX1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_BY_INDEX1=$(ViashRemoveFlags "$1") + shift 1 + ;; + --filter_by_index2) + [ -n "$VIASH_PAR_FILTER_BY_INDEX2" ] && ViashError Bad arguments for option \'--filter_by_index2\': \'$VIASH_PAR_FILTER_BY_INDEX2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_BY_INDEX2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --filter_by_index2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --filter_by_index2=*) + [ -n "$VIASH_PAR_FILTER_BY_INDEX2" ] && ViashError Bad arguments for option \'--filter_by_index2=*\': \'$VIASH_PAR_FILTER_BY_INDEX2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_BY_INDEX2=$(ViashRemoveFlags "$1") + shift 1 + ;; + --filter_by_index_threshold) + [ -n "$VIASH_PAR_FILTER_BY_INDEX_THRESHOLD" ] && ViashError Bad arguments for option \'--filter_by_index_threshold\': \'$VIASH_PAR_FILTER_BY_INDEX_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_BY_INDEX_THRESHOLD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --filter_by_index_threshold. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --filter_by_index_threshold=*) + [ -n "$VIASH_PAR_FILTER_BY_INDEX_THRESHOLD" ] && ViashError Bad arguments for option \'--filter_by_index_threshold=*\': \'$VIASH_PAR_FILTER_BY_INDEX_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_BY_INDEX_THRESHOLD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --correction) + [ -n "$VIASH_PAR_CORRECTION" ] && ViashError Bad arguments for option \'--correction\': \'$VIASH_PAR_CORRECTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CORRECTION=true + shift 1 + ;; + -c) + [ -n "$VIASH_PAR_CORRECTION" ] && ViashError Bad arguments for option \'-c\': \'$VIASH_PAR_CORRECTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CORRECTION=true + shift 1 + ;; + --overlap_len_require) + [ -n "$VIASH_PAR_OVERLAP_LEN_REQUIRE" ] && ViashError Bad arguments for option \'--overlap_len_require\': \'$VIASH_PAR_OVERLAP_LEN_REQUIRE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAP_LEN_REQUIRE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --overlap_len_require. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --overlap_len_require=*) + [ -n "$VIASH_PAR_OVERLAP_LEN_REQUIRE" ] && ViashError Bad arguments for option \'--overlap_len_require=*\': \'$VIASH_PAR_OVERLAP_LEN_REQUIRE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAP_LEN_REQUIRE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --overlap_diff_limit) + [ -n "$VIASH_PAR_OVERLAP_DIFF_LIMIT" ] && ViashError Bad arguments for option \'--overlap_diff_limit\': \'$VIASH_PAR_OVERLAP_DIFF_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAP_DIFF_LIMIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --overlap_diff_limit. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --overlap_diff_limit=*) + [ -n "$VIASH_PAR_OVERLAP_DIFF_LIMIT" ] && ViashError Bad arguments for option \'--overlap_diff_limit=*\': \'$VIASH_PAR_OVERLAP_DIFF_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAP_DIFF_LIMIT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --overlap_diff_percent_limit) + [ -n "$VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT" ] && ViashError Bad arguments for option \'--overlap_diff_percent_limit\': \'$VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --overlap_diff_percent_limit. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --overlap_diff_percent_limit=*) + [ -n "$VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT" ] && ViashError Bad arguments for option \'--overlap_diff_percent_limit=*\': \'$VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --umi) + [ -n "$VIASH_PAR_UMI" ] && ViashError Bad arguments for option \'--umi\': \'$VIASH_PAR_UMI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI=true + shift 1 + ;; + -U) + [ -n "$VIASH_PAR_UMI" ] && ViashError Bad arguments for option \'-U\': \'$VIASH_PAR_UMI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI=true + shift 1 + ;; + --umi_loc) + [ -n "$VIASH_PAR_UMI_LOC" ] && ViashError Bad arguments for option \'--umi_loc\': \'$VIASH_PAR_UMI_LOC\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI_LOC="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --umi_loc. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --umi_loc=*) + [ -n "$VIASH_PAR_UMI_LOC" ] && ViashError Bad arguments for option \'--umi_loc=*\': \'$VIASH_PAR_UMI_LOC\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI_LOC=$(ViashRemoveFlags "$1") + shift 1 + ;; + --umi_len) + [ -n "$VIASH_PAR_UMI_LEN" ] && ViashError Bad arguments for option \'--umi_len\': \'$VIASH_PAR_UMI_LEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI_LEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --umi_len. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --umi_len=*) + [ -n "$VIASH_PAR_UMI_LEN" ] && ViashError Bad arguments for option \'--umi_len=*\': \'$VIASH_PAR_UMI_LEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI_LEN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --umi_prefix) + [ -n "$VIASH_PAR_UMI_PREFIX" ] && ViashError Bad arguments for option \'--umi_prefix\': \'$VIASH_PAR_UMI_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI_PREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --umi_prefix. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --umi_prefix=*) + [ -n "$VIASH_PAR_UMI_PREFIX" ] && ViashError Bad arguments for option \'--umi_prefix=*\': \'$VIASH_PAR_UMI_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI_PREFIX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --umi_skip) + [ -n "$VIASH_PAR_UMI_SKIP" ] && ViashError Bad arguments for option \'--umi_skip\': \'$VIASH_PAR_UMI_SKIP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI_SKIP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --umi_skip. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --umi_skip=*) + [ -n "$VIASH_PAR_UMI_SKIP" ] && ViashError Bad arguments for option \'--umi_skip=*\': \'$VIASH_PAR_UMI_SKIP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI_SKIP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --umi_delim) + [ -n "$VIASH_PAR_UMI_DELIM" ] && ViashError Bad arguments for option \'--umi_delim\': \'$VIASH_PAR_UMI_DELIM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI_DELIM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --umi_delim. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --umi_delim=*) + [ -n "$VIASH_PAR_UMI_DELIM" ] && ViashError Bad arguments for option \'--umi_delim=*\': \'$VIASH_PAR_UMI_DELIM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UMI_DELIM=$(ViashRemoveFlags "$1") + shift 1 + ;; + --overrepresentation_analysis) + [ -n "$VIASH_PAR_OVERREPRESENTATION_ANALYSIS" ] && ViashError Bad arguments for option \'--overrepresentation_analysis\': \'$VIASH_PAR_OVERREPRESENTATION_ANALYSIS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERREPRESENTATION_ANALYSIS=true + shift 1 + ;; + -p) + [ -n "$VIASH_PAR_OVERREPRESENTATION_ANALYSIS" ] && ViashError Bad arguments for option \'-p\': \'$VIASH_PAR_OVERREPRESENTATION_ANALYSIS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERREPRESENTATION_ANALYSIS=true + shift 1 + ;; + --overrepresentation_sampling) + [ -n "$VIASH_PAR_OVERREPRESENTATION_SAMPLING" ] && ViashError Bad arguments for option \'--overrepresentation_sampling\': \'$VIASH_PAR_OVERREPRESENTATION_SAMPLING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERREPRESENTATION_SAMPLING="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --overrepresentation_sampling. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --overrepresentation_sampling=*) + [ -n "$VIASH_PAR_OVERREPRESENTATION_SAMPLING" ] && ViashError Bad arguments for option \'--overrepresentation_sampling=*\': \'$VIASH_PAR_OVERREPRESENTATION_SAMPLING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERREPRESENTATION_SAMPLING=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/fastp:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_IN1+x} ]; then + ViashError '--in1' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUT1+x} ]; then + ViashError '--out1' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_DISABLE_ADAPTER_TRIMMING+x} ]; then + VIASH_PAR_DISABLE_ADAPTER_TRIMMING="false" +fi +if [ -z ${VIASH_PAR_DETECT_ADAPTER_FOR_PE+x} ]; then + VIASH_PAR_DETECT_ADAPTER_FOR_PE="false" +fi +if [ -z ${VIASH_PAR_MERGE+x} ]; then + VIASH_PAR_MERGE="false" +fi +if [ -z ${VIASH_PAR_INCLUDE_UNMERGED+x} ]; then + VIASH_PAR_INCLUDE_UNMERGED="false" +fi +if [ -z ${VIASH_PAR_INTERLEAVED_IN+x} ]; then + VIASH_PAR_INTERLEAVED_IN="false" +fi +if [ -z ${VIASH_PAR_FIX_MGI_ID+x} ]; then + VIASH_PAR_FIX_MGI_ID="false" +fi +if [ -z ${VIASH_PAR_PHRED64+x} ]; then + VIASH_PAR_PHRED64="false" +fi +if [ -z ${VIASH_PAR_DONT_OVERWRITE+x} ]; then + VIASH_PAR_DONT_OVERWRITE="false" +fi +if [ -z ${VIASH_PAR_VERBOSE+x} ]; then + VIASH_PAR_VERBOSE="false" +fi +if [ -z ${VIASH_PAR_DEDUP+x} ]; then + VIASH_PAR_DEDUP="false" +fi +if [ -z ${VIASH_PAR_DONT_EVAL_DUPLICATION+x} ]; then + VIASH_PAR_DONT_EVAL_DUPLICATION="false" +fi +if [ -z ${VIASH_PAR_TRIM_POLY_G+x} ]; then + VIASH_PAR_TRIM_POLY_G="false" +fi +if [ -z ${VIASH_PAR_DISABLE_TRIM_POLY_G+x} ]; then + VIASH_PAR_DISABLE_TRIM_POLY_G="false" +fi +if [ -z ${VIASH_PAR_TRIM_POLY_X+x} ]; then + VIASH_PAR_TRIM_POLY_X="false" +fi +if [ -z ${VIASH_PAR_DISABLE_QUALITY_FILTERING+x} ]; then + VIASH_PAR_DISABLE_QUALITY_FILTERING="false" +fi +if [ -z ${VIASH_PAR_DISABLE_LENGTH_FILTERING+x} ]; then + VIASH_PAR_DISABLE_LENGTH_FILTERING="false" +fi +if [ -z ${VIASH_PAR_LOW_COMPLEXITY_FILTER+x} ]; then + VIASH_PAR_LOW_COMPLEXITY_FILTER="false" +fi +if [ -z ${VIASH_PAR_CORRECTION+x} ]; then + VIASH_PAR_CORRECTION="false" +fi +if [ -z ${VIASH_PAR_UMI+x} ]; then + VIASH_PAR_UMI="false" +fi +if [ -z ${VIASH_PAR_OVERREPRESENTATION_ANALYSIS+x} ]; then + VIASH_PAR_OVERREPRESENTATION_ANALYSIS="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_IN1" ] && [ ! -e "$VIASH_PAR_IN1" ]; then + ViashError "Input file '$VIASH_PAR_IN1' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_IN2" ] && [ ! -e "$VIASH_PAR_IN2" ]; then + ViashError "Input file '$VIASH_PAR_IN2' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_ADAPTER_FASTA" ] && [ ! -e "$VIASH_PAR_ADAPTER_FASTA" ]; then + ViashError "Input file '$VIASH_PAR_ADAPTER_FASTA' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_FILTER_BY_INDEX1" ] && [ ! -e "$VIASH_PAR_FILTER_BY_INDEX1" ]; then + ViashError "Input file '$VIASH_PAR_FILTER_BY_INDEX1' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_FILTER_BY_INDEX2" ] && [ ! -e "$VIASH_PAR_FILTER_BY_INDEX2" ]; then + ViashError "Input file '$VIASH_PAR_FILTER_BY_INDEX2' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_DISABLE_ADAPTER_TRIMMING" ]]; then + if ! [[ "$VIASH_PAR_DISABLE_ADAPTER_TRIMMING" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--disable_adapter_trimming' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DETECT_ADAPTER_FOR_PE" ]]; then + if ! [[ "$VIASH_PAR_DETECT_ADAPTER_FOR_PE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--detect_adapter_for_pe' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TRIM_FRONT1" ]]; then + if ! [[ "$VIASH_PAR_TRIM_FRONT1" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--trim_front1' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TRIM_TAIL1" ]]; then + if ! [[ "$VIASH_PAR_TRIM_TAIL1" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--trim_tail1' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_LEN1" ]]; then + if ! [[ "$VIASH_PAR_MAX_LEN1" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_len1' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_MAX_LEN1 -lt 0 ]]; then + ViashError '--max_len1' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TRIM_FRONT2" ]]; then + if ! [[ "$VIASH_PAR_TRIM_FRONT2" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--trim_front2' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TRIM_TAIL2" ]]; then + if ! [[ "$VIASH_PAR_TRIM_TAIL2" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--trim_tail2' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_LEN2" ]]; then + if ! [[ "$VIASH_PAR_MAX_LEN2" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_len2' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_MAX_LEN2 -lt 0 ]]; then + ViashError '--max_len2' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MERGE" ]]; then + if ! [[ "$VIASH_PAR_MERGE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--merge' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_INCLUDE_UNMERGED" ]]; then + if ! [[ "$VIASH_PAR_INCLUDE_UNMERGED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--include_unmerged' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_INTERLEAVED_IN" ]]; then + if ! [[ "$VIASH_PAR_INTERLEAVED_IN" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--interleaved_in' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FIX_MGI_ID" ]]; then + if ! [[ "$VIASH_PAR_FIX_MGI_ID" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--fix_mgi_id' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PHRED64" ]]; then + if ! [[ "$VIASH_PAR_PHRED64" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--phred64' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_COMPRESSION" ]]; then + if ! [[ "$VIASH_PAR_COMPRESSION" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--compression' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_COMPRESSION -lt 1 ]]; then + ViashError '--compression' has be more than or equal to 1. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_COMPRESSION -gt 9 ]]; then + ViashError '--compression' has be less than or equal to 9. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DONT_OVERWRITE" ]]; then + if ! [[ "$VIASH_PAR_DONT_OVERWRITE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--dont_overwrite' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_VERBOSE" ]]; then + if ! [[ "$VIASH_PAR_VERBOSE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--verbose' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_READS_TO_PROCESS" ]]; then + if ! [[ "$VIASH_PAR_READS_TO_PROCESS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--reads_to_process' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_READS_TO_PROCESS -lt 0 ]]; then + ViashError '--reads_to_process' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DEDUP" ]]; then + if ! [[ "$VIASH_PAR_DEDUP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--dedup' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DUP_CALC_ACCURACY" ]]; then + if ! [[ "$VIASH_PAR_DUP_CALC_ACCURACY" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--dup_calc_accuracy' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_DUP_CALC_ACCURACY -lt 1 ]]; then + ViashError '--dup_calc_accuracy' has be more than or equal to 1. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_DUP_CALC_ACCURACY -gt 6 ]]; then + ViashError '--dup_calc_accuracy' has be less than or equal to 6. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DONT_EVAL_DUPLICATION" ]]; then + if ! [[ "$VIASH_PAR_DONT_EVAL_DUPLICATION" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--dont_eval_duplication' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TRIM_POLY_G" ]]; then + if ! [[ "$VIASH_PAR_TRIM_POLY_G" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--trim_poly_g' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_POLY_G_MIN_LEN" ]]; then + if ! [[ "$VIASH_PAR_POLY_G_MIN_LEN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--poly_g_min_len' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_POLY_G_MIN_LEN -lt 1 ]]; then + ViashError '--poly_g_min_len' has be more than or equal to 1. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DISABLE_TRIM_POLY_G" ]]; then + if ! [[ "$VIASH_PAR_DISABLE_TRIM_POLY_G" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--disable_trim_poly_g' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TRIM_POLY_X" ]]; then + if ! [[ "$VIASH_PAR_TRIM_POLY_X" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--trim_poly_x' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_POLY_X_MIN_LEN" ]]; then + if ! [[ "$VIASH_PAR_POLY_X_MIN_LEN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--poly_x_min_len' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_POLY_X_MIN_LEN -lt 1 ]]; then + ViashError '--poly_x_min_len' has be more than or equal to 1. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUT_FRONT" ]]; then + if ! [[ "$VIASH_PAR_CUT_FRONT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_front' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUT_TAIL" ]]; then + if ! [[ "$VIASH_PAR_CUT_TAIL" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_tail' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUT_RIGHT" ]]; then + if ! [[ "$VIASH_PAR_CUT_RIGHT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_right' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUT_WINDOW_SIZE" ]]; then + if ! [[ "$VIASH_PAR_CUT_WINDOW_SIZE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_window_size' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_CUT_WINDOW_SIZE -lt 1 ]]; then + ViashError '--cut_window_size' has be more than or equal to 1. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUT_MEAN_QUALITY" ]]; then + if ! [[ "$VIASH_PAR_CUT_MEAN_QUALITY" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_mean_quality' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_CUT_MEAN_QUALITY -lt 0 ]]; then + ViashError '--cut_mean_quality' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUT_FRONT_WINDOW_SIZE" ]]; then + if ! [[ "$VIASH_PAR_CUT_FRONT_WINDOW_SIZE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_front_window_size' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_CUT_FRONT_WINDOW_SIZE -lt 1 ]]; then + ViashError '--cut_front_window_size' has be more than or equal to 1. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUT_FRONT_MEAN_QUALITY" ]]; then + if ! [[ "$VIASH_PAR_CUT_FRONT_MEAN_QUALITY" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_front_mean_quality' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_CUT_FRONT_MEAN_QUALITY -lt 0 ]]; then + ViashError '--cut_front_mean_quality' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUT_TAIL_WINDOW_SIZE" ]]; then + if ! [[ "$VIASH_PAR_CUT_TAIL_WINDOW_SIZE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_tail_window_size' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_CUT_TAIL_WINDOW_SIZE -lt 1 ]]; then + ViashError '--cut_tail_window_size' has be more than or equal to 1. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUT_TAIL_MEAN_QUALITY" ]]; then + if ! [[ "$VIASH_PAR_CUT_TAIL_MEAN_QUALITY" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_tail_mean_quality' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_CUT_TAIL_MEAN_QUALITY -lt 0 ]]; then + ViashError '--cut_tail_mean_quality' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUT_RIGHT_WINDOW_SIZE" ]]; then + if ! [[ "$VIASH_PAR_CUT_RIGHT_WINDOW_SIZE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_right_window_size' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_CUT_RIGHT_WINDOW_SIZE -lt 1 ]]; then + ViashError '--cut_right_window_size' has be more than or equal to 1. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUT_RIGHT_MEAN_QUALITY" ]]; then + if ! [[ "$VIASH_PAR_CUT_RIGHT_MEAN_QUALITY" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cut_right_mean_quality' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_CUT_RIGHT_MEAN_QUALITY -lt 0 ]]; then + ViashError '--cut_right_mean_quality' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DISABLE_QUALITY_FILTERING" ]]; then + if ! [[ "$VIASH_PAR_DISABLE_QUALITY_FILTERING" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--disable_quality_filtering' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_QUALIFIED_QUALITY_PHRED" ]]; then + if ! [[ "$VIASH_PAR_QUALIFIED_QUALITY_PHRED" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--qualified_quality_phred' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_QUALIFIED_QUALITY_PHRED -lt 0 ]]; then + ViashError '--qualified_quality_phred' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT" ]]; then + if ! [[ "$VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--unqualified_percent_limit' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT -lt 0 ]]; then + ViashError '--unqualified_percent_limit' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT -gt 100 ]]; then + ViashError '--unqualified_percent_limit' has be less than or equal to 100. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_N_BASE_LIMIT" ]]; then + if ! [[ "$VIASH_PAR_N_BASE_LIMIT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--n_base_limit' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_N_BASE_LIMIT -lt 0 ]]; then + ViashError '--n_base_limit' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_AVERAGE_QUAL" ]]; then + if ! [[ "$VIASH_PAR_AVERAGE_QUAL" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--average_qual' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_AVERAGE_QUAL -lt 0 ]]; then + ViashError '--average_qual' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DISABLE_LENGTH_FILTERING" ]]; then + if ! [[ "$VIASH_PAR_DISABLE_LENGTH_FILTERING" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--disable_length_filtering' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LENGTH_REQUIRED" ]]; then + if ! [[ "$VIASH_PAR_LENGTH_REQUIRED" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--length_required' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_LENGTH_REQUIRED -lt 0 ]]; then + ViashError '--length_required' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LENGTH_LIMIT" ]]; then + if ! [[ "$VIASH_PAR_LENGTH_LIMIT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--length_limit' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_LENGTH_LIMIT -lt 0 ]]; then + ViashError '--length_limit' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LOW_COMPLEXITY_FILTER" ]]; then + if ! [[ "$VIASH_PAR_LOW_COMPLEXITY_FILTER" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--low_complexity_filter' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_COMPLEXITY_THRESHOLD" ]]; then + if ! [[ "$VIASH_PAR_COMPLEXITY_THRESHOLD" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--complexity_threshold' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_COMPLEXITY_THRESHOLD -lt 0 ]]; then + ViashError '--complexity_threshold' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FILTER_BY_INDEX_THRESHOLD" ]]; then + if ! [[ "$VIASH_PAR_FILTER_BY_INDEX_THRESHOLD" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--filter_by_index_threshold' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_FILTER_BY_INDEX_THRESHOLD -lt 0 ]]; then + ViashError '--filter_by_index_threshold' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CORRECTION" ]]; then + if ! [[ "$VIASH_PAR_CORRECTION" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--correction' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OVERLAP_LEN_REQUIRE" ]]; then + if ! [[ "$VIASH_PAR_OVERLAP_LEN_REQUIRE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--overlap_len_require' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_OVERLAP_LEN_REQUIRE -lt 0 ]]; then + ViashError '--overlap_len_require' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OVERLAP_DIFF_LIMIT" ]]; then + if ! [[ "$VIASH_PAR_OVERLAP_DIFF_LIMIT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--overlap_diff_limit' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_OVERLAP_DIFF_LIMIT -lt 0 ]]; then + ViashError '--overlap_diff_limit' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT" ]]; then + if ! [[ "$VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--overlap_diff_percent_limit' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT -lt 0 ]]; then + ViashError '--overlap_diff_percent_limit' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT -gt 100 ]]; then + ViashError '--overlap_diff_percent_limit' has be less than or equal to 100. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_UMI" ]]; then + if ! [[ "$VIASH_PAR_UMI" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--umi' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_UMI_LEN" ]]; then + if ! [[ "$VIASH_PAR_UMI_LEN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--umi_len' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_UMI_LEN -lt 0 ]]; then + ViashError '--umi_len' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_UMI_SKIP" ]]; then + if ! [[ "$VIASH_PAR_UMI_SKIP" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--umi_skip' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_UMI_SKIP -lt 0 ]]; then + ViashError '--umi_skip' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OVERREPRESENTATION_ANALYSIS" ]]; then + if ! [[ "$VIASH_PAR_OVERREPRESENTATION_ANALYSIS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--overrepresentation_analysis' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OVERREPRESENTATION_SAMPLING" ]]; then + if ! [[ "$VIASH_PAR_OVERREPRESENTATION_SAMPLING" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--overrepresentation_sampling' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_OVERREPRESENTATION_SAMPLING -lt 1 ]]; then + ViashError '--overrepresentation_sampling' has be more than or equal to 1. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# check whether value is belongs to a set of choices +if [ ! -z "$VIASH_PAR_UMI_LOC" ]; then + VIASH_PAR_UMI_LOC_CHOICES=("index1;index2;read1;read2;per_index;per_read") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_UMI_LOC_CHOICES[*]};" =~ ";$VIASH_PAR_UMI_LOC;" ]]; then + ViashError '--umi_loc' specified value of \'$VIASH_PAR_UMI_LOC\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUT1" ] && [ ! -d "$(dirname "$VIASH_PAR_OUT1")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUT1")" +fi +if [ ! -z "$VIASH_PAR_OUT2" ] && [ ! -d "$(dirname "$VIASH_PAR_OUT2")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUT2")" +fi +if [ ! -z "$VIASH_PAR_UNPAIRED1" ] && [ ! -d "$(dirname "$VIASH_PAR_UNPAIRED1")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_UNPAIRED1")" +fi +if [ ! -z "$VIASH_PAR_UNPAIRED2" ] && [ ! -d "$(dirname "$VIASH_PAR_UNPAIRED2")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_UNPAIRED2")" +fi +if [ ! -z "$VIASH_PAR_FAILED_OUT" ] && [ ! -d "$(dirname "$VIASH_PAR_FAILED_OUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_FAILED_OUT")" +fi +if [ ! -z "$VIASH_PAR_OVERLAPPED_OUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OVERLAPPED_OUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OVERLAPPED_OUT")" +fi +if [ ! -z "$VIASH_PAR_JSON" ] && [ ! -d "$(dirname "$VIASH_PAR_JSON")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_JSON")" +fi +if [ ! -z "$VIASH_PAR_HTML" ] && [ ! -d "$(dirname "$VIASH_PAR_HTML")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_HTML")" +fi +if [ ! -z "$VIASH_PAR_MERGED_OUT" ] && [ ! -d "$(dirname "$VIASH_PAR_MERGED_OUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_MERGED_OUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_IN1" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_IN1")" ) + VIASH_PAR_IN1=$(ViashDockerAutodetectMount "$VIASH_PAR_IN1") +fi +if [ ! -z "$VIASH_PAR_IN2" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_IN2")" ) + VIASH_PAR_IN2=$(ViashDockerAutodetectMount "$VIASH_PAR_IN2") +fi +if [ ! -z "$VIASH_PAR_OUT1" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUT1")" ) + VIASH_PAR_OUT1=$(ViashDockerAutodetectMount "$VIASH_PAR_OUT1") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUT1" ) +fi +if [ ! -z "$VIASH_PAR_OUT2" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUT2")" ) + VIASH_PAR_OUT2=$(ViashDockerAutodetectMount "$VIASH_PAR_OUT2") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUT2" ) +fi +if [ ! -z "$VIASH_PAR_UNPAIRED1" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_UNPAIRED1")" ) + VIASH_PAR_UNPAIRED1=$(ViashDockerAutodetectMount "$VIASH_PAR_UNPAIRED1") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_UNPAIRED1" ) +fi +if [ ! -z "$VIASH_PAR_UNPAIRED2" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_UNPAIRED2")" ) + VIASH_PAR_UNPAIRED2=$(ViashDockerAutodetectMount "$VIASH_PAR_UNPAIRED2") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_UNPAIRED2" ) +fi +if [ ! -z "$VIASH_PAR_FAILED_OUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FAILED_OUT")" ) + VIASH_PAR_FAILED_OUT=$(ViashDockerAutodetectMount "$VIASH_PAR_FAILED_OUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_FAILED_OUT" ) +fi +if [ ! -z "$VIASH_PAR_OVERLAPPED_OUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OVERLAPPED_OUT")" ) + VIASH_PAR_OVERLAPPED_OUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OVERLAPPED_OUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OVERLAPPED_OUT" ) +fi +if [ ! -z "$VIASH_PAR_JSON" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_JSON")" ) + VIASH_PAR_JSON=$(ViashDockerAutodetectMount "$VIASH_PAR_JSON") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_JSON" ) +fi +if [ ! -z "$VIASH_PAR_HTML" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_HTML")" ) + VIASH_PAR_HTML=$(ViashDockerAutodetectMount "$VIASH_PAR_HTML") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_HTML" ) +fi +if [ ! -z "$VIASH_PAR_ADAPTER_FASTA" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_ADAPTER_FASTA")" ) + VIASH_PAR_ADAPTER_FASTA=$(ViashDockerAutodetectMount "$VIASH_PAR_ADAPTER_FASTA") +fi +if [ ! -z "$VIASH_PAR_MERGED_OUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_MERGED_OUT")" ) + VIASH_PAR_MERGED_OUT=$(ViashDockerAutodetectMount "$VIASH_PAR_MERGED_OUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_MERGED_OUT" ) +fi +if [ ! -z "$VIASH_PAR_FILTER_BY_INDEX1" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FILTER_BY_INDEX1")" ) + VIASH_PAR_FILTER_BY_INDEX1=$(ViashDockerAutodetectMount "$VIASH_PAR_FILTER_BY_INDEX1") +fi +if [ ! -z "$VIASH_PAR_FILTER_BY_INDEX2" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FILTER_BY_INDEX2")" ) + VIASH_PAR_FILTER_BY_INDEX2=$(ViashDockerAutodetectMount "$VIASH_PAR_FILTER_BY_INDEX2") +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-fastp-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_IN1+x} ]; then echo "${VIASH_PAR_IN1}" | sed "s#'#'\"'\"'#g;s#.*#par_in1='&'#" ; else echo "# par_in1="; fi ) +$( if [ ! -z ${VIASH_PAR_IN2+x} ]; then echo "${VIASH_PAR_IN2}" | sed "s#'#'\"'\"'#g;s#.*#par_in2='&'#" ; else echo "# par_in2="; fi ) +$( if [ ! -z ${VIASH_PAR_OUT1+x} ]; then echo "${VIASH_PAR_OUT1}" | sed "s#'#'\"'\"'#g;s#.*#par_out1='&'#" ; else echo "# par_out1="; fi ) +$( if [ ! -z ${VIASH_PAR_OUT2+x} ]; then echo "${VIASH_PAR_OUT2}" | sed "s#'#'\"'\"'#g;s#.*#par_out2='&'#" ; else echo "# par_out2="; fi ) +$( if [ ! -z ${VIASH_PAR_UNPAIRED1+x} ]; then echo "${VIASH_PAR_UNPAIRED1}" | sed "s#'#'\"'\"'#g;s#.*#par_unpaired1='&'#" ; else echo "# par_unpaired1="; fi ) +$( if [ ! -z ${VIASH_PAR_UNPAIRED2+x} ]; then echo "${VIASH_PAR_UNPAIRED2}" | sed "s#'#'\"'\"'#g;s#.*#par_unpaired2='&'#" ; else echo "# par_unpaired2="; fi ) +$( if [ ! -z ${VIASH_PAR_FAILED_OUT+x} ]; then echo "${VIASH_PAR_FAILED_OUT}" | sed "s#'#'\"'\"'#g;s#.*#par_failed_out='&'#" ; else echo "# par_failed_out="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAPPED_OUT+x} ]; then echo "${VIASH_PAR_OVERLAPPED_OUT}" | sed "s#'#'\"'\"'#g;s#.*#par_overlapped_out='&'#" ; else echo "# par_overlapped_out="; fi ) +$( if [ ! -z ${VIASH_PAR_JSON+x} ]; then echo "${VIASH_PAR_JSON}" | sed "s#'#'\"'\"'#g;s#.*#par_json='&'#" ; else echo "# par_json="; fi ) +$( if [ ! -z ${VIASH_PAR_HTML+x} ]; then echo "${VIASH_PAR_HTML}" | sed "s#'#'\"'\"'#g;s#.*#par_html='&'#" ; else echo "# par_html="; fi ) +$( if [ ! -z ${VIASH_PAR_REPORT_TITLE+x} ]; then echo "${VIASH_PAR_REPORT_TITLE}" | sed "s#'#'\"'\"'#g;s#.*#par_report_title='&'#" ; else echo "# par_report_title="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_ADAPTER_TRIMMING+x} ]; then echo "${VIASH_PAR_DISABLE_ADAPTER_TRIMMING}" | sed "s#'#'\"'\"'#g;s#.*#par_disable_adapter_trimming='&'#" ; else echo "# par_disable_adapter_trimming="; fi ) +$( if [ ! -z ${VIASH_PAR_DETECT_ADAPTER_FOR_PE+x} ]; then echo "${VIASH_PAR_DETECT_ADAPTER_FOR_PE}" | sed "s#'#'\"'\"'#g;s#.*#par_detect_adapter_for_pe='&'#" ; else echo "# par_detect_adapter_for_pe="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_SEQUENCE+x} ]; then echo "${VIASH_PAR_ADAPTER_SEQUENCE}" | sed "s#'#'\"'\"'#g;s#.*#par_adapter_sequence='&'#" ; else echo "# par_adapter_sequence="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_SEQUENCE_R2+x} ]; then echo "${VIASH_PAR_ADAPTER_SEQUENCE_R2}" | sed "s#'#'\"'\"'#g;s#.*#par_adapter_sequence_r2='&'#" ; else echo "# par_adapter_sequence_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_FASTA+x} ]; then echo "${VIASH_PAR_ADAPTER_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_adapter_fasta='&'#" ; else echo "# par_adapter_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_FRONT1+x} ]; then echo "${VIASH_PAR_TRIM_FRONT1}" | sed "s#'#'\"'\"'#g;s#.*#par_trim_front1='&'#" ; else echo "# par_trim_front1="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_TAIL1+x} ]; then echo "${VIASH_PAR_TRIM_TAIL1}" | sed "s#'#'\"'\"'#g;s#.*#par_trim_tail1='&'#" ; else echo "# par_trim_tail1="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_LEN1+x} ]; then echo "${VIASH_PAR_MAX_LEN1}" | sed "s#'#'\"'\"'#g;s#.*#par_max_len1='&'#" ; else echo "# par_max_len1="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_FRONT2+x} ]; then echo "${VIASH_PAR_TRIM_FRONT2}" | sed "s#'#'\"'\"'#g;s#.*#par_trim_front2='&'#" ; else echo "# par_trim_front2="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_TAIL2+x} ]; then echo "${VIASH_PAR_TRIM_TAIL2}" | sed "s#'#'\"'\"'#g;s#.*#par_trim_tail2='&'#" ; else echo "# par_trim_tail2="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_LEN2+x} ]; then echo "${VIASH_PAR_MAX_LEN2}" | sed "s#'#'\"'\"'#g;s#.*#par_max_len2='&'#" ; else echo "# par_max_len2="; fi ) +$( if [ ! -z ${VIASH_PAR_MERGE+x} ]; then echo "${VIASH_PAR_MERGE}" | sed "s#'#'\"'\"'#g;s#.*#par_merge='&'#" ; else echo "# par_merge="; fi ) +$( if [ ! -z ${VIASH_PAR_MERGED_OUT+x} ]; then echo "${VIASH_PAR_MERGED_OUT}" | sed "s#'#'\"'\"'#g;s#.*#par_merged_out='&'#" ; else echo "# par_merged_out="; fi ) +$( if [ ! -z ${VIASH_PAR_INCLUDE_UNMERGED+x} ]; then echo "${VIASH_PAR_INCLUDE_UNMERGED}" | sed "s#'#'\"'\"'#g;s#.*#par_include_unmerged='&'#" ; else echo "# par_include_unmerged="; fi ) +$( if [ ! -z ${VIASH_PAR_INTERLEAVED_IN+x} ]; then echo "${VIASH_PAR_INTERLEAVED_IN}" | sed "s#'#'\"'\"'#g;s#.*#par_interleaved_in='&'#" ; else echo "# par_interleaved_in="; fi ) +$( if [ ! -z ${VIASH_PAR_FIX_MGI_ID+x} ]; then echo "${VIASH_PAR_FIX_MGI_ID}" | sed "s#'#'\"'\"'#g;s#.*#par_fix_mgi_id='&'#" ; else echo "# par_fix_mgi_id="; fi ) +$( if [ ! -z ${VIASH_PAR_PHRED64+x} ]; then echo "${VIASH_PAR_PHRED64}" | sed "s#'#'\"'\"'#g;s#.*#par_phred64='&'#" ; else echo "# par_phred64="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPRESSION+x} ]; then echo "${VIASH_PAR_COMPRESSION}" | sed "s#'#'\"'\"'#g;s#.*#par_compression='&'#" ; else echo "# par_compression="; fi ) +$( if [ ! -z ${VIASH_PAR_DONT_OVERWRITE+x} ]; then echo "${VIASH_PAR_DONT_OVERWRITE}" | sed "s#'#'\"'\"'#g;s#.*#par_dont_overwrite='&'#" ; else echo "# par_dont_overwrite="; fi ) +$( if [ ! -z ${VIASH_PAR_VERBOSE+x} ]; then echo "${VIASH_PAR_VERBOSE}" | sed "s#'#'\"'\"'#g;s#.*#par_verbose='&'#" ; else echo "# par_verbose="; fi ) +$( if [ ! -z ${VIASH_PAR_READS_TO_PROCESS+x} ]; then echo "${VIASH_PAR_READS_TO_PROCESS}" | sed "s#'#'\"'\"'#g;s#.*#par_reads_to_process='&'#" ; else echo "# par_reads_to_process="; fi ) +$( if [ ! -z ${VIASH_PAR_DEDUP+x} ]; then echo "${VIASH_PAR_DEDUP}" | sed "s#'#'\"'\"'#g;s#.*#par_dedup='&'#" ; else echo "# par_dedup="; fi ) +$( if [ ! -z ${VIASH_PAR_DUP_CALC_ACCURACY+x} ]; then echo "${VIASH_PAR_DUP_CALC_ACCURACY}" | sed "s#'#'\"'\"'#g;s#.*#par_dup_calc_accuracy='&'#" ; else echo "# par_dup_calc_accuracy="; fi ) +$( if [ ! -z ${VIASH_PAR_DONT_EVAL_DUPLICATION+x} ]; then echo "${VIASH_PAR_DONT_EVAL_DUPLICATION}" | sed "s#'#'\"'\"'#g;s#.*#par_dont_eval_duplication='&'#" ; else echo "# par_dont_eval_duplication="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_POLY_G+x} ]; then echo "${VIASH_PAR_TRIM_POLY_G}" | sed "s#'#'\"'\"'#g;s#.*#par_trim_poly_g='&'#" ; else echo "# par_trim_poly_g="; fi ) +$( if [ ! -z ${VIASH_PAR_POLY_G_MIN_LEN+x} ]; then echo "${VIASH_PAR_POLY_G_MIN_LEN}" | sed "s#'#'\"'\"'#g;s#.*#par_poly_g_min_len='&'#" ; else echo "# par_poly_g_min_len="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_TRIM_POLY_G+x} ]; then echo "${VIASH_PAR_DISABLE_TRIM_POLY_G}" | sed "s#'#'\"'\"'#g;s#.*#par_disable_trim_poly_g='&'#" ; else echo "# par_disable_trim_poly_g="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_POLY_X+x} ]; then echo "${VIASH_PAR_TRIM_POLY_X}" | sed "s#'#'\"'\"'#g;s#.*#par_trim_poly_x='&'#" ; else echo "# par_trim_poly_x="; fi ) +$( if [ ! -z ${VIASH_PAR_POLY_X_MIN_LEN+x} ]; then echo "${VIASH_PAR_POLY_X_MIN_LEN}" | sed "s#'#'\"'\"'#g;s#.*#par_poly_x_min_len='&'#" ; else echo "# par_poly_x_min_len="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_FRONT+x} ]; then echo "${VIASH_PAR_CUT_FRONT}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_front='&'#" ; else echo "# par_cut_front="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_TAIL+x} ]; then echo "${VIASH_PAR_CUT_TAIL}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_tail='&'#" ; else echo "# par_cut_tail="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_RIGHT+x} ]; then echo "${VIASH_PAR_CUT_RIGHT}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_right='&'#" ; else echo "# par_cut_right="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_WINDOW_SIZE+x} ]; then echo "${VIASH_PAR_CUT_WINDOW_SIZE}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_window_size='&'#" ; else echo "# par_cut_window_size="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_MEAN_QUALITY+x} ]; then echo "${VIASH_PAR_CUT_MEAN_QUALITY}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_mean_quality='&'#" ; else echo "# par_cut_mean_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_FRONT_WINDOW_SIZE+x} ]; then echo "${VIASH_PAR_CUT_FRONT_WINDOW_SIZE}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_front_window_size='&'#" ; else echo "# par_cut_front_window_size="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_FRONT_MEAN_QUALITY+x} ]; then echo "${VIASH_PAR_CUT_FRONT_MEAN_QUALITY}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_front_mean_quality='&'#" ; else echo "# par_cut_front_mean_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_TAIL_WINDOW_SIZE+x} ]; then echo "${VIASH_PAR_CUT_TAIL_WINDOW_SIZE}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_tail_window_size='&'#" ; else echo "# par_cut_tail_window_size="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_TAIL_MEAN_QUALITY+x} ]; then echo "${VIASH_PAR_CUT_TAIL_MEAN_QUALITY}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_tail_mean_quality='&'#" ; else echo "# par_cut_tail_mean_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_RIGHT_WINDOW_SIZE+x} ]; then echo "${VIASH_PAR_CUT_RIGHT_WINDOW_SIZE}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_right_window_size='&'#" ; else echo "# par_cut_right_window_size="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_RIGHT_MEAN_QUALITY+x} ]; then echo "${VIASH_PAR_CUT_RIGHT_MEAN_QUALITY}" | sed "s#'#'\"'\"'#g;s#.*#par_cut_right_mean_quality='&'#" ; else echo "# par_cut_right_mean_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_QUALITY_FILTERING+x} ]; then echo "${VIASH_PAR_DISABLE_QUALITY_FILTERING}" | sed "s#'#'\"'\"'#g;s#.*#par_disable_quality_filtering='&'#" ; else echo "# par_disable_quality_filtering="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALIFIED_QUALITY_PHRED+x} ]; then echo "${VIASH_PAR_QUALIFIED_QUALITY_PHRED}" | sed "s#'#'\"'\"'#g;s#.*#par_qualified_quality_phred='&'#" ; else echo "# par_qualified_quality_phred="; fi ) +$( if [ ! -z ${VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT+x} ]; then echo "${VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT}" | sed "s#'#'\"'\"'#g;s#.*#par_unqualified_percent_limit='&'#" ; else echo "# par_unqualified_percent_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_N_BASE_LIMIT+x} ]; then echo "${VIASH_PAR_N_BASE_LIMIT}" | sed "s#'#'\"'\"'#g;s#.*#par_n_base_limit='&'#" ; else echo "# par_n_base_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_AVERAGE_QUAL+x} ]; then echo "${VIASH_PAR_AVERAGE_QUAL}" | sed "s#'#'\"'\"'#g;s#.*#par_average_qual='&'#" ; else echo "# par_average_qual="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_LENGTH_FILTERING+x} ]; then echo "${VIASH_PAR_DISABLE_LENGTH_FILTERING}" | sed "s#'#'\"'\"'#g;s#.*#par_disable_length_filtering='&'#" ; else echo "# par_disable_length_filtering="; fi ) +$( if [ ! -z ${VIASH_PAR_LENGTH_REQUIRED+x} ]; then echo "${VIASH_PAR_LENGTH_REQUIRED}" | sed "s#'#'\"'\"'#g;s#.*#par_length_required='&'#" ; else echo "# par_length_required="; fi ) +$( if [ ! -z ${VIASH_PAR_LENGTH_LIMIT+x} ]; then echo "${VIASH_PAR_LENGTH_LIMIT}" | sed "s#'#'\"'\"'#g;s#.*#par_length_limit='&'#" ; else echo "# par_length_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_LOW_COMPLEXITY_FILTER+x} ]; then echo "${VIASH_PAR_LOW_COMPLEXITY_FILTER}" | sed "s#'#'\"'\"'#g;s#.*#par_low_complexity_filter='&'#" ; else echo "# par_low_complexity_filter="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPLEXITY_THRESHOLD+x} ]; then echo "${VIASH_PAR_COMPLEXITY_THRESHOLD}" | sed "s#'#'\"'\"'#g;s#.*#par_complexity_threshold='&'#" ; else echo "# par_complexity_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTER_BY_INDEX1+x} ]; then echo "${VIASH_PAR_FILTER_BY_INDEX1}" | sed "s#'#'\"'\"'#g;s#.*#par_filter_by_index1='&'#" ; else echo "# par_filter_by_index1="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTER_BY_INDEX2+x} ]; then echo "${VIASH_PAR_FILTER_BY_INDEX2}" | sed "s#'#'\"'\"'#g;s#.*#par_filter_by_index2='&'#" ; else echo "# par_filter_by_index2="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTER_BY_INDEX_THRESHOLD+x} ]; then echo "${VIASH_PAR_FILTER_BY_INDEX_THRESHOLD}" | sed "s#'#'\"'\"'#g;s#.*#par_filter_by_index_threshold='&'#" ; else echo "# par_filter_by_index_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_CORRECTION+x} ]; then echo "${VIASH_PAR_CORRECTION}" | sed "s#'#'\"'\"'#g;s#.*#par_correction='&'#" ; else echo "# par_correction="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAP_LEN_REQUIRE+x} ]; then echo "${VIASH_PAR_OVERLAP_LEN_REQUIRE}" | sed "s#'#'\"'\"'#g;s#.*#par_overlap_len_require='&'#" ; else echo "# par_overlap_len_require="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAP_DIFF_LIMIT+x} ]; then echo "${VIASH_PAR_OVERLAP_DIFF_LIMIT}" | sed "s#'#'\"'\"'#g;s#.*#par_overlap_diff_limit='&'#" ; else echo "# par_overlap_diff_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT+x} ]; then echo "${VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT}" | sed "s#'#'\"'\"'#g;s#.*#par_overlap_diff_percent_limit='&'#" ; else echo "# par_overlap_diff_percent_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI+x} ]; then echo "${VIASH_PAR_UMI}" | sed "s#'#'\"'\"'#g;s#.*#par_umi='&'#" ; else echo "# par_umi="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI_LOC+x} ]; then echo "${VIASH_PAR_UMI_LOC}" | sed "s#'#'\"'\"'#g;s#.*#par_umi_loc='&'#" ; else echo "# par_umi_loc="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI_LEN+x} ]; then echo "${VIASH_PAR_UMI_LEN}" | sed "s#'#'\"'\"'#g;s#.*#par_umi_len='&'#" ; else echo "# par_umi_len="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI_PREFIX+x} ]; then echo "${VIASH_PAR_UMI_PREFIX}" | sed "s#'#'\"'\"'#g;s#.*#par_umi_prefix='&'#" ; else echo "# par_umi_prefix="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI_SKIP+x} ]; then echo "${VIASH_PAR_UMI_SKIP}" | sed "s#'#'\"'\"'#g;s#.*#par_umi_skip='&'#" ; else echo "# par_umi_skip="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI_DELIM+x} ]; then echo "${VIASH_PAR_UMI_DELIM}" | sed "s#'#'\"'\"'#g;s#.*#par_umi_delim='&'#" ; else echo "# par_umi_delim="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERREPRESENTATION_ANALYSIS+x} ]; then echo "${VIASH_PAR_OVERREPRESENTATION_ANALYSIS}" | sed "s#'#'\"'\"'#g;s#.*#par_overrepresentation_analysis='&'#" ; else echo "# par_overrepresentation_analysis="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERREPRESENTATION_SAMPLING+x} ]; then echo "${VIASH_PAR_OVERREPRESENTATION_SAMPLING}" | sed "s#'#'\"'\"'#g;s#.*#par_overrepresentation_sampling='&'#" ; else echo "# par_overrepresentation_sampling="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# disable flags +[[ "\$par_disable_adapter_trimming" == "false" ]] && unset par_disable_adapter_trimming +[[ "\$par_detect_adapter_for_pe" == "false" ]] && unset par_detect_adapter_for_pe +[[ "\$par_merge" == "false" ]] && unset par_merge +[[ "\$par_include_unmerged" == "false" ]] && unset par_include_unmerged +[[ "\$par_interleaved_in" == "false" ]] && unset par_interleaved_in +[[ "\$par_fix_mgi_id" == "false" ]] && unset par_fix_mgi_id +[[ "\$par_phred64" == "false" ]] && unset par_phred64 +[[ "\$par_dont_overwrite" == "false" ]] && unset par_dont_overwrite +[[ "\$par_verbose" == "false" ]] && unset par_verbose +[[ "\$par_dedup" == "false" ]] && unset par_dedup +[[ "\$par_dont_eval_duplication" == "false" ]] && unset par_dont_eval_duplication +[[ "\$par_trim_poly_g" == "false" ]] && unset par_trim_poly_g +[[ "\$par_disable_trim_poly_g" == "false" ]] && unset par_disable_trim_poly_g +[[ "\$par_trim_poly_x" == "false" ]] && unset par_trim_poly_x +[[ "\$par_disable_quality_filtering" == "false" ]] && unset par_disable_quality_filtering +[[ "\$par_disable_length_filtering" == "false" ]] && unset par_disable_length_filtering +[[ "\$par_low_complexity_filter" == "false" ]] && unset par_low_complexity_filter +[[ "\$par_umi" == "false" ]] && unset par_umi +[[ "\$par_overrepresentation_analysis" == "false" ]] && unset par_overrepresentation_analysis + +# run command +fastp \\ + -i "\$par_in1" \\ + -o "\$par_out1" \\ + \${par_in2:+--in2 "\${par_in2}"} \\ + \${par_out2:+--out2 "\${par_out2}"} \\ + \${par_unpaired1:+--unpaired1 "\${par_unpaired1}"} \\ + \${par_unpaired2:+--unpaired2 "\${par_unpaired2}"} \\ + \${par_failed_out:+--failed_out "\${par_failed_out}"} \\ + \${par_overlapped_out:+--overlapped_out "\${par_overlapped_out}"} \\ + \${par_json:+--json "\${par_json}"} \\ + \${par_html:+--html "\${par_html}"} \\ + \${par_report_title:+--report_title "\${par_report_title}"} \\ + \${par_disable_adapter_trimming:+--disable_adapter_trimming} \\ + \${par_detect_adapter_for_pe:+--detect_adapter_for_pe} \\ + \${par_adapter_sequence:+--adapter_sequence "\${par_adapter_sequence}"} \\ + \${par_adapter_sequence_r2:+--adapter_sequence_r2 "\${par_adapter_sequence_r2}"} \\ + \${par_adapter_fasta:+--adapter_fasta "\${par_adapter_fasta}"} \\ + \${par_trim_front1:+--trim_front1 "\${par_trim_front1}"} \\ + \${par_trim_tail1:+--trim_tail1 "\${par_trim_tail1}"} \\ + \${par_max_len1:+--max_len1 "\${par_max_len1}"} \\ + \${par_trim_front2:+--trim_front2 "\${par_trim_front2}"} \\ + \${par_trim_tail2:+--trim_tail2 "\${par_trim_tail2}"} \\ + \${par_max_len2:+--max_len2 "\${par_max_len2}"} \\ + \${par_merge:+--merge} \\ + \${par_merged_out:+--merged_out "\${par_merged_out}"} \\ + \${par_include_unmerged:+--include_unmerged} \\ + \${par_interleaved_in:+--interleaved_in} \\ + \${par_fix_mgi_id:+--fix_mgi_id} \\ + \${par_phred64:+--phred64} \\ + \${par_compression:+--compression "\${par_compression}"} \\ + \${par_dont_overwrite:+--dont_overwrite} \\ + \${par_verbose:+--verbose} \\ + \${par_reads_to_process:+--reads_to_process "\${par_reads_to_process}"} \\ + \${par_dedup:+--dedup} \\ + \${par_dup_calc_accuracy:+--dup_calc_accuracy "\${par_dup_calc_accuracy}"} \\ + \${par_dont_eval_duplication:+--dont_eval_duplication} \\ + \${par_trim_poly_g:+--trim_poly_g} \\ + \${par_poly_g_min_len:+--poly_g_min_len "\${par_poly_g_min_len}"} \\ + \${par_disable_trim_poly_g:+--disable_trim_poly_g} \\ + \${par_trim_poly_x:+--trim_poly_x} \\ + \${par_poly_x_min_len:+--poly_x_min_len "\${par_poly_x_min_len}"} \\ + \${par_cut_front:+--cut_front "\${par_cut_front}"} \\ + \${par_cut_tail:+--cut_tail "\${par_cut_tail}"} \\ + \${par_cut_right:+--cut_right "\${par_cut_right}"} \\ + \${par_cut_window_size:+--cut_window_size "\${par_cut_window_size}"} \\ + \${par_cut_mean_quality:+--cut_mean_quality "\${par_cut_mean_quality}"} \\ + \${par_cut_front_window_size:+--cut_front_window_size "\${par_cut_front_window_size}"} \\ + \${par_cut_front_mean_quality:+--cut_front_mean_quality "\${par_cut_front_mean_quality}"} \\ + \${par_cut_tail_window_size:+--cut_tail_window_size "\${par_cut_tail_window_size}"} \\ + \${par_cut_tail_mean_quality:+--cut_tail_mean_quality "\${par_cut_tail_mean_quality}"} \\ + \${par_cut_right_window_size:+--cut_right_window_size "\${par_cut_right_window_size}"} \\ + \${par_cut_right_mean_quality:+--cut_right_mean_quality "\${par_cut_right_mean_quality}"} \\ + \${par_disable_quality_filtering:+--disable_quality_filtering} \\ + \${par_qualified_quality_phred:+--qualified_quality_phred "\${par_qualified_quality_phred}"} \\ + \${par_unqualified_percent_limit:+--unqualified_percent_limit "\${par_unqualified_percent_limit}"} \\ + \${par_n_base_limit:+--n_base_limit "\${par_n_base_limit}"} \\ + \${par_average_qual:+--average_qual "\${par_average_qual}"} \\ + \${par_disable_length_filtering:+--disable_length_filtering} \\ + \${par_length_required:+--length_required "\${par_length_required}"} \\ + \${par_length_limit:+--length_limit "\${par_length_limit}"} \\ + \${par_low_complexity_filter:+--low_complexity_filter} \\ + \${par_complexity_threshold:+--complexity_threshold "\${par_complexity_threshold}"} \\ + \${par_filter_by_index1:+--filter_by_index1 "\${par_filter_by_index1}"} \\ + \${par_filter_by_index2:+--filter_by_index2 "\${par_filter_by_index2}"} \\ + \${par_filter_by_index_threshold:+--filter_by_index_threshold "\${par_filter_by_index_threshold}"} \\ + \${par_correction:+--correction} \\ + \${par_overlap_len_require:+--overlap_len_require "\${par_overlap_len_require}"} \\ + \${par_overlap_diff_limit:+--overlap_diff_limit "\${par_overlap_diff_limit}"} \\ + \${par_overlap_diff_percent_limit:+--overlap_diff_percent_limit "\${par_overlap_diff_percent_limit}"} \\ + \${par_umi:+--umi} \\ + \${par_umi_loc:+--umi_loc "\${par_umi_loc}"} \\ + \${par_umi_len:+--umi_len "\${par_umi_len}"} \\ + \${par_umi_prefix:+--umi_prefix "\${par_umi_prefix}"} \\ + \${par_umi_skip:+--umi_skip "\${par_umi_skip}"} \\ + \${par_umi_delim:+--umi_delim "\${par_umi_delim}"} \\ + \${par_overrepresentation_analysis:+--overrepresentation_analysis} \\ + \${par_overrepresentation_sampling:+--overrepresentation_sampling "\${par_overrepresentation_sampling}"} \\ + \${meta_cpus:+--thread "\${meta_cpus}"} +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_IN1" ]; then + VIASH_PAR_IN1=$(ViashDockerStripAutomount "$VIASH_PAR_IN1") + fi + if [ ! -z "$VIASH_PAR_IN2" ]; then + VIASH_PAR_IN2=$(ViashDockerStripAutomount "$VIASH_PAR_IN2") + fi + if [ ! -z "$VIASH_PAR_OUT1" ]; then + VIASH_PAR_OUT1=$(ViashDockerStripAutomount "$VIASH_PAR_OUT1") + fi + if [ ! -z "$VIASH_PAR_OUT2" ]; then + VIASH_PAR_OUT2=$(ViashDockerStripAutomount "$VIASH_PAR_OUT2") + fi + if [ ! -z "$VIASH_PAR_UNPAIRED1" ]; then + VIASH_PAR_UNPAIRED1=$(ViashDockerStripAutomount "$VIASH_PAR_UNPAIRED1") + fi + if [ ! -z "$VIASH_PAR_UNPAIRED2" ]; then + VIASH_PAR_UNPAIRED2=$(ViashDockerStripAutomount "$VIASH_PAR_UNPAIRED2") + fi + if [ ! -z "$VIASH_PAR_FAILED_OUT" ]; then + VIASH_PAR_FAILED_OUT=$(ViashDockerStripAutomount "$VIASH_PAR_FAILED_OUT") + fi + if [ ! -z "$VIASH_PAR_OVERLAPPED_OUT" ]; then + VIASH_PAR_OVERLAPPED_OUT=$(ViashDockerStripAutomount "$VIASH_PAR_OVERLAPPED_OUT") + fi + if [ ! -z "$VIASH_PAR_JSON" ]; then + VIASH_PAR_JSON=$(ViashDockerStripAutomount "$VIASH_PAR_JSON") + fi + if [ ! -z "$VIASH_PAR_HTML" ]; then + VIASH_PAR_HTML=$(ViashDockerStripAutomount "$VIASH_PAR_HTML") + fi + if [ ! -z "$VIASH_PAR_ADAPTER_FASTA" ]; then + VIASH_PAR_ADAPTER_FASTA=$(ViashDockerStripAutomount "$VIASH_PAR_ADAPTER_FASTA") + fi + if [ ! -z "$VIASH_PAR_MERGED_OUT" ]; then + VIASH_PAR_MERGED_OUT=$(ViashDockerStripAutomount "$VIASH_PAR_MERGED_OUT") + fi + if [ ! -z "$VIASH_PAR_FILTER_BY_INDEX1" ]; then + VIASH_PAR_FILTER_BY_INDEX1=$(ViashDockerStripAutomount "$VIASH_PAR_FILTER_BY_INDEX1") + fi + if [ ! -z "$VIASH_PAR_FILTER_BY_INDEX2" ]; then + VIASH_PAR_FILTER_BY_INDEX2=$(ViashDockerStripAutomount "$VIASH_PAR_FILTER_BY_INDEX2") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUT1" ] && [ ! -e "$VIASH_PAR_OUT1" ]; then + ViashError "Output file '$VIASH_PAR_OUT1' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_OUT2" ] && [ ! -e "$VIASH_PAR_OUT2" ]; then + ViashError "Output file '$VIASH_PAR_OUT2' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_UNPAIRED1" ] && [ ! -e "$VIASH_PAR_UNPAIRED1" ]; then + ViashError "Output file '$VIASH_PAR_UNPAIRED1' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_UNPAIRED2" ] && [ ! -e "$VIASH_PAR_UNPAIRED2" ]; then + ViashError "Output file '$VIASH_PAR_UNPAIRED2' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_FAILED_OUT" ] && [ ! -e "$VIASH_PAR_FAILED_OUT" ]; then + ViashError "Output file '$VIASH_PAR_FAILED_OUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_OVERLAPPED_OUT" ] && [ ! -e "$VIASH_PAR_OVERLAPPED_OUT" ]; then + ViashError "Output file '$VIASH_PAR_OVERLAPPED_OUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_JSON" ] && [ ! -e "$VIASH_PAR_JSON" ]; then + ViashError "Output file '$VIASH_PAR_JSON' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_HTML" ] && [ ! -e "$VIASH_PAR_HTML" ]; then + ViashError "Output file '$VIASH_PAR_HTML' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_MERGED_OUT" ] && [ ! -e "$VIASH_PAR_MERGED_OUT" ]; then + ViashError "Output file '$VIASH_PAR_MERGED_OUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/featurecounts/.config.vsh.yaml b/target/executable/featurecounts/.config.vsh.yaml new file mode 100644 index 00000000..7042eabe --- /dev/null +++ b/target/executable/featurecounts/.config.vsh.yaml @@ -0,0 +1,657 @@ +name: "featurecounts" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--annotation" + alternatives: + - "-a" + description: "Name of an annotation file. GTF/GFF format by default. See '--format'\ + \ option for more format information.\n" + info: null + example: + - "annotation.gtf" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--input" + alternatives: + - "-i" + description: "A list of SAM or BAM format files separated by semi-colon (;). They\ + \ can be either name or location sorted. Location-sorted paired-end reads are\ + \ automatically sorted by read names.\n" + info: null + example: + - "input_file1.bam" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--counts" + alternatives: + - "-o" + description: "Name of output file including read counts in tab delimited format.\n" + info: null + example: + - "features.tsv" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--summary" + description: "Summary statistics of counting results in tab delimited format.\n" + info: null + example: + - "summary.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--junctions" + description: "Count number of reads supporting each exon-exon junction. Junctions\ + \ were identified from those exon-spanning reads in the input (containing 'N'\ + \ in CIGAR string).\n" + info: null + example: + - "junctions.txt" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Annotation" + arguments: + - type: "string" + name: "--format" + alternatives: + - "-F" + description: "Specify format of the provided annotation file. Acceptable formats\ + \ include 'GTF' (or compatible GFF format) and 'SAF'. 'GTF' by default. \n" + info: null + example: + - "GTF" + required: false + choices: + - "GTF" + - "GFF" + - "SAF" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--feature_type" + alternatives: + - "-t" + description: "Specify feature type(s) in a GTF annotation. If multiple types are\ + \ provided, they should be separated by ';' with no space in between. 'exon'\ + \ by default. Rows in the annotation with a matched feature will be extracted\ + \ and used for read mapping.\n" + info: null + example: + - "exon" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--attribute_type" + alternatives: + - "-g" + description: "Specify attribute type in GTF annotation. 'gene_id' by default.\ + \ Meta-features used for read counting will be extracted from annotation using\ + \ the provided value.\n" + info: null + example: + - "gene_id" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--extra_attributes" + description: "Extract extra attribute types from the provided GTF annotation and\ + \ include them in the counting output. These attribute types will not be used\ + \ to group features. If more than one attribute type is provided they should\ + \ be separated by semicolon (;).\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--chrom_alias" + alternatives: + - "-A" + description: "Provide a chromosome name alias file to match chr names in annotation\ + \ with those in the reads. This should be a two-column comma-delimited text\ + \ file. Its first column should include chr names in the annotation and its\ + \ second column should include chr names in the reads. Chr names are case sensitive.\ + \ No column header should be included in the file.\n" + info: null + example: + - "chrom_alias.csv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Level of summarization" + arguments: + - type: "boolean_true" + name: "--feature_level" + alternatives: + - "-f" + description: "Perform read counting at feature level (eg. counting reads for exons\ + \ rather than genes).\n" + info: null + direction: "input" +- name: "Overlap between reads and features" + arguments: + - type: "boolean_true" + name: "--overlapping" + alternatives: + - "-O" + description: "Assign reads to all their overlapping meta-features (or features\ + \ if '--feature_level' is specified).\n" + info: null + direction: "input" + - type: "integer" + name: "--min_overlap" + description: "Minimum number of overlapping bases in a read that is required for\ + \ read assignment. 1 by default. Number of overlapping bases is counted from\ + \ both reads if paired end. If a negative value is provided, then a gap of up\ + \ to specified size will be allowed between read and the feature that the read\ + \ is assigned to.\n" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--frac_overlap" + description: "Minimum fraction of overlapping bases in a read that is required\ + \ for read assignment. Value should be within range [0,1]. 0 by default. Number\ + \ of overlapping bases is counted from both reads if paired end. Both this option\ + \ and '--min_overlap' option need to be satisfied for read assignment.\n" + info: null + example: + - 0.0 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--frac_overlap_feature" + description: "Minimum fraction of overlapping bases in a feature that is required\ + \ for read assignment. Value should be within range [0,1]. 0 by default.\n" + info: null + example: + - 0.0 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--largest_overlap" + description: "Assign reads to a meta-feature/feature that has the largest number\ + \ of overlapping bases.\n" + info: null + direction: "input" + - type: "integer" + name: "--non_overlap" + description: "Maximum number of non-overlapping bases in a read (or a read pair)\ + \ that is allowed when being assigned to a feature. No limit is set by default.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--non_overlap_feature" + description: "Maximum number of non-overlapping bases in a feature that is allowed\ + \ in read assignment. No limit is set by default.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--read_extension5" + description: "Reads are extended upstream by bases from their 5' end.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--read_extension3" + description: "Reads are extended upstream by bases from their 3' end.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--read2pos" + description: "Reduce reads to their 5' most base or 3' most base. Read counting\ + \ is then performed based on the single base the read is reduced to.\n" + info: null + required: false + choices: + - 3 + - 5 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Multi-mapping reads" + arguments: + - type: "boolean_true" + name: "--multi_mapping" + alternatives: + - "-M" + description: "Multi-mapping reads will also be counted. For a multi-mapping read,\ + \ all its reported alignments will be counted. The 'NH' tag in BAM/SAM input\ + \ is used to detect multi-mapping reads.\n" + info: null + direction: "input" +- name: "Fractional counting" + arguments: + - type: "boolean_true" + name: "--fraction" + description: "Assign fractional counts to features. This option must be used together\ + \ with '--multi_mapping' or '--overlapping' or both. When '--multi_mapping'\ + \ is specified, each reported alignment from a multi-mapping read (identified\ + \ via 'NH' tag) will carry a fractional count of 1/x, instead of 1 (one), where\ + \ x is the total number of alignments reported for the same read. When '--overlapping'\ + \ is specified, each overlapping feature will receive a fractional count of\ + \ 1/y, where y is the total number of features overlapping with the read. When\ + \ both '--multi_mapping' and '--overlapping' are specified, each alignment will\ + \ carry a fractional count of 1/(x*y).\n" + info: null + direction: "input" +- name: "Read filtering" + arguments: + - type: "integer" + name: "--min_map_quality" + alternatives: + - "-Q" + description: "The minimum mapping quality score a read must satisfy in order to\ + \ be counted. For paired-end reads, at least one end should satisfy this criteria.\ + \ 0 by default.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--split_only" + description: "Count split alignments only (ie. alignments with CIGAR string containing\ + \ 'N'). An example of split alignments is exon-spanning reads in RNA-seq data.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--non_split_only" + description: "If specified, only non-split alignments (CIGAR strings do not contain\ + \ letter 'N') will be counted. All the other alignments will be ignored.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--primary" + description: "Count primary alignments only. Primary alignments are identified\ + \ using bit 0x100 in SAM/BAM FLAG field.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--ignore_dup" + description: "Ignore duplicate reads in read counting. Duplicate reads are identified\ + \ using bit Ox400 in BAM/SAM FLAG field. The whole read pair is ignored if one\ + \ of the reads is a duplicate read for paired end data.\n" + info: null + direction: "input" +- name: "Strandedness" + arguments: + - type: "integer" + name: "--strand" + alternatives: + - "-s" + description: "Perform strand-specific read counting. A single integer value (applied\ + \ to all input files) should be provided. Possible values include: 0 (unstranded),\ + \ 1 (stranded) and 2 (reversely stranded). Default value is 0 (ie. unstranded\ + \ read counting carried out for all input files).\n" + info: null + example: + - 0 + required: false + choices: + - 0 + - 1 + - 2 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Exon-exon junctions" + arguments: + - type: "file" + name: "--ref_fasta" + alternatives: + - "-G" + description: "Provide the name of a FASTA-format file that contains the reference\ + \ sequences used in read mapping that produced the provided SAM/BAM files.\n" + info: null + example: + - "reference.fasta" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Parameters specific to paired end reads" + arguments: + - type: "boolean_true" + name: "--paired" + alternatives: + - "-p" + description: "Specify that input data contain paired-end reads. To perform fragment\ + \ counting (ie. counting read pairs), the '--countReadPairs' parameter should\ + \ also be specified in addition to this parameter.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--count_read_pairs" + description: "Count read pairs (fragments) instead of reads. This option is only\ + \ applicable for paired-end reads.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--both_aligned" + alternatives: + - "-B" + description: "Count read pairs (fragments) instead of reads. This option is only\ + \ applicable for paired-end reads.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--check_pe_dist" + alternatives: + - "-P" + description: "Check validity of paired-end distance when counting read pairs.\ + \ Use '--min_length' and '--max_length' to set thresholds.\n" + info: null + direction: "input" + - type: "integer" + name: "--min_length" + alternatives: + - "-d" + description: "Minimum fragment/template length, 50 by default.\n" + info: null + example: + - 50 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_length" + alternatives: + - "-D" + description: "Maximum fragment/template length, 600 by default.\n" + info: null + example: + - 600 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--same_strand" + alternatives: + - "-C" + description: "Do not count read pairs that have their two ends mapping to different\ + \ chromosomes or mapping to same chromosome but on different strands.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--donotsort" + description: "Do not sort reads in BAM/SAM input. Note that reads from the same\ + \ pair are required to be located next to each other in the input.\n" + info: null + direction: "input" +- name: "Read groups" + arguments: + - type: "boolean_true" + name: "--by_read_group" + description: "Assign reads by read group. \"RG\" tag is required to be present\ + \ in the input BAM/SAM files.\n" + info: null + direction: "input" +- name: "Long reads" + arguments: + - type: "boolean_true" + name: "--long_reads" + description: "Count long reads such as Nanopore and PacBio reads. Long read counting\ + \ can only run in one thread and only reads (not read-pairs) can be counted.\ + \ There is no limitation on the number of 'M' operations allowed in a CIGAR\ + \ string in long read counting.\n" + info: null + direction: "input" +- name: "Assignment results for each read" + arguments: + - type: "file" + name: "--detailed_results" + description: "Directory to save the detailed assignment results. Use `--detailed_results_format`\ + \ to determine the format of the detailed results.\n" + info: null + example: + - "detailed_results" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--detailed_results_format" + alternatives: + - "-R" + description: "Output detailed assignment results for each read or read-pair. Results\ + \ are saved to a file that is in one of the following formats: CORE, SAM and\ + \ BAM. See documentaiton for more info about these formats.\n" + info: null + required: false + choices: + - "CORE" + - "SAM" + - "BAM" + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Miscellaneous" + arguments: + - type: "integer" + name: "--max_M_op" + description: "Maximum number of 'M' operations allowed in a CIGAR string. 10 by\ + \ default. Both 'X' and '=' are treated as 'M' and adjacent 'M' operations are\ + \ merged in the CIGAR string.\n" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--verbose" + description: "Output verbose information for debugging, such as un-matched chromosome/contig\ + \ names.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "featureCounts is a read summarization program for counting reads generated\ + \ from either RNA or genomic DNA sequencing experiments by implementing highly efficient\ + \ chromosome hashing and feature blocking techniques. It works with either single\ + \ or paired-end reads and provides a wide range of options appropriate for different\ + \ sequencing applications.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "Read counting" +- "Genomic features" +license: "GPL-3.0" +references: + doi: + - "10.1093/bioinformatics/btt656" +links: + repository: "https://github.com/ShiLab-Bioinformatics/subread" + homepage: "https://subread.sourceforge.net/" + documentation: "https://subread.sourceforge.net/SubreadUsersGuide.pdf" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/subread:2.0.6--he4a0461_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "featureCounts -v 2>&1 | sed 's/featureCounts v\\([0-9.]*\\)/featureCounts:\ + \ \\1/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/featurecounts/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/featurecounts" + executable: "target/executable/featurecounts/featurecounts" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/featurecounts/featurecounts b/target/executable/featurecounts/featurecounts new file mode 100755 index 00000000..49f6e3d8 --- /dev/null +++ b/target/executable/featurecounts/featurecounts @@ -0,0 +1,2402 @@ +#!/usr/bin/env bash + +# featurecounts main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="featurecounts" +VIASH_META_FUNCTIONALITY_NAME="featurecounts" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "featurecounts main" + echo "" + echo "featureCounts is a read summarization program for counting reads generated from" + echo "either RNA or genomic DNA sequencing experiments by implementing highly" + echo "efficient chromosome hashing and feature blocking techniques. It works with" + echo "either single or paired-end reads and provides a wide range of options" + echo "appropriate for different sequencing applications." + echo "" + echo "Inputs:" + echo " -a, --annotation" + echo " type: file, required parameter, file must exist" + echo " example: annotation.gtf" + echo " Name of an annotation file. GTF/GFF format by default. See '--format'" + echo " option for more format information." + echo "" + echo " -i, --input" + echo " type: file, required parameter, multiple values allowed, file must exist" + echo " example: input_file1.bam" + echo " A list of SAM or BAM format files separated by semi-colon (;). They can" + echo " be either name or location sorted. Location-sorted paired-end reads are" + echo " automatically sorted by read names." + echo "" + echo "Outputs:" + echo " -o, --counts" + echo " type: file, required parameter, output, file must exist" + echo " example: features.tsv" + echo " Name of output file including read counts in tab delimited format." + echo "" + echo " --summary" + echo " type: file, output, file must exist" + echo " example: summary.tsv" + echo " Summary statistics of counting results in tab delimited format." + echo "" + echo " --junctions" + echo " type: file, output, file must exist" + echo " example: junctions.txt" + echo " Count number of reads supporting each exon-exon junction. Junctions were" + echo " identified from those exon-spanning reads in the input (containing 'N'" + echo " in CIGAR string)." + echo "" + echo "Annotation:" + echo " -F, --format" + echo " type: string" + echo " example: GTF" + echo " choices: [ GTF, GFF, SAF ]" + echo " Specify format of the provided annotation file. Acceptable formats" + echo " include 'GTF' (or compatible GFF format) and 'SAF'. 'GTF' by default." + echo "" + echo " -t, --feature_type" + echo " type: string, multiple values allowed" + echo " example: exon" + echo " Specify feature type(s) in a GTF annotation. If multiple types are" + echo " provided, they should be separated by ';' with no space in between." + echo " 'exon' by default. Rows in the annotation with a matched feature will be" + echo " extracted and used for read mapping." + echo "" + echo " -g, --attribute_type" + echo " type: string" + echo " example: gene_id" + echo " Specify attribute type in GTF annotation. 'gene_id' by default." + echo " Meta-features used for read counting will be extracted from annotation" + echo " using the provided value." + echo "" + echo " --extra_attributes" + echo " type: string, multiple values allowed" + echo " Extract extra attribute types from the provided GTF annotation and" + echo " include them in the counting output. These attribute types will not be" + echo " used to group features. If more than one attribute type is provided they" + echo " should be separated by semicolon (;)." + echo "" + echo " -A, --chrom_alias" + echo " type: file, file must exist" + echo " example: chrom_alias.csv" + echo " Provide a chromosome name alias file to match chr names in annotation" + echo " with those in the reads. This should be a two-column comma-delimited" + echo " text file. Its first column should include chr names in the annotation" + echo " and its second column should include chr names in the reads. Chr names" + echo " are case sensitive. No column header should be included in the file." + echo "" + echo "Level of summarization:" + echo " -f, --feature_level" + echo " type: boolean_true" + echo " Perform read counting at feature level (eg. counting reads for exons" + echo " rather than genes)." + echo "" + echo "Overlap between reads and features:" + echo " -O, --overlapping" + echo " type: boolean_true" + echo " Assign reads to all their overlapping meta-features (or features if" + echo " '--feature_level' is specified)." + echo "" + echo " --min_overlap" + echo " type: integer" + echo " example: 1" + echo " Minimum number of overlapping bases in a read that is required for read" + echo " assignment. 1 by default. Number of overlapping bases is counted from" + echo " both reads if paired end. If a negative value is provided, then a gap of" + echo " up to specified size will be allowed between read and the feature that" + echo " the read is assigned to." + echo "" + echo " --frac_overlap" + echo " type: double" + echo " example: 0.0" + echo " min: 0.0" + echo " max: 1.0" + echo " Minimum fraction of overlapping bases in a read that is required for" + echo " read assignment. Value should be within range [0,1]. 0 by default." + echo " Number of overlapping bases is counted from both reads if paired end." + echo " Both this option and '--min_overlap' option need to be satisfied for" + echo " read assignment." + echo "" + echo " --frac_overlap_feature" + echo " type: double" + echo " example: 0.0" + echo " min: 0.0" + echo " max: 1.0" + echo " Minimum fraction of overlapping bases in a feature that is required for" + echo " read assignment. Value should be within range [0,1]. 0 by default." + echo "" + echo " --largest_overlap" + echo " type: boolean_true" + echo " Assign reads to a meta-feature/feature that has the largest number of" + echo " overlapping bases." + echo "" + echo " --non_overlap" + echo " type: integer" + echo " Maximum number of non-overlapping bases in a read (or a read pair) that" + echo " is allowed when being assigned to a feature. No limit is set by default." + echo "" + echo " --non_overlap_feature" + echo " type: integer" + echo " Maximum number of non-overlapping bases in a feature that is allowed in" + echo " read assignment. No limit is set by default." + echo "" + echo " --read_extension5" + echo " type: integer" + echo " Reads are extended upstream by bases from their 5' end." + echo "" + echo " --read_extension3" + echo " type: integer" + echo " Reads are extended upstream by bases from their 3' end." + echo "" + echo " --read2pos" + echo " type: integer" + echo " choices: [ 3, 5 ]" + echo " Reduce reads to their 5' most base or 3' most base. Read counting is" + echo " then performed based on the single base the read is reduced to." + echo "" + echo "Multi-mapping reads:" + echo " -M, --multi_mapping" + echo " type: boolean_true" + echo " Multi-mapping reads will also be counted. For a multi-mapping read, all" + echo " its reported alignments will be counted. The 'NH' tag in BAM/SAM input" + echo " is used to detect multi-mapping reads." + echo "" + echo "Fractional counting:" + echo " --fraction" + echo " type: boolean_true" + echo " Assign fractional counts to features. This option must be used together" + echo " with '--multi_mapping' or '--overlapping' or both. When" + echo " '--multi_mapping' is specified, each reported alignment from a" + echo " multi-mapping read (identified via 'NH' tag) will carry a fractional" + echo " count of 1/x, instead of 1 (one), where x is the total number of" + echo " alignments reported for the same read. When '--overlapping' is" + echo " specified, each overlapping feature will receive a fractional count of" + echo " 1/y, where y is the total number of features overlapping with the read." + echo " When both '--multi_mapping' and '--overlapping' are specified, each" + echo " alignment will carry a fractional count of 1/(x*y)." + echo "" + echo "Read filtering:" + echo " -Q, --min_map_quality" + echo " type: integer" + echo " example: 0" + echo " The minimum mapping quality score a read must satisfy in order to be" + echo " counted. For paired-end reads, at least one end should satisfy this" + echo " criteria. 0 by default." + echo "" + echo " --split_only" + echo " type: boolean_true" + echo " Count split alignments only (ie. alignments with CIGAR string containing" + echo " 'N'). An example of split alignments is exon-spanning reads in RNA-seq" + echo " data." + echo "" + echo " --non_split_only" + echo " type: boolean_true" + echo " If specified, only non-split alignments (CIGAR strings do not contain" + echo " letter 'N') will be counted. All the other alignments will be ignored." + echo "" + echo " --primary" + echo " type: boolean_true" + echo " Count primary alignments only. Primary alignments are identified using" + echo " bit 0x100 in SAM/BAM FLAG field." + echo "" + echo " --ignore_dup" + echo " type: boolean_true" + echo " Ignore duplicate reads in read counting. Duplicate reads are identified" + echo " using bit Ox400 in BAM/SAM FLAG field. The whole read pair is ignored if" + echo " one of the reads is a duplicate read for paired end data." + echo "" + echo "Strandedness:" + echo " -s, --strand" + echo " type: integer" + echo " example: 0" + echo " choices: [ 0, 1, 2 ]" + echo " Perform strand-specific read counting. A single integer value (applied" + echo " to all input files) should be provided. Possible values include: 0" + echo " (unstranded), 1 (stranded) and 2 (reversely stranded). Default value is" + echo " 0 (ie. unstranded read counting carried out for all input files)." + echo "" + echo "Exon-exon junctions:" + echo " -G, --ref_fasta" + echo " type: file, file must exist" + echo " example: reference.fasta" + echo " Provide the name of a FASTA-format file that contains the reference" + echo " sequences used in read mapping that produced the provided SAM/BAM files." + echo "" + echo "Parameters specific to paired end reads:" + echo " -p, --paired" + echo " type: boolean_true" + echo " Specify that input data contain paired-end reads. To perform fragment" + echo " counting (ie. counting read pairs), the '--countReadPairs' parameter" + echo " should also be specified in addition to this parameter." + echo "" + echo " --count_read_pairs" + echo " type: boolean_true" + echo " Count read pairs (fragments) instead of reads. This option is only" + echo " applicable for paired-end reads." + echo "" + echo " -B, --both_aligned" + echo " type: boolean_true" + echo " Count read pairs (fragments) instead of reads. This option is only" + echo " applicable for paired-end reads." + echo "" + echo " -P, --check_pe_dist" + echo " type: boolean_true" + echo " Check validity of paired-end distance when counting read pairs. Use" + echo " '--min_length' and '--max_length' to set thresholds." + echo "" + echo " -d, --min_length" + echo " type: integer" + echo " example: 50" + echo " Minimum fragment/template length, 50 by default." + echo "" + echo " -D, --max_length" + echo " type: integer" + echo " example: 600" + echo " Maximum fragment/template length, 600 by default." + echo "" + echo " -C, --same_strand" + echo " type: boolean_true" + echo " Do not count read pairs that have their two ends mapping to different" + echo " chromosomes or mapping to same chromosome but on different strands." + echo "" + echo " --donotsort" + echo " type: boolean_true" + echo " Do not sort reads in BAM/SAM input. Note that reads from the same pair" + echo " are required to be located next to each other in the input." + echo "" + echo "Read groups:" + echo " --by_read_group" + echo " type: boolean_true" + echo " Assign reads by read group. \"RG\" tag is required to be present in the" + echo " input BAM/SAM files." + echo "" + echo "Long reads:" + echo " --long_reads" + echo " type: boolean_true" + echo " Count long reads such as Nanopore and PacBio reads. Long read counting" + echo " can only run in one thread and only reads (not read-pairs) can be" + echo " counted. There is no limitation on the number of 'M' operations allowed" + echo " in a CIGAR string in long read counting." + echo "" + echo "Assignment results for each read:" + echo " --detailed_results" + echo " type: file, output, file must exist" + echo " example: detailed_results" + echo " Directory to save the detailed assignment results. Use" + echo " \`--detailed_results_format\` to determine the format of the detailed" + echo " results." + echo "" + echo " -R, --detailed_results_format" + echo " type: string" + echo " choices: [ CORE, SAM, BAM ]" + echo " Output detailed assignment results for each read or read-pair. Results" + echo " are saved to a file that is in one of the following formats: CORE, SAM" + echo " and BAM. See documentaiton for more info about these formats." + echo "" + echo "Miscellaneous:" + echo " --max_M_op" + echo " type: integer" + echo " example: 10" + echo " Maximum number of 'M' operations allowed in a CIGAR string. 10 by" + echo " default. Both 'X' and '=' are treated as 'M' and adjacent 'M' operations" + echo " are merged in the CIGAR string." + echo "" + echo " --verbose" + echo " type: boolean_true" + echo " Output verbose information for debugging, such as un-matched" + echo " chromosome/contig names." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/subread:2.0.6--he4a0461_0 +ENTRYPOINT [] +RUN featureCounts -v 2>&1 | sed 's/featureCounts v\([0-9.]*\)/featureCounts: \1/' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component featurecounts" +LABEL org.opencontainers.image.created="2024-06-24T08:36:36Z" +LABEL org.opencontainers.image.source="https://github.com/ShiLab-Bioinformatics/subread" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "featurecounts main" + exit + ;; + --annotation) + [ -n "$VIASH_PAR_ANNOTATION" ] && ViashError Bad arguments for option \'--annotation\': \'$VIASH_PAR_ANNOTATION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ANNOTATION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --annotation. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --annotation=*) + [ -n "$VIASH_PAR_ANNOTATION" ] && ViashError Bad arguments for option \'--annotation=*\': \'$VIASH_PAR_ANNOTATION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ANNOTATION=$(ViashRemoveFlags "$1") + shift 1 + ;; + -a) + [ -n "$VIASH_PAR_ANNOTATION" ] && ViashError Bad arguments for option \'-a\': \'$VIASH_PAR_ANNOTATION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ANNOTATION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -a. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input) + if [ -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT="$2" + else + VIASH_PAR_INPUT="$VIASH_PAR_INPUT;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + if [ -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + else + VIASH_PAR_INPUT="$VIASH_PAR_INPUT;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -i) + if [ -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT="$2" + else + VIASH_PAR_INPUT="$VIASH_PAR_INPUT;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -i. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --counts) + [ -n "$VIASH_PAR_COUNTS" ] && ViashError Bad arguments for option \'--counts\': \'$VIASH_PAR_COUNTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COUNTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --counts. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --counts=*) + [ -n "$VIASH_PAR_COUNTS" ] && ViashError Bad arguments for option \'--counts=*\': \'$VIASH_PAR_COUNTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COUNTS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_COUNTS" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_COUNTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COUNTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --summary) + [ -n "$VIASH_PAR_SUMMARY" ] && ViashError Bad arguments for option \'--summary\': \'$VIASH_PAR_SUMMARY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUMMARY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --summary. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --summary=*) + [ -n "$VIASH_PAR_SUMMARY" ] && ViashError Bad arguments for option \'--summary=*\': \'$VIASH_PAR_SUMMARY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUMMARY=$(ViashRemoveFlags "$1") + shift 1 + ;; + --junctions) + [ -n "$VIASH_PAR_JUNCTIONS" ] && ViashError Bad arguments for option \'--junctions\': \'$VIASH_PAR_JUNCTIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_JUNCTIONS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --junctions. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --junctions=*) + [ -n "$VIASH_PAR_JUNCTIONS" ] && ViashError Bad arguments for option \'--junctions=*\': \'$VIASH_PAR_JUNCTIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_JUNCTIONS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --format) + [ -n "$VIASH_PAR_FORMAT" ] && ViashError Bad arguments for option \'--format\': \'$VIASH_PAR_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORMAT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --format. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --format=*) + [ -n "$VIASH_PAR_FORMAT" ] && ViashError Bad arguments for option \'--format=*\': \'$VIASH_PAR_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORMAT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -F) + [ -n "$VIASH_PAR_FORMAT" ] && ViashError Bad arguments for option \'-F\': \'$VIASH_PAR_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORMAT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -F. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --feature_type) + if [ -z "$VIASH_PAR_FEATURE_TYPE" ]; then + VIASH_PAR_FEATURE_TYPE="$2" + else + VIASH_PAR_FEATURE_TYPE="$VIASH_PAR_FEATURE_TYPE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --feature_type. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --feature_type=*) + if [ -z "$VIASH_PAR_FEATURE_TYPE" ]; then + VIASH_PAR_FEATURE_TYPE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_FEATURE_TYPE="$VIASH_PAR_FEATURE_TYPE;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -t) + if [ -z "$VIASH_PAR_FEATURE_TYPE" ]; then + VIASH_PAR_FEATURE_TYPE="$2" + else + VIASH_PAR_FEATURE_TYPE="$VIASH_PAR_FEATURE_TYPE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -t. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --attribute_type) + [ -n "$VIASH_PAR_ATTRIBUTE_TYPE" ] && ViashError Bad arguments for option \'--attribute_type\': \'$VIASH_PAR_ATTRIBUTE_TYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ATTRIBUTE_TYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --attribute_type. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --attribute_type=*) + [ -n "$VIASH_PAR_ATTRIBUTE_TYPE" ] && ViashError Bad arguments for option \'--attribute_type=*\': \'$VIASH_PAR_ATTRIBUTE_TYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ATTRIBUTE_TYPE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -g) + [ -n "$VIASH_PAR_ATTRIBUTE_TYPE" ] && ViashError Bad arguments for option \'-g\': \'$VIASH_PAR_ATTRIBUTE_TYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ATTRIBUTE_TYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -g. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --extra_attributes) + if [ -z "$VIASH_PAR_EXTRA_ATTRIBUTES" ]; then + VIASH_PAR_EXTRA_ATTRIBUTES="$2" + else + VIASH_PAR_EXTRA_ATTRIBUTES="$VIASH_PAR_EXTRA_ATTRIBUTES;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --extra_attributes. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --extra_attributes=*) + if [ -z "$VIASH_PAR_EXTRA_ATTRIBUTES" ]; then + VIASH_PAR_EXTRA_ATTRIBUTES=$(ViashRemoveFlags "$1") + else + VIASH_PAR_EXTRA_ATTRIBUTES="$VIASH_PAR_EXTRA_ATTRIBUTES;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --chrom_alias) + [ -n "$VIASH_PAR_CHROM_ALIAS" ] && ViashError Bad arguments for option \'--chrom_alias\': \'$VIASH_PAR_CHROM_ALIAS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHROM_ALIAS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chrom_alias. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chrom_alias=*) + [ -n "$VIASH_PAR_CHROM_ALIAS" ] && ViashError Bad arguments for option \'--chrom_alias=*\': \'$VIASH_PAR_CHROM_ALIAS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHROM_ALIAS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -A) + [ -n "$VIASH_PAR_CHROM_ALIAS" ] && ViashError Bad arguments for option \'-A\': \'$VIASH_PAR_CHROM_ALIAS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHROM_ALIAS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -A. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --feature_level) + [ -n "$VIASH_PAR_FEATURE_LEVEL" ] && ViashError Bad arguments for option \'--feature_level\': \'$VIASH_PAR_FEATURE_LEVEL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FEATURE_LEVEL=true + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_FEATURE_LEVEL" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_FEATURE_LEVEL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FEATURE_LEVEL=true + shift 1 + ;; + --overlapping) + [ -n "$VIASH_PAR_OVERLAPPING" ] && ViashError Bad arguments for option \'--overlapping\': \'$VIASH_PAR_OVERLAPPING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAPPING=true + shift 1 + ;; + -O) + [ -n "$VIASH_PAR_OVERLAPPING" ] && ViashError Bad arguments for option \'-O\': \'$VIASH_PAR_OVERLAPPING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OVERLAPPING=true + shift 1 + ;; + --min_overlap) + [ -n "$VIASH_PAR_MIN_OVERLAP" ] && ViashError Bad arguments for option \'--min_overlap\': \'$VIASH_PAR_MIN_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_OVERLAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_overlap. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_overlap=*) + [ -n "$VIASH_PAR_MIN_OVERLAP" ] && ViashError Bad arguments for option \'--min_overlap=*\': \'$VIASH_PAR_MIN_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_OVERLAP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --frac_overlap) + [ -n "$VIASH_PAR_FRAC_OVERLAP" ] && ViashError Bad arguments for option \'--frac_overlap\': \'$VIASH_PAR_FRAC_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRAC_OVERLAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --frac_overlap. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --frac_overlap=*) + [ -n "$VIASH_PAR_FRAC_OVERLAP" ] && ViashError Bad arguments for option \'--frac_overlap=*\': \'$VIASH_PAR_FRAC_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRAC_OVERLAP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --frac_overlap_feature) + [ -n "$VIASH_PAR_FRAC_OVERLAP_FEATURE" ] && ViashError Bad arguments for option \'--frac_overlap_feature\': \'$VIASH_PAR_FRAC_OVERLAP_FEATURE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRAC_OVERLAP_FEATURE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --frac_overlap_feature. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --frac_overlap_feature=*) + [ -n "$VIASH_PAR_FRAC_OVERLAP_FEATURE" ] && ViashError Bad arguments for option \'--frac_overlap_feature=*\': \'$VIASH_PAR_FRAC_OVERLAP_FEATURE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRAC_OVERLAP_FEATURE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --largest_overlap) + [ -n "$VIASH_PAR_LARGEST_OVERLAP" ] && ViashError Bad arguments for option \'--largest_overlap\': \'$VIASH_PAR_LARGEST_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LARGEST_OVERLAP=true + shift 1 + ;; + --non_overlap) + [ -n "$VIASH_PAR_NON_OVERLAP" ] && ViashError Bad arguments for option \'--non_overlap\': \'$VIASH_PAR_NON_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NON_OVERLAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --non_overlap. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --non_overlap=*) + [ -n "$VIASH_PAR_NON_OVERLAP" ] && ViashError Bad arguments for option \'--non_overlap=*\': \'$VIASH_PAR_NON_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NON_OVERLAP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --non_overlap_feature) + [ -n "$VIASH_PAR_NON_OVERLAP_FEATURE" ] && ViashError Bad arguments for option \'--non_overlap_feature\': \'$VIASH_PAR_NON_OVERLAP_FEATURE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NON_OVERLAP_FEATURE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --non_overlap_feature. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --non_overlap_feature=*) + [ -n "$VIASH_PAR_NON_OVERLAP_FEATURE" ] && ViashError Bad arguments for option \'--non_overlap_feature=*\': \'$VIASH_PAR_NON_OVERLAP_FEATURE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NON_OVERLAP_FEATURE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --read_extension5) + [ -n "$VIASH_PAR_READ_EXTENSION5" ] && ViashError Bad arguments for option \'--read_extension5\': \'$VIASH_PAR_READ_EXTENSION5\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_EXTENSION5="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --read_extension5. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read_extension5=*) + [ -n "$VIASH_PAR_READ_EXTENSION5" ] && ViashError Bad arguments for option \'--read_extension5=*\': \'$VIASH_PAR_READ_EXTENSION5\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_EXTENSION5=$(ViashRemoveFlags "$1") + shift 1 + ;; + --read_extension3) + [ -n "$VIASH_PAR_READ_EXTENSION3" ] && ViashError Bad arguments for option \'--read_extension3\': \'$VIASH_PAR_READ_EXTENSION3\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_EXTENSION3="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --read_extension3. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read_extension3=*) + [ -n "$VIASH_PAR_READ_EXTENSION3" ] && ViashError Bad arguments for option \'--read_extension3=*\': \'$VIASH_PAR_READ_EXTENSION3\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_EXTENSION3=$(ViashRemoveFlags "$1") + shift 1 + ;; + --read2pos) + [ -n "$VIASH_PAR_READ2POS" ] && ViashError Bad arguments for option \'--read2pos\': \'$VIASH_PAR_READ2POS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ2POS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --read2pos. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read2pos=*) + [ -n "$VIASH_PAR_READ2POS" ] && ViashError Bad arguments for option \'--read2pos=*\': \'$VIASH_PAR_READ2POS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ2POS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --multi_mapping) + [ -n "$VIASH_PAR_MULTI_MAPPING" ] && ViashError Bad arguments for option \'--multi_mapping\': \'$VIASH_PAR_MULTI_MAPPING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MULTI_MAPPING=true + shift 1 + ;; + -M) + [ -n "$VIASH_PAR_MULTI_MAPPING" ] && ViashError Bad arguments for option \'-M\': \'$VIASH_PAR_MULTI_MAPPING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MULTI_MAPPING=true + shift 1 + ;; + --fraction) + [ -n "$VIASH_PAR_FRACTION" ] && ViashError Bad arguments for option \'--fraction\': \'$VIASH_PAR_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FRACTION=true + shift 1 + ;; + --min_map_quality) + [ -n "$VIASH_PAR_MIN_MAP_QUALITY" ] && ViashError Bad arguments for option \'--min_map_quality\': \'$VIASH_PAR_MIN_MAP_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_MAP_QUALITY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_map_quality. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_map_quality=*) + [ -n "$VIASH_PAR_MIN_MAP_QUALITY" ] && ViashError Bad arguments for option \'--min_map_quality=*\': \'$VIASH_PAR_MIN_MAP_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_MAP_QUALITY=$(ViashRemoveFlags "$1") + shift 1 + ;; + -Q) + [ -n "$VIASH_PAR_MIN_MAP_QUALITY" ] && ViashError Bad arguments for option \'-Q\': \'$VIASH_PAR_MIN_MAP_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_MAP_QUALITY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -Q. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --split_only) + [ -n "$VIASH_PAR_SPLIT_ONLY" ] && ViashError Bad arguments for option \'--split_only\': \'$VIASH_PAR_SPLIT_ONLY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLIT_ONLY=true + shift 1 + ;; + --non_split_only) + [ -n "$VIASH_PAR_NON_SPLIT_ONLY" ] && ViashError Bad arguments for option \'--non_split_only\': \'$VIASH_PAR_NON_SPLIT_ONLY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NON_SPLIT_ONLY=true + shift 1 + ;; + --primary) + [ -n "$VIASH_PAR_PRIMARY" ] && ViashError Bad arguments for option \'--primary\': \'$VIASH_PAR_PRIMARY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PRIMARY=true + shift 1 + ;; + --ignore_dup) + [ -n "$VIASH_PAR_IGNORE_DUP" ] && ViashError Bad arguments for option \'--ignore_dup\': \'$VIASH_PAR_IGNORE_DUP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IGNORE_DUP=true + shift 1 + ;; + --strand) + [ -n "$VIASH_PAR_STRAND" ] && ViashError Bad arguments for option \'--strand\': \'$VIASH_PAR_STRAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRAND="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --strand. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --strand=*) + [ -n "$VIASH_PAR_STRAND" ] && ViashError Bad arguments for option \'--strand=*\': \'$VIASH_PAR_STRAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRAND=$(ViashRemoveFlags "$1") + shift 1 + ;; + -s) + [ -n "$VIASH_PAR_STRAND" ] && ViashError Bad arguments for option \'-s\': \'$VIASH_PAR_STRAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRAND="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -s. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ref_fasta) + [ -n "$VIASH_PAR_REF_FASTA" ] && ViashError Bad arguments for option \'--ref_fasta\': \'$VIASH_PAR_REF_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF_FASTA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --ref_fasta. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ref_fasta=*) + [ -n "$VIASH_PAR_REF_FASTA" ] && ViashError Bad arguments for option \'--ref_fasta=*\': \'$VIASH_PAR_REF_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF_FASTA=$(ViashRemoveFlags "$1") + shift 1 + ;; + -G) + [ -n "$VIASH_PAR_REF_FASTA" ] && ViashError Bad arguments for option \'-G\': \'$VIASH_PAR_REF_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF_FASTA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -G. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --paired) + [ -n "$VIASH_PAR_PAIRED" ] && ViashError Bad arguments for option \'--paired\': \'$VIASH_PAR_PAIRED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PAIRED=true + shift 1 + ;; + -p) + [ -n "$VIASH_PAR_PAIRED" ] && ViashError Bad arguments for option \'-p\': \'$VIASH_PAR_PAIRED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PAIRED=true + shift 1 + ;; + --count_read_pairs) + [ -n "$VIASH_PAR_COUNT_READ_PAIRS" ] && ViashError Bad arguments for option \'--count_read_pairs\': \'$VIASH_PAR_COUNT_READ_PAIRS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COUNT_READ_PAIRS=true + shift 1 + ;; + --both_aligned) + [ -n "$VIASH_PAR_BOTH_ALIGNED" ] && ViashError Bad arguments for option \'--both_aligned\': \'$VIASH_PAR_BOTH_ALIGNED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BOTH_ALIGNED=true + shift 1 + ;; + -B) + [ -n "$VIASH_PAR_BOTH_ALIGNED" ] && ViashError Bad arguments for option \'-B\': \'$VIASH_PAR_BOTH_ALIGNED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BOTH_ALIGNED=true + shift 1 + ;; + --check_pe_dist) + [ -n "$VIASH_PAR_CHECK_PE_DIST" ] && ViashError Bad arguments for option \'--check_pe_dist\': \'$VIASH_PAR_CHECK_PE_DIST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHECK_PE_DIST=true + shift 1 + ;; + -P) + [ -n "$VIASH_PAR_CHECK_PE_DIST" ] && ViashError Bad arguments for option \'-P\': \'$VIASH_PAR_CHECK_PE_DIST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHECK_PE_DIST=true + shift 1 + ;; + --min_length) + [ -n "$VIASH_PAR_MIN_LENGTH" ] && ViashError Bad arguments for option \'--min_length\': \'$VIASH_PAR_MIN_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_length=*) + [ -n "$VIASH_PAR_MIN_LENGTH" ] && ViashError Bad arguments for option \'--min_length=*\': \'$VIASH_PAR_MIN_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -d) + [ -n "$VIASH_PAR_MIN_LENGTH" ] && ViashError Bad arguments for option \'-d\': \'$VIASH_PAR_MIN_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -d. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_length) + [ -n "$VIASH_PAR_MAX_LENGTH" ] && ViashError Bad arguments for option \'--max_length\': \'$VIASH_PAR_MAX_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_length=*) + [ -n "$VIASH_PAR_MAX_LENGTH" ] && ViashError Bad arguments for option \'--max_length=*\': \'$VIASH_PAR_MAX_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -D) + [ -n "$VIASH_PAR_MAX_LENGTH" ] && ViashError Bad arguments for option \'-D\': \'$VIASH_PAR_MAX_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -D. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --same_strand) + [ -n "$VIASH_PAR_SAME_STRAND" ] && ViashError Bad arguments for option \'--same_strand\': \'$VIASH_PAR_SAME_STRAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAME_STRAND=true + shift 1 + ;; + -C) + [ -n "$VIASH_PAR_SAME_STRAND" ] && ViashError Bad arguments for option \'-C\': \'$VIASH_PAR_SAME_STRAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAME_STRAND=true + shift 1 + ;; + --donotsort) + [ -n "$VIASH_PAR_DONOTSORT" ] && ViashError Bad arguments for option \'--donotsort\': \'$VIASH_PAR_DONOTSORT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DONOTSORT=true + shift 1 + ;; + --by_read_group) + [ -n "$VIASH_PAR_BY_READ_GROUP" ] && ViashError Bad arguments for option \'--by_read_group\': \'$VIASH_PAR_BY_READ_GROUP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BY_READ_GROUP=true + shift 1 + ;; + --long_reads) + [ -n "$VIASH_PAR_LONG_READS" ] && ViashError Bad arguments for option \'--long_reads\': \'$VIASH_PAR_LONG_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LONG_READS=true + shift 1 + ;; + --detailed_results) + [ -n "$VIASH_PAR_DETAILED_RESULTS" ] && ViashError Bad arguments for option \'--detailed_results\': \'$VIASH_PAR_DETAILED_RESULTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DETAILED_RESULTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --detailed_results. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --detailed_results=*) + [ -n "$VIASH_PAR_DETAILED_RESULTS" ] && ViashError Bad arguments for option \'--detailed_results=*\': \'$VIASH_PAR_DETAILED_RESULTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DETAILED_RESULTS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --detailed_results_format) + [ -n "$VIASH_PAR_DETAILED_RESULTS_FORMAT" ] && ViashError Bad arguments for option \'--detailed_results_format\': \'$VIASH_PAR_DETAILED_RESULTS_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DETAILED_RESULTS_FORMAT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --detailed_results_format. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --detailed_results_format=*) + [ -n "$VIASH_PAR_DETAILED_RESULTS_FORMAT" ] && ViashError Bad arguments for option \'--detailed_results_format=*\': \'$VIASH_PAR_DETAILED_RESULTS_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DETAILED_RESULTS_FORMAT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -R) + [ -n "$VIASH_PAR_DETAILED_RESULTS_FORMAT" ] && ViashError Bad arguments for option \'-R\': \'$VIASH_PAR_DETAILED_RESULTS_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DETAILED_RESULTS_FORMAT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -R. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_M_op) + [ -n "$VIASH_PAR_MAX_M_OP" ] && ViashError Bad arguments for option \'--max_M_op\': \'$VIASH_PAR_MAX_M_OP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_M_OP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_M_op. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_M_op=*) + [ -n "$VIASH_PAR_MAX_M_OP" ] && ViashError Bad arguments for option \'--max_M_op=*\': \'$VIASH_PAR_MAX_M_OP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_M_OP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --verbose) + [ -n "$VIASH_PAR_VERBOSE" ] && ViashError Bad arguments for option \'--verbose\': \'$VIASH_PAR_VERBOSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_VERBOSE=true + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/featurecounts:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_ANNOTATION+x} ]; then + ViashError '--annotation' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_COUNTS+x} ]; then + ViashError '--counts' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_FEATURE_LEVEL+x} ]; then + VIASH_PAR_FEATURE_LEVEL="false" +fi +if [ -z ${VIASH_PAR_OVERLAPPING+x} ]; then + VIASH_PAR_OVERLAPPING="false" +fi +if [ -z ${VIASH_PAR_LARGEST_OVERLAP+x} ]; then + VIASH_PAR_LARGEST_OVERLAP="false" +fi +if [ -z ${VIASH_PAR_MULTI_MAPPING+x} ]; then + VIASH_PAR_MULTI_MAPPING="false" +fi +if [ -z ${VIASH_PAR_FRACTION+x} ]; then + VIASH_PAR_FRACTION="false" +fi +if [ -z ${VIASH_PAR_SPLIT_ONLY+x} ]; then + VIASH_PAR_SPLIT_ONLY="false" +fi +if [ -z ${VIASH_PAR_NON_SPLIT_ONLY+x} ]; then + VIASH_PAR_NON_SPLIT_ONLY="false" +fi +if [ -z ${VIASH_PAR_PRIMARY+x} ]; then + VIASH_PAR_PRIMARY="false" +fi +if [ -z ${VIASH_PAR_IGNORE_DUP+x} ]; then + VIASH_PAR_IGNORE_DUP="false" +fi +if [ -z ${VIASH_PAR_PAIRED+x} ]; then + VIASH_PAR_PAIRED="false" +fi +if [ -z ${VIASH_PAR_COUNT_READ_PAIRS+x} ]; then + VIASH_PAR_COUNT_READ_PAIRS="false" +fi +if [ -z ${VIASH_PAR_BOTH_ALIGNED+x} ]; then + VIASH_PAR_BOTH_ALIGNED="false" +fi +if [ -z ${VIASH_PAR_CHECK_PE_DIST+x} ]; then + VIASH_PAR_CHECK_PE_DIST="false" +fi +if [ -z ${VIASH_PAR_SAME_STRAND+x} ]; then + VIASH_PAR_SAME_STRAND="false" +fi +if [ -z ${VIASH_PAR_DONOTSORT+x} ]; then + VIASH_PAR_DONOTSORT="false" +fi +if [ -z ${VIASH_PAR_BY_READ_GROUP+x} ]; then + VIASH_PAR_BY_READ_GROUP="false" +fi +if [ -z ${VIASH_PAR_LONG_READS+x} ]; then + VIASH_PAR_LONG_READS="false" +fi +if [ -z ${VIASH_PAR_VERBOSE+x} ]; then + VIASH_PAR_VERBOSE="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_ANNOTATION" ] && [ ! -e "$VIASH_PAR_ANNOTATION" ]; then + ViashError "Input file '$VIASH_PAR_ANNOTATION' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_INPUT" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_INPUT; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi +if [ ! -z "$VIASH_PAR_CHROM_ALIAS" ] && [ ! -e "$VIASH_PAR_CHROM_ALIAS" ]; then + ViashError "Input file '$VIASH_PAR_CHROM_ALIAS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REF_FASTA" ] && [ ! -e "$VIASH_PAR_REF_FASTA" ]; then + ViashError "Input file '$VIASH_PAR_REF_FASTA' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_FEATURE_LEVEL" ]]; then + if ! [[ "$VIASH_PAR_FEATURE_LEVEL" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--feature_level' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OVERLAPPING" ]]; then + if ! [[ "$VIASH_PAR_OVERLAPPING" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--overlapping' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_OVERLAP" ]]; then + if ! [[ "$VIASH_PAR_MIN_OVERLAP" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_overlap' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FRAC_OVERLAP" ]]; then + if ! [[ "$VIASH_PAR_FRAC_OVERLAP" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--frac_overlap' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_FRAC_OVERLAP '>=' 0.0 | bc` -eq 1 ]]; then + ViashError '--frac_overlap' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_FRAC_OVERLAP -v n2=0.0 'BEGIN { print (n1 >= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--frac_overlap' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--frac_overlap' specifies a minimum value but the value was not verified as neither \'bc\' or \`awk\` are present on the system. + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_FRAC_OVERLAP '<=' 1.0 | bc` -eq 1 ]]; then + ViashError '--frac_overlap' has to be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_FRAC_OVERLAP -v n2=1.0 'BEGIN { print (n1 <= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--frac_overlap' has be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--frac_overlap' specifies a maximum value but the value was not verified as neither \'bc\' or \'awk\' are present on the system. + fi +fi +if [[ -n "$VIASH_PAR_FRAC_OVERLAP_FEATURE" ]]; then + if ! [[ "$VIASH_PAR_FRAC_OVERLAP_FEATURE" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--frac_overlap_feature' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_FRAC_OVERLAP_FEATURE '>=' 0.0 | bc` -eq 1 ]]; then + ViashError '--frac_overlap_feature' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_FRAC_OVERLAP_FEATURE -v n2=0.0 'BEGIN { print (n1 >= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--frac_overlap_feature' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--frac_overlap_feature' specifies a minimum value but the value was not verified as neither \'bc\' or \`awk\` are present on the system. + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_FRAC_OVERLAP_FEATURE '<=' 1.0 | bc` -eq 1 ]]; then + ViashError '--frac_overlap_feature' has to be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_FRAC_OVERLAP_FEATURE -v n2=1.0 'BEGIN { print (n1 <= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--frac_overlap_feature' has be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--frac_overlap_feature' specifies a maximum value but the value was not verified as neither \'bc\' or \'awk\' are present on the system. + fi +fi +if [[ -n "$VIASH_PAR_LARGEST_OVERLAP" ]]; then + if ! [[ "$VIASH_PAR_LARGEST_OVERLAP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--largest_overlap' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NON_OVERLAP" ]]; then + if ! [[ "$VIASH_PAR_NON_OVERLAP" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--non_overlap' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NON_OVERLAP_FEATURE" ]]; then + if ! [[ "$VIASH_PAR_NON_OVERLAP_FEATURE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--non_overlap_feature' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_READ_EXTENSION5" ]]; then + if ! [[ "$VIASH_PAR_READ_EXTENSION5" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--read_extension5' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_READ_EXTENSION3" ]]; then + if ! [[ "$VIASH_PAR_READ_EXTENSION3" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--read_extension3' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_READ2POS" ]]; then + if ! [[ "$VIASH_PAR_READ2POS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--read2pos' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MULTI_MAPPING" ]]; then + if ! [[ "$VIASH_PAR_MULTI_MAPPING" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--multi_mapping' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FRACTION" ]]; then + if ! [[ "$VIASH_PAR_FRACTION" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--fraction' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_MAP_QUALITY" ]]; then + if ! [[ "$VIASH_PAR_MIN_MAP_QUALITY" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_map_quality' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SPLIT_ONLY" ]]; then + if ! [[ "$VIASH_PAR_SPLIT_ONLY" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--split_only' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NON_SPLIT_ONLY" ]]; then + if ! [[ "$VIASH_PAR_NON_SPLIT_ONLY" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--non_split_only' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PRIMARY" ]]; then + if ! [[ "$VIASH_PAR_PRIMARY" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--primary' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_IGNORE_DUP" ]]; then + if ! [[ "$VIASH_PAR_IGNORE_DUP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--ignore_dup' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_STRAND" ]]; then + if ! [[ "$VIASH_PAR_STRAND" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--strand' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PAIRED" ]]; then + if ! [[ "$VIASH_PAR_PAIRED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--paired' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_COUNT_READ_PAIRS" ]]; then + if ! [[ "$VIASH_PAR_COUNT_READ_PAIRS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--count_read_pairs' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BOTH_ALIGNED" ]]; then + if ! [[ "$VIASH_PAR_BOTH_ALIGNED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--both_aligned' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHECK_PE_DIST" ]]; then + if ! [[ "$VIASH_PAR_CHECK_PE_DIST" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--check_pe_dist' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_MIN_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_MAX_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SAME_STRAND" ]]; then + if ! [[ "$VIASH_PAR_SAME_STRAND" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--same_strand' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DONOTSORT" ]]; then + if ! [[ "$VIASH_PAR_DONOTSORT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--donotsort' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BY_READ_GROUP" ]]; then + if ! [[ "$VIASH_PAR_BY_READ_GROUP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--by_read_group' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LONG_READS" ]]; then + if ! [[ "$VIASH_PAR_LONG_READS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--long_reads' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_M_OP" ]]; then + if ! [[ "$VIASH_PAR_MAX_M_OP" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_M_op' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_VERBOSE" ]]; then + if ! [[ "$VIASH_PAR_VERBOSE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--verbose' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# check whether value is belongs to a set of choices +if [ ! -z "$VIASH_PAR_FORMAT" ]; then + VIASH_PAR_FORMAT_CHOICES=("GTF;GFF;SAF") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_FORMAT_CHOICES[*]};" =~ ";$VIASH_PAR_FORMAT;" ]]; then + ViashError '--format' specified value of \'$VIASH_PAR_FORMAT\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +if [ ! -z "$VIASH_PAR_READ2POS" ]; then + VIASH_PAR_READ2POS_CHOICES=("3;5") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_READ2POS_CHOICES[*]};" =~ ";$VIASH_PAR_READ2POS;" ]]; then + ViashError '--read2pos' specified value of \'$VIASH_PAR_READ2POS\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +if [ ! -z "$VIASH_PAR_STRAND" ]; then + VIASH_PAR_STRAND_CHOICES=("0;1;2") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_STRAND_CHOICES[*]};" =~ ";$VIASH_PAR_STRAND;" ]]; then + ViashError '--strand' specified value of \'$VIASH_PAR_STRAND\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +if [ ! -z "$VIASH_PAR_DETAILED_RESULTS_FORMAT" ]; then + VIASH_PAR_DETAILED_RESULTS_FORMAT_CHOICES=("CORE;SAM;BAM") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_DETAILED_RESULTS_FORMAT_CHOICES[*]};" =~ ";$VIASH_PAR_DETAILED_RESULTS_FORMAT;" ]]; then + ViashError '--detailed_results_format' specified value of \'$VIASH_PAR_DETAILED_RESULTS_FORMAT\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_COUNTS" ] && [ ! -d "$(dirname "$VIASH_PAR_COUNTS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_COUNTS")" +fi +if [ ! -z "$VIASH_PAR_SUMMARY" ] && [ ! -d "$(dirname "$VIASH_PAR_SUMMARY")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_SUMMARY")" +fi +if [ ! -z "$VIASH_PAR_JUNCTIONS" ] && [ ! -d "$(dirname "$VIASH_PAR_JUNCTIONS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_JUNCTIONS")" +fi +if [ ! -z "$VIASH_PAR_DETAILED_RESULTS" ] && [ ! -d "$(dirname "$VIASH_PAR_DETAILED_RESULTS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_DETAILED_RESULTS")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_ANNOTATION" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_ANNOTATION")" ) + VIASH_PAR_ANNOTATION=$(ViashDockerAutodetectMount "$VIASH_PAR_ANNOTATION") +fi +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_TEST_INPUT=() + IFS=';' + for var in $VIASH_PAR_INPUT; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_INPUT+=( "$var" ) + done + VIASH_PAR_INPUT=$(IFS=';' ; echo "${VIASH_TEST_INPUT[*]}") +fi +if [ ! -z "$VIASH_PAR_COUNTS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_COUNTS")" ) + VIASH_PAR_COUNTS=$(ViashDockerAutodetectMount "$VIASH_PAR_COUNTS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_COUNTS" ) +fi +if [ ! -z "$VIASH_PAR_SUMMARY" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SUMMARY")" ) + VIASH_PAR_SUMMARY=$(ViashDockerAutodetectMount "$VIASH_PAR_SUMMARY") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_SUMMARY" ) +fi +if [ ! -z "$VIASH_PAR_JUNCTIONS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_JUNCTIONS")" ) + VIASH_PAR_JUNCTIONS=$(ViashDockerAutodetectMount "$VIASH_PAR_JUNCTIONS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_JUNCTIONS" ) +fi +if [ ! -z "$VIASH_PAR_CHROM_ALIAS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_CHROM_ALIAS")" ) + VIASH_PAR_CHROM_ALIAS=$(ViashDockerAutodetectMount "$VIASH_PAR_CHROM_ALIAS") +fi +if [ ! -z "$VIASH_PAR_REF_FASTA" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REF_FASTA")" ) + VIASH_PAR_REF_FASTA=$(ViashDockerAutodetectMount "$VIASH_PAR_REF_FASTA") +fi +if [ ! -z "$VIASH_PAR_DETAILED_RESULTS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_DETAILED_RESULTS")" ) + VIASH_PAR_DETAILED_RESULTS=$(ViashDockerAutodetectMount "$VIASH_PAR_DETAILED_RESULTS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_DETAILED_RESULTS" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-featurecounts-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +set -e + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_ANNOTATION+x} ]; then echo "${VIASH_PAR_ANNOTATION}" | sed "s#'#'\"'\"'#g;s#.*#par_annotation='&'#" ; else echo "# par_annotation="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_COUNTS+x} ]; then echo "${VIASH_PAR_COUNTS}" | sed "s#'#'\"'\"'#g;s#.*#par_counts='&'#" ; else echo "# par_counts="; fi ) +$( if [ ! -z ${VIASH_PAR_SUMMARY+x} ]; then echo "${VIASH_PAR_SUMMARY}" | sed "s#'#'\"'\"'#g;s#.*#par_summary='&'#" ; else echo "# par_summary="; fi ) +$( if [ ! -z ${VIASH_PAR_JUNCTIONS+x} ]; then echo "${VIASH_PAR_JUNCTIONS}" | sed "s#'#'\"'\"'#g;s#.*#par_junctions='&'#" ; else echo "# par_junctions="; fi ) +$( if [ ! -z ${VIASH_PAR_FORMAT+x} ]; then echo "${VIASH_PAR_FORMAT}" | sed "s#'#'\"'\"'#g;s#.*#par_format='&'#" ; else echo "# par_format="; fi ) +$( if [ ! -z ${VIASH_PAR_FEATURE_TYPE+x} ]; then echo "${VIASH_PAR_FEATURE_TYPE}" | sed "s#'#'\"'\"'#g;s#.*#par_feature_type='&'#" ; else echo "# par_feature_type="; fi ) +$( if [ ! -z ${VIASH_PAR_ATTRIBUTE_TYPE+x} ]; then echo "${VIASH_PAR_ATTRIBUTE_TYPE}" | sed "s#'#'\"'\"'#g;s#.*#par_attribute_type='&'#" ; else echo "# par_attribute_type="; fi ) +$( if [ ! -z ${VIASH_PAR_EXTRA_ATTRIBUTES+x} ]; then echo "${VIASH_PAR_EXTRA_ATTRIBUTES}" | sed "s#'#'\"'\"'#g;s#.*#par_extra_attributes='&'#" ; else echo "# par_extra_attributes="; fi ) +$( if [ ! -z ${VIASH_PAR_CHROM_ALIAS+x} ]; then echo "${VIASH_PAR_CHROM_ALIAS}" | sed "s#'#'\"'\"'#g;s#.*#par_chrom_alias='&'#" ; else echo "# par_chrom_alias="; fi ) +$( if [ ! -z ${VIASH_PAR_FEATURE_LEVEL+x} ]; then echo "${VIASH_PAR_FEATURE_LEVEL}" | sed "s#'#'\"'\"'#g;s#.*#par_feature_level='&'#" ; else echo "# par_feature_level="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAPPING+x} ]; then echo "${VIASH_PAR_OVERLAPPING}" | sed "s#'#'\"'\"'#g;s#.*#par_overlapping='&'#" ; else echo "# par_overlapping="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_OVERLAP+x} ]; then echo "${VIASH_PAR_MIN_OVERLAP}" | sed "s#'#'\"'\"'#g;s#.*#par_min_overlap='&'#" ; else echo "# par_min_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_FRAC_OVERLAP+x} ]; then echo "${VIASH_PAR_FRAC_OVERLAP}" | sed "s#'#'\"'\"'#g;s#.*#par_frac_overlap='&'#" ; else echo "# par_frac_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_FRAC_OVERLAP_FEATURE+x} ]; then echo "${VIASH_PAR_FRAC_OVERLAP_FEATURE}" | sed "s#'#'\"'\"'#g;s#.*#par_frac_overlap_feature='&'#" ; else echo "# par_frac_overlap_feature="; fi ) +$( if [ ! -z ${VIASH_PAR_LARGEST_OVERLAP+x} ]; then echo "${VIASH_PAR_LARGEST_OVERLAP}" | sed "s#'#'\"'\"'#g;s#.*#par_largest_overlap='&'#" ; else echo "# par_largest_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_NON_OVERLAP+x} ]; then echo "${VIASH_PAR_NON_OVERLAP}" | sed "s#'#'\"'\"'#g;s#.*#par_non_overlap='&'#" ; else echo "# par_non_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_NON_OVERLAP_FEATURE+x} ]; then echo "${VIASH_PAR_NON_OVERLAP_FEATURE}" | sed "s#'#'\"'\"'#g;s#.*#par_non_overlap_feature='&'#" ; else echo "# par_non_overlap_feature="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_EXTENSION5+x} ]; then echo "${VIASH_PAR_READ_EXTENSION5}" | sed "s#'#'\"'\"'#g;s#.*#par_read_extension5='&'#" ; else echo "# par_read_extension5="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_EXTENSION3+x} ]; then echo "${VIASH_PAR_READ_EXTENSION3}" | sed "s#'#'\"'\"'#g;s#.*#par_read_extension3='&'#" ; else echo "# par_read_extension3="; fi ) +$( if [ ! -z ${VIASH_PAR_READ2POS+x} ]; then echo "${VIASH_PAR_READ2POS}" | sed "s#'#'\"'\"'#g;s#.*#par_read2pos='&'#" ; else echo "# par_read2pos="; fi ) +$( if [ ! -z ${VIASH_PAR_MULTI_MAPPING+x} ]; then echo "${VIASH_PAR_MULTI_MAPPING}" | sed "s#'#'\"'\"'#g;s#.*#par_multi_mapping='&'#" ; else echo "# par_multi_mapping="; fi ) +$( if [ ! -z ${VIASH_PAR_FRACTION+x} ]; then echo "${VIASH_PAR_FRACTION}" | sed "s#'#'\"'\"'#g;s#.*#par_fraction='&'#" ; else echo "# par_fraction="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_MAP_QUALITY+x} ]; then echo "${VIASH_PAR_MIN_MAP_QUALITY}" | sed "s#'#'\"'\"'#g;s#.*#par_min_map_quality='&'#" ; else echo "# par_min_map_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLIT_ONLY+x} ]; then echo "${VIASH_PAR_SPLIT_ONLY}" | sed "s#'#'\"'\"'#g;s#.*#par_split_only='&'#" ; else echo "# par_split_only="; fi ) +$( if [ ! -z ${VIASH_PAR_NON_SPLIT_ONLY+x} ]; then echo "${VIASH_PAR_NON_SPLIT_ONLY}" | sed "s#'#'\"'\"'#g;s#.*#par_non_split_only='&'#" ; else echo "# par_non_split_only="; fi ) +$( if [ ! -z ${VIASH_PAR_PRIMARY+x} ]; then echo "${VIASH_PAR_PRIMARY}" | sed "s#'#'\"'\"'#g;s#.*#par_primary='&'#" ; else echo "# par_primary="; fi ) +$( if [ ! -z ${VIASH_PAR_IGNORE_DUP+x} ]; then echo "${VIASH_PAR_IGNORE_DUP}" | sed "s#'#'\"'\"'#g;s#.*#par_ignore_dup='&'#" ; else echo "# par_ignore_dup="; fi ) +$( if [ ! -z ${VIASH_PAR_STRAND+x} ]; then echo "${VIASH_PAR_STRAND}" | sed "s#'#'\"'\"'#g;s#.*#par_strand='&'#" ; else echo "# par_strand="; fi ) +$( if [ ! -z ${VIASH_PAR_REF_FASTA+x} ]; then echo "${VIASH_PAR_REF_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_ref_fasta='&'#" ; else echo "# par_ref_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_PAIRED+x} ]; then echo "${VIASH_PAR_PAIRED}" | sed "s#'#'\"'\"'#g;s#.*#par_paired='&'#" ; else echo "# par_paired="; fi ) +$( if [ ! -z ${VIASH_PAR_COUNT_READ_PAIRS+x} ]; then echo "${VIASH_PAR_COUNT_READ_PAIRS}" | sed "s#'#'\"'\"'#g;s#.*#par_count_read_pairs='&'#" ; else echo "# par_count_read_pairs="; fi ) +$( if [ ! -z ${VIASH_PAR_BOTH_ALIGNED+x} ]; then echo "${VIASH_PAR_BOTH_ALIGNED}" | sed "s#'#'\"'\"'#g;s#.*#par_both_aligned='&'#" ; else echo "# par_both_aligned="; fi ) +$( if [ ! -z ${VIASH_PAR_CHECK_PE_DIST+x} ]; then echo "${VIASH_PAR_CHECK_PE_DIST}" | sed "s#'#'\"'\"'#g;s#.*#par_check_pe_dist='&'#" ; else echo "# par_check_pe_dist="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_LENGTH+x} ]; then echo "${VIASH_PAR_MIN_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_min_length='&'#" ; else echo "# par_min_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_LENGTH+x} ]; then echo "${VIASH_PAR_MAX_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_max_length='&'#" ; else echo "# par_max_length="; fi ) +$( if [ ! -z ${VIASH_PAR_SAME_STRAND+x} ]; then echo "${VIASH_PAR_SAME_STRAND}" | sed "s#'#'\"'\"'#g;s#.*#par_same_strand='&'#" ; else echo "# par_same_strand="; fi ) +$( if [ ! -z ${VIASH_PAR_DONOTSORT+x} ]; then echo "${VIASH_PAR_DONOTSORT}" | sed "s#'#'\"'\"'#g;s#.*#par_donotsort='&'#" ; else echo "# par_donotsort="; fi ) +$( if [ ! -z ${VIASH_PAR_BY_READ_GROUP+x} ]; then echo "${VIASH_PAR_BY_READ_GROUP}" | sed "s#'#'\"'\"'#g;s#.*#par_by_read_group='&'#" ; else echo "# par_by_read_group="; fi ) +$( if [ ! -z ${VIASH_PAR_LONG_READS+x} ]; then echo "${VIASH_PAR_LONG_READS}" | sed "s#'#'\"'\"'#g;s#.*#par_long_reads='&'#" ; else echo "# par_long_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_DETAILED_RESULTS+x} ]; then echo "${VIASH_PAR_DETAILED_RESULTS}" | sed "s#'#'\"'\"'#g;s#.*#par_detailed_results='&'#" ; else echo "# par_detailed_results="; fi ) +$( if [ ! -z ${VIASH_PAR_DETAILED_RESULTS_FORMAT+x} ]; then echo "${VIASH_PAR_DETAILED_RESULTS_FORMAT}" | sed "s#'#'\"'\"'#g;s#.*#par_detailed_results_format='&'#" ; else echo "# par_detailed_results_format="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_M_OP+x} ]; then echo "${VIASH_PAR_MAX_M_OP}" | sed "s#'#'\"'\"'#g;s#.*#par_max_M_op='&'#" ; else echo "# par_max_M_op="; fi ) +$( if [ ! -z ${VIASH_PAR_VERBOSE+x} ]; then echo "${VIASH_PAR_VERBOSE}" | sed "s#'#'\"'\"'#g;s#.*#par_verbose='&'#" ; else echo "# par_verbose="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# create temporary directory +tmp_dir=\$(mktemp -d -p "\$meta_temp_dir" "\${meta_functionality_name}_XXXXXX") +mkdir -p "\$tmp_dir/temp" + +# create detailed_results directory if variable is set and directory does not exist +if [[ ! -z "\$par_detailed_results" ]] && [[ ! -d "\$par_detailed_results" ]]; then + mkdir -p "\$par_detailed_results" +fi + +# replace comma with semicolon +par_feature_type=\$(echo \$par_feature_type | tr ',' ';') +par_extra_attributes=\$(echo \$par_extra_attributes | tr ',' ';') + +# unset flag variables +[[ "\$par_feature_level" == "false" ]] && unset par_feature_level +[[ "\$par_overlapping" == "false" ]] && unset par_overlapping +[[ "\$par_largest_overlap" == "false" ]] && unset par_largest_overlap +[[ "\$par_multi_mapping" == "false" ]] && unset par_multi_mapping +[[ "\$par_fraction" == "false" ]] && unset par_fraction +[[ "\$par_split_only" == "false" ]] && unset par_split_only +[[ "\$par_non_split_only" == "false" ]] && unset par_non_split_only +[[ "\$par_primary" == "false" ]] && unset par_primary +[[ "\$par_ignore_dup" == "false" ]] && unset par_ignore_dup +[[ "\$par_paired" == "false" ]] && unset par_paired +[[ "\$par_count_read_pairs" == "false" ]] && unset par_count_read_pairs +[[ "\$par_both_aligned" == "false" ]] && unset par_both_aligned +[[ "\$par_check_pe_dist" == "false" ]] && unset par_check_pe_dist +[[ "\$par_same_strand" == "false" ]] && unset par_same_strand +[[ "\$par_donotsort" == "false" ]] && unset par_donotsort +[[ "\$par_by_read_group" == "false" ]] && unset par_by_read_group +[[ "\$par_long_reads" == "false" ]] && unset par_long_reads +[[ "\$par_verbose" == "false" ]] && unset par_verbose + +IFS=";" read -ra input <<< \$par_input + +featureCounts \\ + \${par_format:+-F "\${par_format}"} \\ + \${par_feature_type:+-t "\${par_feature_type}"} \\ + \${par_attribute_type:+-g "\${par_attribute_type}"} \\ + \${par_extra_attributes:+--extraAttributes "\${extra_attributes}"} \\ + \${par_chrom_alias:+-A "\${par_chrom_alias}"} \\ + \${par_feature_level:+-f} \\ + \${par_overlapping:+-O} \\ + \${par_min_overlap:+--minOverlap "\${par_min_overlap}"} \\ + \${par_frac_overlap:+--fracOverlap "\${par_frac_overlap}"} \\ + \${par_frac_overlap_feature:+--fracOverlapFeature "\${par_frac_overlap_feature}"} \\ + \${par_largest_overlap:+--largestOverlap} \\ + \${par_non_overlap:+--nonOverlap "\${par_non_overlap}"} \\ + \${par_non_overlap_feature:+--nonOverlapFeature "\${par_non_overlap_feature}"} \\ + \${par_read_extension5:+--readExtension5 "\${par_read_extension5}"} \\ + \${par_read_extension3:+--readExtension3 "\${par_read_extension3}"} \\ + \${par_read2pos:+--read2pos "\${par_read2pos}"} \\ + \${par_multi_mapping:+-M} \\ + \${par_fraction:+--fraction} \\ + \${par_min_map_quality:+-Q "\${par_min_map_quality}"} \\ + \${par_split_only:+--splitOnly} \\ + \${par_non_split_only:+--nonSplitOnly} \\ + \${par_primary:+--primary} \\ + \${par_ignore_dup:+--ignoreDup} \\ + \${par_strand:+-s "\${par_strand}"} \\ + \${par_junctions:+-J} \\ + \${par_ref_fasta:+-G "\${par_ref_fasta}"} \\ + \${par_paired:+-p} \\ + \${par_count_read_pairs:+--countReadPairs} \\ + \${par_both_aligned:+-B} \\ + \${par_check_pe_dist:+-P} \\ + \${par_min_length:+-d "\${par_min_length}"} \\ + \${par_max_length:+-D "\${par_max_length}"} \\ + \${par_same_strand:+-C} \\ + \${par_donotsort:+--donotsort} \\ + \${par_by_read_group:+--byReadGroup} \\ + \${par_long_reads:+-L} \\ + \${par_detailed_results:+--Rpath "\${par_detailed_results}"} \\ + \${par_detailed_results_format:+-R "\${par_detailed_results_format}"} \\ + \${par_max_M_op:+--maxMOp "\${par_max_M_op}"} \\ + \${par_verbose:+--verbose} \\ + \${meta_cpus:+-T "\${meta_cpus}"} \\ + --tmpDir "\$tmp_dir/temp" \\ + -a "\$par_annotation" \\ + -o "\$tmp_dir/output.txt" \\ + "\${input[*]}" + +[[ ! -z "\$par_counts" ]] && mv "\$tmp_dir/output.txt" "\$par_counts" +[[ ! -z "\$par_summary" ]] && mv "\$tmp_dir/output.txt.summary" "\$par_summary" +if [[ ! -z "\$par_junctions" ]] && [[ -e "\$tmp_dir/output.txt.jcounts" ]]; then + mv "\$tmp_dir/output.txt.jcounts" "\$par_junctions" +fi +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_ANNOTATION" ]; then + VIASH_PAR_ANNOTATION=$(ViashDockerStripAutomount "$VIASH_PAR_ANNOTATION") + fi + if [ ! -z "$VIASH_PAR_INPUT" ]; then + unset VIASH_TEST_INPUT + IFS=';' + for var in $VIASH_PAR_INPUT; do + unset IFS + if [ -z "$VIASH_TEST_INPUT" ]; then + VIASH_TEST_INPUT="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_INPUT="$VIASH_TEST_INPUT;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_INPUT="$VIASH_TEST_INPUT" + fi + if [ ! -z "$VIASH_PAR_COUNTS" ]; then + VIASH_PAR_COUNTS=$(ViashDockerStripAutomount "$VIASH_PAR_COUNTS") + fi + if [ ! -z "$VIASH_PAR_SUMMARY" ]; then + VIASH_PAR_SUMMARY=$(ViashDockerStripAutomount "$VIASH_PAR_SUMMARY") + fi + if [ ! -z "$VIASH_PAR_JUNCTIONS" ]; then + VIASH_PAR_JUNCTIONS=$(ViashDockerStripAutomount "$VIASH_PAR_JUNCTIONS") + fi + if [ ! -z "$VIASH_PAR_CHROM_ALIAS" ]; then + VIASH_PAR_CHROM_ALIAS=$(ViashDockerStripAutomount "$VIASH_PAR_CHROM_ALIAS") + fi + if [ ! -z "$VIASH_PAR_REF_FASTA" ]; then + VIASH_PAR_REF_FASTA=$(ViashDockerStripAutomount "$VIASH_PAR_REF_FASTA") + fi + if [ ! -z "$VIASH_PAR_DETAILED_RESULTS" ]; then + VIASH_PAR_DETAILED_RESULTS=$(ViashDockerStripAutomount "$VIASH_PAR_DETAILED_RESULTS") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_COUNTS" ] && [ ! -e "$VIASH_PAR_COUNTS" ]; then + ViashError "Output file '$VIASH_PAR_COUNTS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_SUMMARY" ] && [ ! -e "$VIASH_PAR_SUMMARY" ]; then + ViashError "Output file '$VIASH_PAR_SUMMARY' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_JUNCTIONS" ] && [ ! -e "$VIASH_PAR_JUNCTIONS" ]; then + ViashError "Output file '$VIASH_PAR_JUNCTIONS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_DETAILED_RESULTS" ] && [ ! -e "$VIASH_PAR_DETAILED_RESULTS" ]; then + ViashError "Output file '$VIASH_PAR_DETAILED_RESULTS' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/gffread/.config.vsh.yaml b/target/executable/gffread/.config.vsh.yaml new file mode 100644 index 00000000..26ef7d49 --- /dev/null +++ b/target/executable/gffread/.config.vsh.yaml @@ -0,0 +1,697 @@ +name: "gffread" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "A reference file in either the GFF3, GFF2 or GTF format.\n" + info: null + example: + - "annotation.gff" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--chr_mapping" + alternatives: + - "-m" + description: " is a name mapping table for converting reference sequence\ + \ names, \nhaving this 2-column format: .\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--seq_info" + alternatives: + - "-s" + description: " is a tab-delimited file providing this info for\ + \ each of the mapped \nsequences: \ + \ (useful for --description option with \nmRNA/EST/protein mappings).\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--genome" + alternatives: + - "-g" + description: "Full path to a multi-fasta file with the genomic sequences for all\ + \ input mappings, \nOR a directory with single-fasta files (one per genomic\ + \ sequence, with file names \nmatching sequence names).\n" + info: null + example: + - "genome.fa" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--outfile" + alternatives: + - "-o" + description: "Write the output records into .\n" + info: null + default: + - "output.gff" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--force_exons" + description: "Make sure that the lowest level GFF features are considered \"exon\"\ + \ features.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--gene2exon" + description: "For single-line genes not parenting any transcripts, add an exon\ + \ feature spanning \nthe entire gene (treat it as a transcript).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--t_adopt" + description: "Try to find a parent gene overlapping/containing a transcript that\ + \ does not have \nany explicit gene Parent.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--decode" + alternatives: + - "-D" + description: "Decode url encoded characters within attributes.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--merge_exons" + alternatives: + - "-Z" + description: "Merge very close exons into a single exon (when intron size<4).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--junctions" + alternatives: + - "-j" + description: "Output the junctions and the corresponding transcripts.\n" + info: null + direction: "input" + - type: "file" + name: "--spliced_exons" + alternatives: + - "-w" + description: "Write a fasta file with spliced exons for each transcript.\n" + info: null + example: + - "exons.fa" + must_exist: false + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--w_add" + description: "For the --spliced_exons option, extract additional bases both\ + \ upstream and \ndownstream of the transcript boundaries.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--w_nocds" + description: "For --spliced_exons, disable the output of CDS info in the FASTA\ + \ file.\n" + info: null + direction: "input" + - type: "file" + name: "--spliced_cds" + alternatives: + - "-x" + description: "Write a fasta file with spliced CDS for each GFF transcript.\n" + info: null + example: + - "cds.fa" + must_exist: false + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--tr_cds" + alternatives: + - "-y" + description: "Write a protein fasta file with the translation of CDS for each\ + \ record.\n" + info: null + example: + - "tr_cds.fa" + must_exist: false + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--w_coords" + alternatives: + - "-W" + description: "For --spliced_exons, --spliced_cds and -tr_cds options, write in\ + \ the FASTA defline \nall the exon coordinates projected onto the spliced sequence.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--stop_dot" + alternatives: + - "-S" + description: "For --tr_cds option, use '*' instead of '.' as stop codon translation.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--id_version" + alternatives: + - "-L" + description: "Ensembl GTF to GFF3 conversion, adds version to IDs.\n" + info: null + direction: "input" + - type: "string" + name: "--trackname" + alternatives: + - "-t" + description: "Use in the 2nd column of each GFF/GTF output line.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--gtf_output" + alternatives: + - "-T" + description: "Main output will be GTF instead of GFF3.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--bed" + description: "Output records in BED format instead of default GFF3.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--tlf" + description: "Output \"transcript line format\" which is like GFF but with exons\ + \ and CDS related \nfeatures stored as GFF attributes in the transcript feature\ + \ line, like this:\n exoncount=N;exons=;CDSphase=;CDS=\n\ + is a comma-delimited list of exon_start-exon_end coordinates;\n\ + \ is CDS_start:CDS_end coordinates or a list like .\n" + info: null + direction: "input" + - type: "string" + name: "--table" + description: "Output a simple tab delimited format instead of GFF, with columns\ + \ having the values \nof GFF attributes given in ; special pseudo-attributes\ + \ (prefixed by @) are \nrecognized:\n @id, @geneid, @chr, @start, @end, @strand,\ + \ @numexons, @exons, @cds, @covlen, @cdslen\nIf any of --spliced_exons/--tr_cds/--spliced_cds\ + \ FASTA output files are enabled, the \nsame fields (excluding @id) are appended\ + \ to the definition line of corresponding FASTA\nrecords.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "boolean_true" + name: "--expose_dups" + alternatives: + - "-E" + - "-v" + description: "Expose (warn about) duplicate transcript IDs and other potential\ + \ problems with the \ngiven GFF/GTF records.\n" + info: null + direction: "input" +- name: "Options" + arguments: + - type: "file" + name: "--ids" + description: "Discard records/transcripts if their IDs are not listed in .\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--nids" + description: "Discard records/transcripts if their IDs are listed in .\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--maxintron" + alternatives: + - "-i" + description: "Discard transcripts having an intron larger than .\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--minlen" + alternatives: + - "-l" + description: "Discard transcripts shorter than bases.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--range" + alternatives: + - "-r" + description: "Only show transcripts overlapping coordinate range ..\ + \ (on chromosome/contig \n, strand if provided).\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--strict_range" + alternatives: + - "-R" + description: "For --range option, discard all transcripts that are not fully contained\ + \ within the given \nrange.\n" + info: null + direction: "input" + - type: "string" + name: "--jmatch" + description: "Only output transcripts matching the given junction.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_single_exon" + alternatives: + - "-U" + description: "Discard single-exon transcripts.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--coding" + alternatives: + - "-C" + description: "Coding only: discard mRNAs that have no CDS features.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--nc" + description: "Non-coding only: discard mRNAs that have CDS features.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--ignore_locus" + description: "Discard locus features and attributes found in the input.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--description" + alternatives: + - "-A" + description: "Use the description field from and add it as the\ + \ value for a 'descr' \nattribute to the GFF record.\n" + info: null + direction: "input" +- name: "Sorting" + arguments: + - type: "boolean_true" + name: "--sort_alpha" + description: "Chromosomes (reference sequences) are sorted alphabetically.\n" + info: null + direction: "input" + - type: "file" + name: "--sort_by" + description: "Sort the reference sequences by the order in which their names are\ + \ given in the \n file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Misc options" + arguments: + - type: "boolean_true" + name: "--keep_attrs" + alternatives: + - "-F" + description: "Keep all GFF attributes (for non-exon features).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--keep_exon_attrs" + description: "For -F option, do not attempt to reduce redundant exon/CDS attributes.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_exon_attrs" + alternatives: + - "-G" + description: "Do not keep exon attributes, move them to the transcript feature\ + \ (for GFF3 output).\n" + info: null + direction: "input" + - type: "string" + name: "--attrs" + description: "Only output the GTF/GFF attributes listed in which is\ + \ a comma delimited \nlist of attribute names to.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--keep_genes" + description: "In transcript-only mode (default), also preserve gene records.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--keep_comments" + description: "For GFF3 input/output, try to preserve comments.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--process_other" + alternatives: + - "-O" + description: "process other non-transcript GFF records (by default non-transcript\ + \ records are ignored).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--rm_stop_codons" + alternatives: + - "-V" + description: "Discard any mRNAs with CDS having in-frame stop codons (requires\ + \ --genome).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--adj_cds_start" + alternatives: + - "-H" + description: "For --rm_stop_codons option, check and adjust the starting CDS phase\ + \ if the original phase\nleads to a translation with an in-frame stop codon.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--opposite_strand" + alternatives: + - "-B" + description: "For -V option, single-exon transcripts are also checked on the opposite\ + \ strand (requires \n--genome). \n" + info: null + direction: "input" + - type: "boolean_true" + name: "--coding_status" + alternatives: + - "-P" + description: "Add transcript level GFF attributes about the coding status of each\ + \ transcript, including \npartialness or in-frame stop codons (requires --genome).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--add_hasCDS" + description: "Add a \"hasCDS\" attribute with value \"true\" for transcripts that\ + \ have CDS features. \n" + info: null + direction: "input" + - type: "boolean_true" + name: "--adj_stop" + description: "Stop codon adjustment: enables --coding_status and performs automatic\ + \ adjustment of the CDS stop \ncoordinate if premature or downstream.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--rm_noncanon" + alternatives: + - "-N" + description: "Discard multi-exon mRNAs that have any intron with a non-canonical\ + \ splice site consensus \n(i.e. not GT-AG, GC-AG or AT-AC).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--complete_cds" + alternatives: + - "-J" + description: "Discard any mRNAs that either lack initial START codon or the terminal\ + \ STOP codon, or \nhave an in-frame stop codon (i.e. only print mRNAs with a\ + \ complete CDS).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_pseudo" + description: "Filter out records matching the 'pseudo' keyword.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--in_bed" + description: "Input should be parsed as BED format (automatic if the input filename\ + \ ends with .bed*).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--in_tlf" + description: "Input GFF-like one-line-per-transcript format without exon/CDS features\ + \ (see --tlf option \nbelow); automatic if the input filename ends with .tlf).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--stream" + description: "Fast processing of input GFF/BED transcripts as they are received\ + \ (no sorting, exons must \nbe grouped by transcript in the input data).\n" + info: null + direction: "input" +- name: "Clustering" + arguments: + - type: "boolean_true" + name: "--merge" + alternatives: + - "-M" + description: "Cluster the input transcripts into loci, discarding \"redundant\"\ + \ transcripts (those with \nthe same exact introns and fully contained or equal\ + \ boundaries).\n" + info: null + direction: "input" + - type: "file" + name: "--dupinfo" + alternatives: + - "-d" + description: "For --merge option, write duplication info to file .\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--cluster_only" + description: "Same as --merge but without discarding any of the \"duplicate\"\ + \ transcripts, only create \n\"locus\" features.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--rm_redundant" + alternatives: + - "-K" + description: "For --merge option: also discard as redundant the shorter, fully\ + \ contained transcripts (intron \nchains matching a part of the container).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_boundary" + alternatives: + - "-Q" + description: "For --merge option, no longer require boundary containment when\ + \ assessing redundancy (can be \ncombined with --rm_redundant); only introns\ + \ have to match for multi-exon transcripts, and >=80%\noverlap for single-exon\ + \ transcripts.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_overlap" + alternatives: + - "-Y" + description: "For --merge option, enforce --no_boundary but also discard overlapping\ + \ single-exon transcripts,\neven on the opposite strand (can be combined with\ + \ --rm_redudant).\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Validate, filter, convert and perform various other operations on GFF\ + \ files." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "gff" +- "conversion" +- "validation" +- "filtering" +license: "MIT" +references: + doi: + - "10.12688/f1000research.23297.2" +links: + repository: "https://github.com/gpertea/gffread" + homepage: "https://ccb.jhu.edu/software/stringtie/gff.shtml#gffread" + documentation: "https://ccb.jhu.edu/software/stringtie/gff.shtml#gffread" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/gffread:0.12.7--hdcf5f25_3" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "echo \"gffread: \\\"$(gffread --version 2>&1)\\\"\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/gffread/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/gffread" + executable: "target/executable/gffread/gffread" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/gffread/gffread b/target/executable/gffread/gffread new file mode 100755 index 00000000..c659368e --- /dev/null +++ b/target/executable/gffread/gffread @@ -0,0 +1,2702 @@ +#!/usr/bin/env bash + +# gffread main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="gffread" +VIASH_META_FUNCTIONALITY_NAME="gffread" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "gffread main" + echo "" + echo "Validate, filter, convert and perform various other operations on GFF files." + echo "" + echo "Inputs:" + echo " --input" + echo " type: file, required parameter, file must exist" + echo " example: annotation.gff" + echo " A reference file in either the GFF3, GFF2 or GTF format." + echo "" + echo " -m, --chr_mapping" + echo " type: file, file must exist" + echo " is a name mapping table for converting reference sequence" + echo " names," + echo " having this 2-column format: ." + echo "" + echo " -s, --seq_info" + echo " type: file, file must exist" + echo " is a tab-delimited file providing this info for each of" + echo " the mapped" + echo " sequences: (useful for" + echo " --description option with" + echo " mRNA/EST/protein mappings)." + echo "" + echo " -g, --genome" + echo " type: file, file must exist" + echo " example: genome.fa" + echo " Full path to a multi-fasta file with the genomic sequences for all input" + echo " mappings," + echo " OR a directory with single-fasta files (one per genomic sequence, with" + echo " file names" + echo " matching sequence names)." + echo "" + echo "Outputs:" + echo " -o, --outfile" + echo " type: file, required parameter, output, file must exist" + echo " default: output.gff" + echo " Write the output records into ." + echo "" + echo " --force_exons" + echo " type: boolean_true" + echo " Make sure that the lowest level GFF features are considered \"exon\"" + echo " features." + echo "" + echo " --gene2exon" + echo " type: boolean_true" + echo " For single-line genes not parenting any transcripts, add an exon feature" + echo " spanning" + echo " the entire gene (treat it as a transcript)." + echo "" + echo " --t_adopt" + echo " type: boolean_true" + echo " Try to find a parent gene overlapping/containing a transcript that does" + echo " not have" + echo " any explicit gene Parent." + echo "" + echo " -D, --decode" + echo " type: boolean_true" + echo " Decode url encoded characters within attributes." + echo "" + echo " -Z, --merge_exons" + echo " type: boolean_true" + echo " Merge very close exons into a single exon (when intron size<4)." + echo "" + echo " -j, --junctions" + echo " type: boolean_true" + echo " Output the junctions and the corresponding transcripts." + echo "" + echo " -w, --spliced_exons" + echo " type: file, output" + echo " example: exons.fa" + echo " Write a fasta file with spliced exons for each transcript." + echo "" + echo " --w_add" + echo " type: integer" + echo " For the --spliced_exons option, extract additional bases both" + echo " upstream and" + echo " downstream of the transcript boundaries." + echo "" + echo " --w_nocds" + echo " type: boolean_true" + echo " For --spliced_exons, disable the output of CDS info in the FASTA file." + echo "" + echo " -x, --spliced_cds" + echo " type: file" + echo " example: cds.fa" + echo " Write a fasta file with spliced CDS for each GFF transcript." + echo "" + echo " -y, --tr_cds" + echo " type: file" + echo " example: tr_cds.fa" + echo " Write a protein fasta file with the translation of CDS for each record." + echo "" + echo " -W, --w_coords" + echo " type: boolean_true" + echo " For --spliced_exons, --spliced_cds and -tr_cds options, write in the" + echo " FASTA defline" + echo " all the exon coordinates projected onto the spliced sequence." + echo "" + echo " -S, --stop_dot" + echo " type: boolean_true" + echo " For --tr_cds option, use '*' instead of '.' as stop codon translation." + echo "" + echo " -L, --id_version" + echo " type: boolean_true" + echo " Ensembl GTF to GFF3 conversion, adds version to IDs." + echo "" + echo " -t, --trackname" + echo " type: string" + echo " Use in the 2nd column of each GFF/GTF output line." + echo "" + echo " -T, --gtf_output" + echo " type: boolean_true" + echo " Main output will be GTF instead of GFF3." + echo "" + echo " --bed" + echo " type: boolean_true" + echo " Output records in BED format instead of default GFF3." + echo "" + echo " --tlf" + echo " type: boolean_true" + echo " Output \"transcript line format\" which is like GFF but with exons and CDS" + echo " related" + echo " features stored as GFF attributes in the transcript feature line, like" + echo " this:" + echo " exoncount=N;exons=;CDSphase=;CDS=" + echo " is a comma-delimited list of exon_start-exon_end coordinates;" + echo " is CDS_start:CDS_end coordinates or a list like ." + echo "" + echo " --table" + echo " type: string, multiple values allowed" + echo " Output a simple tab delimited format instead of GFF, with columns having" + echo " the values" + echo " of GFF attributes given in ; special pseudo-attributes" + echo " (prefixed by @) are" + echo " recognized:" + echo " @id, @geneid, @chr, @start, @end, @strand, @numexons, @exons, @cds," + echo " @covlen, @cdslen" + echo " If any of --spliced_exons/--tr_cds/--spliced_cds FASTA output files are" + echo " enabled, the" + echo " same fields (excluding @id) are appended to the definition line of" + echo " corresponding FASTA" + echo " records." + echo "" + echo " -E, -v, --expose_dups" + echo " type: boolean_true" + echo " Expose (warn about) duplicate transcript IDs and other potential" + echo " problems with the" + echo " given GFF/GTF records." + echo "" + echo "Options:" + echo " --ids" + echo " type: file, file must exist" + echo " Discard records/transcripts if their IDs are not listed in ." + echo "" + echo " --nids" + echo " type: file, file must exist" + echo " Discard records/transcripts if their IDs are listed in ." + echo "" + echo " -i, --maxintron" + echo " type: integer" + echo " Discard transcripts having an intron larger than ." + echo "" + echo " -l, --minlen" + echo " type: integer" + echo " Discard transcripts shorter than bases." + echo "" + echo " -r, --range" + echo " type: string" + echo " Only show transcripts overlapping coordinate range .. (on" + echo " chromosome/contig" + echo " , strand if provided)." + echo "" + echo " -R, --strict_range" + echo " type: boolean_true" + echo " For --range option, discard all transcripts that are not fully contained" + echo " within the given" + echo " range." + echo "" + echo " --jmatch" + echo " type: string" + echo " Only output transcripts matching the given junction." + echo "" + echo " -U, --no_single_exon" + echo " type: boolean_true" + echo " Discard single-exon transcripts." + echo "" + echo " -C, --coding" + echo " type: boolean_true" + echo " Coding only: discard mRNAs that have no CDS features." + echo "" + echo " --nc" + echo " type: boolean_true" + echo " Non-coding only: discard mRNAs that have CDS features." + echo "" + echo " --ignore_locus" + echo " type: boolean_true" + echo " Discard locus features and attributes found in the input." + echo "" + echo " -A, --description" + echo " type: boolean_true" + echo " Use the description field from and add it as the value" + echo " for a 'descr'" + echo " attribute to the GFF record." + echo "" + echo "Sorting:" + echo " --sort_alpha" + echo " type: boolean_true" + echo " Chromosomes (reference sequences) are sorted alphabetically." + echo "" + echo " --sort_by" + echo " type: file, file must exist" + echo " Sort the reference sequences by the order in which their names are given" + echo " in the" + echo " file." + echo "" + echo "Misc options:" + echo " -F, --keep_attrs" + echo " type: boolean_true" + echo " Keep all GFF attributes (for non-exon features)." + echo "" + echo " --keep_exon_attrs" + echo " type: boolean_true" + echo " For -F option, do not attempt to reduce redundant exon/CDS attributes." + echo "" + echo " -G, --no_exon_attrs" + echo " type: boolean_true" + echo " Do not keep exon attributes, move them to the transcript feature (for" + echo " GFF3 output)." + echo "" + echo " --attrs" + echo " type: string" + echo " Only output the GTF/GFF attributes listed in which is a" + echo " comma delimited" + echo " list of attribute names to." + echo "" + echo " --keep_genes" + echo " type: boolean_true" + echo " In transcript-only mode (default), also preserve gene records." + echo "" + echo " --keep_comments" + echo " type: boolean_true" + echo " For GFF3 input/output, try to preserve comments." + echo "" + echo " -O, --process_other" + echo " type: boolean_true" + echo " process other non-transcript GFF records (by default non-transcript" + echo " records are ignored)." + echo "" + echo " -V, --rm_stop_codons" + echo " type: boolean_true" + echo " Discard any mRNAs with CDS having in-frame stop codons (requires" + echo " --genome)." + echo "" + echo " -H, --adj_cds_start" + echo " type: boolean_true" + echo " For --rm_stop_codons option, check and adjust the starting CDS phase if" + echo " the original phase" + echo " leads to a translation with an in-frame stop codon." + echo "" + echo " -B, --opposite_strand" + echo " type: boolean_true" + echo " For -V option, single-exon transcripts are also checked on the opposite" + echo " strand (requires" + echo " --genome)." + echo "" + echo " -P, --coding_status" + echo " type: boolean_true" + echo " Add transcript level GFF attributes about the coding status of each" + echo " transcript, including" + echo " partialness or in-frame stop codons (requires --genome)." + echo "" + echo " --add_hasCDS" + echo " type: boolean_true" + echo " Add a \"hasCDS\" attribute with value \"true\" for transcripts that have CDS" + echo " features." + echo "" + echo " --adj_stop" + echo " type: boolean_true" + echo " Stop codon adjustment: enables --coding_status and performs automatic" + echo " adjustment of the CDS stop" + echo " coordinate if premature or downstream." + echo "" + echo " -N, --rm_noncanon" + echo " type: boolean_true" + echo " Discard multi-exon mRNAs that have any intron with a non-canonical" + echo " splice site consensus" + echo " (i.e. not GT-AG, GC-AG or AT-AC)." + echo "" + echo " -J, --complete_cds" + echo " type: boolean_true" + echo " Discard any mRNAs that either lack initial START codon or the terminal" + echo " STOP codon, or" + echo " have an in-frame stop codon (i.e. only print mRNAs with a complete CDS)." + echo "" + echo " --no_pseudo" + echo " type: boolean_true" + echo " Filter out records matching the 'pseudo' keyword." + echo "" + echo " --in_bed" + echo " type: boolean_true" + echo " Input should be parsed as BED format (automatic if the input filename" + echo " ends with .bed*)." + echo "" + echo " --in_tlf" + echo " type: boolean_true" + echo " Input GFF-like one-line-per-transcript format without exon/CDS features" + echo " (see --tlf option" + echo " below); automatic if the input filename ends with .tlf)." + echo "" + echo " --stream" + echo " type: boolean_true" + echo " Fast processing of input GFF/BED transcripts as they are received (no" + echo " sorting, exons must" + echo " be grouped by transcript in the input data)." + echo "" + echo "Clustering:" + echo " -M, --merge" + echo " type: boolean_true" + echo " Cluster the input transcripts into loci, discarding \"redundant\"" + echo " transcripts (those with" + echo " the same exact introns and fully contained or equal boundaries)." + echo "" + echo " -d, --dupinfo" + echo " type: file, file must exist" + echo " For --merge option, write duplication info to file ." + echo "" + echo " --cluster_only" + echo " type: boolean_true" + echo " Same as --merge but without discarding any of the \"duplicate\"" + echo " transcripts, only create" + echo " \"locus\" features." + echo "" + echo " -K, --rm_redundant" + echo " type: boolean_true" + echo " For --merge option: also discard as redundant the shorter, fully" + echo " contained transcripts (intron" + echo " chains matching a part of the container)." + echo "" + echo " -Q, --no_boundary" + echo " type: boolean_true" + echo " For --merge option, no longer require boundary containment when" + echo " assessing redundancy (can be" + echo " combined with --rm_redundant); only introns have to match for multi-exon" + echo " transcripts, and >=80%" + echo " overlap for single-exon transcripts." + echo "" + echo " -Y, --no_overlap" + echo " type: boolean_true" + echo " For --merge option, enforce --no_boundary but also discard overlapping" + echo " single-exon transcripts," + echo " even on the opposite strand (can be combined with --rm_redudant)." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/gffread:0.12.7--hdcf5f25_3 +ENTRYPOINT [] +RUN echo "gffread: \"$(gffread --version 2>&1)\"" > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component gffread" +LABEL org.opencontainers.image.created="2024-06-24T08:36:41Z" +LABEL org.opencontainers.image.source="https://github.com/gpertea/gffread" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "gffread main" + exit + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chr_mapping) + [ -n "$VIASH_PAR_CHR_MAPPING" ] && ViashError Bad arguments for option \'--chr_mapping\': \'$VIASH_PAR_CHR_MAPPING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHR_MAPPING="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chr_mapping. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chr_mapping=*) + [ -n "$VIASH_PAR_CHR_MAPPING" ] && ViashError Bad arguments for option \'--chr_mapping=*\': \'$VIASH_PAR_CHR_MAPPING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHR_MAPPING=$(ViashRemoveFlags "$1") + shift 1 + ;; + -m) + [ -n "$VIASH_PAR_CHR_MAPPING" ] && ViashError Bad arguments for option \'-m\': \'$VIASH_PAR_CHR_MAPPING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHR_MAPPING="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -m. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --seq_info) + [ -n "$VIASH_PAR_SEQ_INFO" ] && ViashError Bad arguments for option \'--seq_info\': \'$VIASH_PAR_SEQ_INFO\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEQ_INFO="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --seq_info. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --seq_info=*) + [ -n "$VIASH_PAR_SEQ_INFO" ] && ViashError Bad arguments for option \'--seq_info=*\': \'$VIASH_PAR_SEQ_INFO\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEQ_INFO=$(ViashRemoveFlags "$1") + shift 1 + ;; + -s) + [ -n "$VIASH_PAR_SEQ_INFO" ] && ViashError Bad arguments for option \'-s\': \'$VIASH_PAR_SEQ_INFO\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEQ_INFO="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -s. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genome) + [ -n "$VIASH_PAR_GENOME" ] && ViashError Bad arguments for option \'--genome\': \'$VIASH_PAR_GENOME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genome. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genome=*) + [ -n "$VIASH_PAR_GENOME" ] && ViashError Bad arguments for option \'--genome=*\': \'$VIASH_PAR_GENOME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOME=$(ViashRemoveFlags "$1") + shift 1 + ;; + -g) + [ -n "$VIASH_PAR_GENOME" ] && ViashError Bad arguments for option \'-g\': \'$VIASH_PAR_GENOME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -g. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outfile) + [ -n "$VIASH_PAR_OUTFILE" ] && ViashError Bad arguments for option \'--outfile\': \'$VIASH_PAR_OUTFILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outfile. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outfile=*) + [ -n "$VIASH_PAR_OUTFILE" ] && ViashError Bad arguments for option \'--outfile=*\': \'$VIASH_PAR_OUTFILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTFILE" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTFILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --force_exons) + [ -n "$VIASH_PAR_FORCE_EXONS" ] && ViashError Bad arguments for option \'--force_exons\': \'$VIASH_PAR_FORCE_EXONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORCE_EXONS=true + shift 1 + ;; + --gene2exon) + [ -n "$VIASH_PAR_GENE2EXON" ] && ViashError Bad arguments for option \'--gene2exon\': \'$VIASH_PAR_GENE2EXON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENE2EXON=true + shift 1 + ;; + --t_adopt) + [ -n "$VIASH_PAR_T_ADOPT" ] && ViashError Bad arguments for option \'--t_adopt\': \'$VIASH_PAR_T_ADOPT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_T_ADOPT=true + shift 1 + ;; + --decode) + [ -n "$VIASH_PAR_DECODE" ] && ViashError Bad arguments for option \'--decode\': \'$VIASH_PAR_DECODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DECODE=true + shift 1 + ;; + -D) + [ -n "$VIASH_PAR_DECODE" ] && ViashError Bad arguments for option \'-D\': \'$VIASH_PAR_DECODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DECODE=true + shift 1 + ;; + --merge_exons) + [ -n "$VIASH_PAR_MERGE_EXONS" ] && ViashError Bad arguments for option \'--merge_exons\': \'$VIASH_PAR_MERGE_EXONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MERGE_EXONS=true + shift 1 + ;; + -Z) + [ -n "$VIASH_PAR_MERGE_EXONS" ] && ViashError Bad arguments for option \'-Z\': \'$VIASH_PAR_MERGE_EXONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MERGE_EXONS=true + shift 1 + ;; + --junctions) + [ -n "$VIASH_PAR_JUNCTIONS" ] && ViashError Bad arguments for option \'--junctions\': \'$VIASH_PAR_JUNCTIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_JUNCTIONS=true + shift 1 + ;; + -j) + [ -n "$VIASH_PAR_JUNCTIONS" ] && ViashError Bad arguments for option \'-j\': \'$VIASH_PAR_JUNCTIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_JUNCTIONS=true + shift 1 + ;; + --spliced_exons) + [ -n "$VIASH_PAR_SPLICED_EXONS" ] && ViashError Bad arguments for option \'--spliced_exons\': \'$VIASH_PAR_SPLICED_EXONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLICED_EXONS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --spliced_exons. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --spliced_exons=*) + [ -n "$VIASH_PAR_SPLICED_EXONS" ] && ViashError Bad arguments for option \'--spliced_exons=*\': \'$VIASH_PAR_SPLICED_EXONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLICED_EXONS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -w) + [ -n "$VIASH_PAR_SPLICED_EXONS" ] && ViashError Bad arguments for option \'-w\': \'$VIASH_PAR_SPLICED_EXONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLICED_EXONS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -w. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --w_add) + [ -n "$VIASH_PAR_W_ADD" ] && ViashError Bad arguments for option \'--w_add\': \'$VIASH_PAR_W_ADD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_W_ADD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --w_add. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --w_add=*) + [ -n "$VIASH_PAR_W_ADD" ] && ViashError Bad arguments for option \'--w_add=*\': \'$VIASH_PAR_W_ADD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_W_ADD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --w_nocds) + [ -n "$VIASH_PAR_W_NOCDS" ] && ViashError Bad arguments for option \'--w_nocds\': \'$VIASH_PAR_W_NOCDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_W_NOCDS=true + shift 1 + ;; + --spliced_cds) + [ -n "$VIASH_PAR_SPLICED_CDS" ] && ViashError Bad arguments for option \'--spliced_cds\': \'$VIASH_PAR_SPLICED_CDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLICED_CDS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --spliced_cds. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --spliced_cds=*) + [ -n "$VIASH_PAR_SPLICED_CDS" ] && ViashError Bad arguments for option \'--spliced_cds=*\': \'$VIASH_PAR_SPLICED_CDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLICED_CDS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -x) + [ -n "$VIASH_PAR_SPLICED_CDS" ] && ViashError Bad arguments for option \'-x\': \'$VIASH_PAR_SPLICED_CDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLICED_CDS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -x. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tr_cds) + [ -n "$VIASH_PAR_TR_CDS" ] && ViashError Bad arguments for option \'--tr_cds\': \'$VIASH_PAR_TR_CDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TR_CDS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --tr_cds. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tr_cds=*) + [ -n "$VIASH_PAR_TR_CDS" ] && ViashError Bad arguments for option \'--tr_cds=*\': \'$VIASH_PAR_TR_CDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TR_CDS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -y) + [ -n "$VIASH_PAR_TR_CDS" ] && ViashError Bad arguments for option \'-y\': \'$VIASH_PAR_TR_CDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TR_CDS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -y. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --w_coords) + [ -n "$VIASH_PAR_W_COORDS" ] && ViashError Bad arguments for option \'--w_coords\': \'$VIASH_PAR_W_COORDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_W_COORDS=true + shift 1 + ;; + -W) + [ -n "$VIASH_PAR_W_COORDS" ] && ViashError Bad arguments for option \'-W\': \'$VIASH_PAR_W_COORDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_W_COORDS=true + shift 1 + ;; + --stop_dot) + [ -n "$VIASH_PAR_STOP_DOT" ] && ViashError Bad arguments for option \'--stop_dot\': \'$VIASH_PAR_STOP_DOT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STOP_DOT=true + shift 1 + ;; + -S) + [ -n "$VIASH_PAR_STOP_DOT" ] && ViashError Bad arguments for option \'-S\': \'$VIASH_PAR_STOP_DOT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STOP_DOT=true + shift 1 + ;; + --id_version) + [ -n "$VIASH_PAR_ID_VERSION" ] && ViashError Bad arguments for option \'--id_version\': \'$VIASH_PAR_ID_VERSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ID_VERSION=true + shift 1 + ;; + -L) + [ -n "$VIASH_PAR_ID_VERSION" ] && ViashError Bad arguments for option \'-L\': \'$VIASH_PAR_ID_VERSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ID_VERSION=true + shift 1 + ;; + --trackname) + [ -n "$VIASH_PAR_TRACKNAME" ] && ViashError Bad arguments for option \'--trackname\': \'$VIASH_PAR_TRACKNAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRACKNAME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --trackname. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --trackname=*) + [ -n "$VIASH_PAR_TRACKNAME" ] && ViashError Bad arguments for option \'--trackname=*\': \'$VIASH_PAR_TRACKNAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRACKNAME=$(ViashRemoveFlags "$1") + shift 1 + ;; + -t) + [ -n "$VIASH_PAR_TRACKNAME" ] && ViashError Bad arguments for option \'-t\': \'$VIASH_PAR_TRACKNAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRACKNAME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -t. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --gtf_output) + [ -n "$VIASH_PAR_GTF_OUTPUT" ] && ViashError Bad arguments for option \'--gtf_output\': \'$VIASH_PAR_GTF_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GTF_OUTPUT=true + shift 1 + ;; + -T) + [ -n "$VIASH_PAR_GTF_OUTPUT" ] && ViashError Bad arguments for option \'-T\': \'$VIASH_PAR_GTF_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GTF_OUTPUT=true + shift 1 + ;; + --bed) + [ -n "$VIASH_PAR_BED" ] && ViashError Bad arguments for option \'--bed\': \'$VIASH_PAR_BED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BED=true + shift 1 + ;; + --tlf) + [ -n "$VIASH_PAR_TLF" ] && ViashError Bad arguments for option \'--tlf\': \'$VIASH_PAR_TLF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TLF=true + shift 1 + ;; + --table) + if [ -z "$VIASH_PAR_TABLE" ]; then + VIASH_PAR_TABLE="$2" + else + VIASH_PAR_TABLE="$VIASH_PAR_TABLE,""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --table. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --table=*) + if [ -z "$VIASH_PAR_TABLE" ]; then + VIASH_PAR_TABLE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_TABLE="$VIASH_PAR_TABLE,"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --expose_dups) + [ -n "$VIASH_PAR_EXPOSE_DUPS" ] && ViashError Bad arguments for option \'--expose_dups\': \'$VIASH_PAR_EXPOSE_DUPS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXPOSE_DUPS=true + shift 1 + ;; + -E) + [ -n "$VIASH_PAR_EXPOSE_DUPS" ] && ViashError Bad arguments for option \'-E\': \'$VIASH_PAR_EXPOSE_DUPS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXPOSE_DUPS=true + shift 1 + ;; + -v) + [ -n "$VIASH_PAR_EXPOSE_DUPS" ] && ViashError Bad arguments for option \'-v\': \'$VIASH_PAR_EXPOSE_DUPS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXPOSE_DUPS=true + shift 1 + ;; + --ids) + [ -n "$VIASH_PAR_IDS" ] && ViashError Bad arguments for option \'--ids\': \'$VIASH_PAR_IDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IDS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --ids. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ids=*) + [ -n "$VIASH_PAR_IDS" ] && ViashError Bad arguments for option \'--ids=*\': \'$VIASH_PAR_IDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IDS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --nids) + [ -n "$VIASH_PAR_NIDS" ] && ViashError Bad arguments for option \'--nids\': \'$VIASH_PAR_NIDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NIDS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --nids. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --nids=*) + [ -n "$VIASH_PAR_NIDS" ] && ViashError Bad arguments for option \'--nids=*\': \'$VIASH_PAR_NIDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NIDS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --maxintron) + [ -n "$VIASH_PAR_MAXINTRON" ] && ViashError Bad arguments for option \'--maxintron\': \'$VIASH_PAR_MAXINTRON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAXINTRON="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --maxintron. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --maxintron=*) + [ -n "$VIASH_PAR_MAXINTRON" ] && ViashError Bad arguments for option \'--maxintron=*\': \'$VIASH_PAR_MAXINTRON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAXINTRON=$(ViashRemoveFlags "$1") + shift 1 + ;; + -i) + [ -n "$VIASH_PAR_MAXINTRON" ] && ViashError Bad arguments for option \'-i\': \'$VIASH_PAR_MAXINTRON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAXINTRON="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -i. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --minlen) + [ -n "$VIASH_PAR_MINLEN" ] && ViashError Bad arguments for option \'--minlen\': \'$VIASH_PAR_MINLEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MINLEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --minlen. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --minlen=*) + [ -n "$VIASH_PAR_MINLEN" ] && ViashError Bad arguments for option \'--minlen=*\': \'$VIASH_PAR_MINLEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MINLEN=$(ViashRemoveFlags "$1") + shift 1 + ;; + -l) + [ -n "$VIASH_PAR_MINLEN" ] && ViashError Bad arguments for option \'-l\': \'$VIASH_PAR_MINLEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MINLEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -l. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --range) + [ -n "$VIASH_PAR_RANGE" ] && ViashError Bad arguments for option \'--range\': \'$VIASH_PAR_RANGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RANGE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --range. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --range=*) + [ -n "$VIASH_PAR_RANGE" ] && ViashError Bad arguments for option \'--range=*\': \'$VIASH_PAR_RANGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RANGE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -r) + [ -n "$VIASH_PAR_RANGE" ] && ViashError Bad arguments for option \'-r\': \'$VIASH_PAR_RANGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RANGE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -r. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --strict_range) + [ -n "$VIASH_PAR_STRICT_RANGE" ] && ViashError Bad arguments for option \'--strict_range\': \'$VIASH_PAR_STRICT_RANGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRICT_RANGE=true + shift 1 + ;; + -R) + [ -n "$VIASH_PAR_STRICT_RANGE" ] && ViashError Bad arguments for option \'-R\': \'$VIASH_PAR_STRICT_RANGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRICT_RANGE=true + shift 1 + ;; + --jmatch) + [ -n "$VIASH_PAR_JMATCH" ] && ViashError Bad arguments for option \'--jmatch\': \'$VIASH_PAR_JMATCH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_JMATCH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --jmatch. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --jmatch=*) + [ -n "$VIASH_PAR_JMATCH" ] && ViashError Bad arguments for option \'--jmatch=*\': \'$VIASH_PAR_JMATCH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_JMATCH=$(ViashRemoveFlags "$1") + shift 1 + ;; + --no_single_exon) + [ -n "$VIASH_PAR_NO_SINGLE_EXON" ] && ViashError Bad arguments for option \'--no_single_exon\': \'$VIASH_PAR_NO_SINGLE_EXON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_SINGLE_EXON=true + shift 1 + ;; + -U) + [ -n "$VIASH_PAR_NO_SINGLE_EXON" ] && ViashError Bad arguments for option \'-U\': \'$VIASH_PAR_NO_SINGLE_EXON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_SINGLE_EXON=true + shift 1 + ;; + --coding) + [ -n "$VIASH_PAR_CODING" ] && ViashError Bad arguments for option \'--coding\': \'$VIASH_PAR_CODING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CODING=true + shift 1 + ;; + -C) + [ -n "$VIASH_PAR_CODING" ] && ViashError Bad arguments for option \'-C\': \'$VIASH_PAR_CODING\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CODING=true + shift 1 + ;; + --nc) + [ -n "$VIASH_PAR_NC" ] && ViashError Bad arguments for option \'--nc\': \'$VIASH_PAR_NC\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NC=true + shift 1 + ;; + --ignore_locus) + [ -n "$VIASH_PAR_IGNORE_LOCUS" ] && ViashError Bad arguments for option \'--ignore_locus\': \'$VIASH_PAR_IGNORE_LOCUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IGNORE_LOCUS=true + shift 1 + ;; + --description) + [ -n "$VIASH_PAR_DESCRIPTION" ] && ViashError Bad arguments for option \'--description\': \'$VIASH_PAR_DESCRIPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DESCRIPTION=true + shift 1 + ;; + -A) + [ -n "$VIASH_PAR_DESCRIPTION" ] && ViashError Bad arguments for option \'-A\': \'$VIASH_PAR_DESCRIPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DESCRIPTION=true + shift 1 + ;; + --sort_alpha) + [ -n "$VIASH_PAR_SORT_ALPHA" ] && ViashError Bad arguments for option \'--sort_alpha\': \'$VIASH_PAR_SORT_ALPHA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SORT_ALPHA=true + shift 1 + ;; + --sort_by) + [ -n "$VIASH_PAR_SORT_BY" ] && ViashError Bad arguments for option \'--sort_by\': \'$VIASH_PAR_SORT_BY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SORT_BY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sort_by. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sort_by=*) + [ -n "$VIASH_PAR_SORT_BY" ] && ViashError Bad arguments for option \'--sort_by=*\': \'$VIASH_PAR_SORT_BY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SORT_BY=$(ViashRemoveFlags "$1") + shift 1 + ;; + --keep_attrs) + [ -n "$VIASH_PAR_KEEP_ATTRS" ] && ViashError Bad arguments for option \'--keep_attrs\': \'$VIASH_PAR_KEEP_ATTRS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KEEP_ATTRS=true + shift 1 + ;; + -F) + [ -n "$VIASH_PAR_KEEP_ATTRS" ] && ViashError Bad arguments for option \'-F\': \'$VIASH_PAR_KEEP_ATTRS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KEEP_ATTRS=true + shift 1 + ;; + --keep_exon_attrs) + [ -n "$VIASH_PAR_KEEP_EXON_ATTRS" ] && ViashError Bad arguments for option \'--keep_exon_attrs\': \'$VIASH_PAR_KEEP_EXON_ATTRS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KEEP_EXON_ATTRS=true + shift 1 + ;; + --no_exon_attrs) + [ -n "$VIASH_PAR_NO_EXON_ATTRS" ] && ViashError Bad arguments for option \'--no_exon_attrs\': \'$VIASH_PAR_NO_EXON_ATTRS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_EXON_ATTRS=true + shift 1 + ;; + -G) + [ -n "$VIASH_PAR_NO_EXON_ATTRS" ] && ViashError Bad arguments for option \'-G\': \'$VIASH_PAR_NO_EXON_ATTRS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_EXON_ATTRS=true + shift 1 + ;; + --attrs) + [ -n "$VIASH_PAR_ATTRS" ] && ViashError Bad arguments for option \'--attrs\': \'$VIASH_PAR_ATTRS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ATTRS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --attrs. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --attrs=*) + [ -n "$VIASH_PAR_ATTRS" ] && ViashError Bad arguments for option \'--attrs=*\': \'$VIASH_PAR_ATTRS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ATTRS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --keep_genes) + [ -n "$VIASH_PAR_KEEP_GENES" ] && ViashError Bad arguments for option \'--keep_genes\': \'$VIASH_PAR_KEEP_GENES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KEEP_GENES=true + shift 1 + ;; + --keep_comments) + [ -n "$VIASH_PAR_KEEP_COMMENTS" ] && ViashError Bad arguments for option \'--keep_comments\': \'$VIASH_PAR_KEEP_COMMENTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KEEP_COMMENTS=true + shift 1 + ;; + --process_other) + [ -n "$VIASH_PAR_PROCESS_OTHER" ] && ViashError Bad arguments for option \'--process_other\': \'$VIASH_PAR_PROCESS_OTHER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PROCESS_OTHER=true + shift 1 + ;; + -O) + [ -n "$VIASH_PAR_PROCESS_OTHER" ] && ViashError Bad arguments for option \'-O\': \'$VIASH_PAR_PROCESS_OTHER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PROCESS_OTHER=true + shift 1 + ;; + --rm_stop_codons) + [ -n "$VIASH_PAR_RM_STOP_CODONS" ] && ViashError Bad arguments for option \'--rm_stop_codons\': \'$VIASH_PAR_RM_STOP_CODONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RM_STOP_CODONS=true + shift 1 + ;; + -V) + [ -n "$VIASH_PAR_RM_STOP_CODONS" ] && ViashError Bad arguments for option \'-V\': \'$VIASH_PAR_RM_STOP_CODONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RM_STOP_CODONS=true + shift 1 + ;; + --adj_cds_start) + [ -n "$VIASH_PAR_ADJ_CDS_START" ] && ViashError Bad arguments for option \'--adj_cds_start\': \'$VIASH_PAR_ADJ_CDS_START\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADJ_CDS_START=true + shift 1 + ;; + -H) + [ -n "$VIASH_PAR_ADJ_CDS_START" ] && ViashError Bad arguments for option \'-H\': \'$VIASH_PAR_ADJ_CDS_START\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADJ_CDS_START=true + shift 1 + ;; + --opposite_strand) + [ -n "$VIASH_PAR_OPPOSITE_STRAND" ] && ViashError Bad arguments for option \'--opposite_strand\': \'$VIASH_PAR_OPPOSITE_STRAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OPPOSITE_STRAND=true + shift 1 + ;; + -B) + [ -n "$VIASH_PAR_OPPOSITE_STRAND" ] && ViashError Bad arguments for option \'-B\': \'$VIASH_PAR_OPPOSITE_STRAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OPPOSITE_STRAND=true + shift 1 + ;; + --coding_status) + [ -n "$VIASH_PAR_CODING_STATUS" ] && ViashError Bad arguments for option \'--coding_status\': \'$VIASH_PAR_CODING_STATUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CODING_STATUS=true + shift 1 + ;; + -P) + [ -n "$VIASH_PAR_CODING_STATUS" ] && ViashError Bad arguments for option \'-P\': \'$VIASH_PAR_CODING_STATUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CODING_STATUS=true + shift 1 + ;; + --add_hasCDS) + [ -n "$VIASH_PAR_ADD_HASCDS" ] && ViashError Bad arguments for option \'--add_hasCDS\': \'$VIASH_PAR_ADD_HASCDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADD_HASCDS=true + shift 1 + ;; + --adj_stop) + [ -n "$VIASH_PAR_ADJ_STOP" ] && ViashError Bad arguments for option \'--adj_stop\': \'$VIASH_PAR_ADJ_STOP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADJ_STOP=true + shift 1 + ;; + --rm_noncanon) + [ -n "$VIASH_PAR_RM_NONCANON" ] && ViashError Bad arguments for option \'--rm_noncanon\': \'$VIASH_PAR_RM_NONCANON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RM_NONCANON=true + shift 1 + ;; + -N) + [ -n "$VIASH_PAR_RM_NONCANON" ] && ViashError Bad arguments for option \'-N\': \'$VIASH_PAR_RM_NONCANON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RM_NONCANON=true + shift 1 + ;; + --complete_cds) + [ -n "$VIASH_PAR_COMPLETE_CDS" ] && ViashError Bad arguments for option \'--complete_cds\': \'$VIASH_PAR_COMPLETE_CDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPLETE_CDS=true + shift 1 + ;; + -J) + [ -n "$VIASH_PAR_COMPLETE_CDS" ] && ViashError Bad arguments for option \'-J\': \'$VIASH_PAR_COMPLETE_CDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPLETE_CDS=true + shift 1 + ;; + --no_pseudo) + [ -n "$VIASH_PAR_NO_PSEUDO" ] && ViashError Bad arguments for option \'--no_pseudo\': \'$VIASH_PAR_NO_PSEUDO\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_PSEUDO=true + shift 1 + ;; + --in_bed) + [ -n "$VIASH_PAR_IN_BED" ] && ViashError Bad arguments for option \'--in_bed\': \'$VIASH_PAR_IN_BED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IN_BED=true + shift 1 + ;; + --in_tlf) + [ -n "$VIASH_PAR_IN_TLF" ] && ViashError Bad arguments for option \'--in_tlf\': \'$VIASH_PAR_IN_TLF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IN_TLF=true + shift 1 + ;; + --stream) + [ -n "$VIASH_PAR_STREAM" ] && ViashError Bad arguments for option \'--stream\': \'$VIASH_PAR_STREAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STREAM=true + shift 1 + ;; + --merge) + [ -n "$VIASH_PAR_MERGE" ] && ViashError Bad arguments for option \'--merge\': \'$VIASH_PAR_MERGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MERGE=true + shift 1 + ;; + -M) + [ -n "$VIASH_PAR_MERGE" ] && ViashError Bad arguments for option \'-M\': \'$VIASH_PAR_MERGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MERGE=true + shift 1 + ;; + --dupinfo) + [ -n "$VIASH_PAR_DUPINFO" ] && ViashError Bad arguments for option \'--dupinfo\': \'$VIASH_PAR_DUPINFO\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DUPINFO="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --dupinfo. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --dupinfo=*) + [ -n "$VIASH_PAR_DUPINFO" ] && ViashError Bad arguments for option \'--dupinfo=*\': \'$VIASH_PAR_DUPINFO\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DUPINFO=$(ViashRemoveFlags "$1") + shift 1 + ;; + -d) + [ -n "$VIASH_PAR_DUPINFO" ] && ViashError Bad arguments for option \'-d\': \'$VIASH_PAR_DUPINFO\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DUPINFO="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -d. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cluster_only) + [ -n "$VIASH_PAR_CLUSTER_ONLY" ] && ViashError Bad arguments for option \'--cluster_only\': \'$VIASH_PAR_CLUSTER_ONLY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CLUSTER_ONLY=true + shift 1 + ;; + --rm_redundant) + [ -n "$VIASH_PAR_RM_REDUNDANT" ] && ViashError Bad arguments for option \'--rm_redundant\': \'$VIASH_PAR_RM_REDUNDANT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RM_REDUNDANT=true + shift 1 + ;; + -K) + [ -n "$VIASH_PAR_RM_REDUNDANT" ] && ViashError Bad arguments for option \'-K\': \'$VIASH_PAR_RM_REDUNDANT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RM_REDUNDANT=true + shift 1 + ;; + --no_boundary) + [ -n "$VIASH_PAR_NO_BOUNDARY" ] && ViashError Bad arguments for option \'--no_boundary\': \'$VIASH_PAR_NO_BOUNDARY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_BOUNDARY=true + shift 1 + ;; + -Q) + [ -n "$VIASH_PAR_NO_BOUNDARY" ] && ViashError Bad arguments for option \'-Q\': \'$VIASH_PAR_NO_BOUNDARY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_BOUNDARY=true + shift 1 + ;; + --no_overlap) + [ -n "$VIASH_PAR_NO_OVERLAP" ] && ViashError Bad arguments for option \'--no_overlap\': \'$VIASH_PAR_NO_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_OVERLAP=true + shift 1 + ;; + -Y) + [ -n "$VIASH_PAR_NO_OVERLAP" ] && ViashError Bad arguments for option \'-Y\': \'$VIASH_PAR_NO_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_OVERLAP=true + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/gffread:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUTFILE+x} ]; then + ViashError '--outfile' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_FORCE_EXONS+x} ]; then + VIASH_PAR_FORCE_EXONS="false" +fi +if [ -z ${VIASH_PAR_GENE2EXON+x} ]; then + VIASH_PAR_GENE2EXON="false" +fi +if [ -z ${VIASH_PAR_T_ADOPT+x} ]; then + VIASH_PAR_T_ADOPT="false" +fi +if [ -z ${VIASH_PAR_DECODE+x} ]; then + VIASH_PAR_DECODE="false" +fi +if [ -z ${VIASH_PAR_MERGE_EXONS+x} ]; then + VIASH_PAR_MERGE_EXONS="false" +fi +if [ -z ${VIASH_PAR_JUNCTIONS+x} ]; then + VIASH_PAR_JUNCTIONS="false" +fi +if [ -z ${VIASH_PAR_W_NOCDS+x} ]; then + VIASH_PAR_W_NOCDS="false" +fi +if [ -z ${VIASH_PAR_W_COORDS+x} ]; then + VIASH_PAR_W_COORDS="false" +fi +if [ -z ${VIASH_PAR_STOP_DOT+x} ]; then + VIASH_PAR_STOP_DOT="false" +fi +if [ -z ${VIASH_PAR_ID_VERSION+x} ]; then + VIASH_PAR_ID_VERSION="false" +fi +if [ -z ${VIASH_PAR_GTF_OUTPUT+x} ]; then + VIASH_PAR_GTF_OUTPUT="false" +fi +if [ -z ${VIASH_PAR_BED+x} ]; then + VIASH_PAR_BED="false" +fi +if [ -z ${VIASH_PAR_TLF+x} ]; then + VIASH_PAR_TLF="false" +fi +if [ -z ${VIASH_PAR_EXPOSE_DUPS+x} ]; then + VIASH_PAR_EXPOSE_DUPS="false" +fi +if [ -z ${VIASH_PAR_STRICT_RANGE+x} ]; then + VIASH_PAR_STRICT_RANGE="false" +fi +if [ -z ${VIASH_PAR_NO_SINGLE_EXON+x} ]; then + VIASH_PAR_NO_SINGLE_EXON="false" +fi +if [ -z ${VIASH_PAR_CODING+x} ]; then + VIASH_PAR_CODING="false" +fi +if [ -z ${VIASH_PAR_NC+x} ]; then + VIASH_PAR_NC="false" +fi +if [ -z ${VIASH_PAR_IGNORE_LOCUS+x} ]; then + VIASH_PAR_IGNORE_LOCUS="false" +fi +if [ -z ${VIASH_PAR_DESCRIPTION+x} ]; then + VIASH_PAR_DESCRIPTION="false" +fi +if [ -z ${VIASH_PAR_SORT_ALPHA+x} ]; then + VIASH_PAR_SORT_ALPHA="false" +fi +if [ -z ${VIASH_PAR_KEEP_ATTRS+x} ]; then + VIASH_PAR_KEEP_ATTRS="false" +fi +if [ -z ${VIASH_PAR_KEEP_EXON_ATTRS+x} ]; then + VIASH_PAR_KEEP_EXON_ATTRS="false" +fi +if [ -z ${VIASH_PAR_NO_EXON_ATTRS+x} ]; then + VIASH_PAR_NO_EXON_ATTRS="false" +fi +if [ -z ${VIASH_PAR_KEEP_GENES+x} ]; then + VIASH_PAR_KEEP_GENES="false" +fi +if [ -z ${VIASH_PAR_KEEP_COMMENTS+x} ]; then + VIASH_PAR_KEEP_COMMENTS="false" +fi +if [ -z ${VIASH_PAR_PROCESS_OTHER+x} ]; then + VIASH_PAR_PROCESS_OTHER="false" +fi +if [ -z ${VIASH_PAR_RM_STOP_CODONS+x} ]; then + VIASH_PAR_RM_STOP_CODONS="false" +fi +if [ -z ${VIASH_PAR_ADJ_CDS_START+x} ]; then + VIASH_PAR_ADJ_CDS_START="false" +fi +if [ -z ${VIASH_PAR_OPPOSITE_STRAND+x} ]; then + VIASH_PAR_OPPOSITE_STRAND="false" +fi +if [ -z ${VIASH_PAR_CODING_STATUS+x} ]; then + VIASH_PAR_CODING_STATUS="false" +fi +if [ -z ${VIASH_PAR_ADD_HASCDS+x} ]; then + VIASH_PAR_ADD_HASCDS="false" +fi +if [ -z ${VIASH_PAR_ADJ_STOP+x} ]; then + VIASH_PAR_ADJ_STOP="false" +fi +if [ -z ${VIASH_PAR_RM_NONCANON+x} ]; then + VIASH_PAR_RM_NONCANON="false" +fi +if [ -z ${VIASH_PAR_COMPLETE_CDS+x} ]; then + VIASH_PAR_COMPLETE_CDS="false" +fi +if [ -z ${VIASH_PAR_NO_PSEUDO+x} ]; then + VIASH_PAR_NO_PSEUDO="false" +fi +if [ -z ${VIASH_PAR_IN_BED+x} ]; then + VIASH_PAR_IN_BED="false" +fi +if [ -z ${VIASH_PAR_IN_TLF+x} ]; then + VIASH_PAR_IN_TLF="false" +fi +if [ -z ${VIASH_PAR_STREAM+x} ]; then + VIASH_PAR_STREAM="false" +fi +if [ -z ${VIASH_PAR_MERGE+x} ]; then + VIASH_PAR_MERGE="false" +fi +if [ -z ${VIASH_PAR_CLUSTER_ONLY+x} ]; then + VIASH_PAR_CLUSTER_ONLY="false" +fi +if [ -z ${VIASH_PAR_RM_REDUNDANT+x} ]; then + VIASH_PAR_RM_REDUNDANT="false" +fi +if [ -z ${VIASH_PAR_NO_BOUNDARY+x} ]; then + VIASH_PAR_NO_BOUNDARY="false" +fi +if [ -z ${VIASH_PAR_NO_OVERLAP+x} ]; then + VIASH_PAR_NO_OVERLAP="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_CHR_MAPPING" ] && [ ! -e "$VIASH_PAR_CHR_MAPPING" ]; then + ViashError "Input file '$VIASH_PAR_CHR_MAPPING' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_SEQ_INFO" ] && [ ! -e "$VIASH_PAR_SEQ_INFO" ]; then + ViashError "Input file '$VIASH_PAR_SEQ_INFO' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_GENOME" ] && [ ! -e "$VIASH_PAR_GENOME" ]; then + ViashError "Input file '$VIASH_PAR_GENOME' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_IDS" ] && [ ! -e "$VIASH_PAR_IDS" ]; then + ViashError "Input file '$VIASH_PAR_IDS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_NIDS" ] && [ ! -e "$VIASH_PAR_NIDS" ]; then + ViashError "Input file '$VIASH_PAR_NIDS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_SORT_BY" ] && [ ! -e "$VIASH_PAR_SORT_BY" ]; then + ViashError "Input file '$VIASH_PAR_SORT_BY' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_DUPINFO" ] && [ ! -e "$VIASH_PAR_DUPINFO" ]; then + ViashError "Input file '$VIASH_PAR_DUPINFO' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_FORCE_EXONS" ]]; then + if ! [[ "$VIASH_PAR_FORCE_EXONS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--force_exons' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GENE2EXON" ]]; then + if ! [[ "$VIASH_PAR_GENE2EXON" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--gene2exon' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_T_ADOPT" ]]; then + if ! [[ "$VIASH_PAR_T_ADOPT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--t_adopt' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DECODE" ]]; then + if ! [[ "$VIASH_PAR_DECODE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--decode' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MERGE_EXONS" ]]; then + if ! [[ "$VIASH_PAR_MERGE_EXONS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--merge_exons' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_JUNCTIONS" ]]; then + if ! [[ "$VIASH_PAR_JUNCTIONS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--junctions' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_W_ADD" ]]; then + if ! [[ "$VIASH_PAR_W_ADD" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--w_add' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_W_NOCDS" ]]; then + if ! [[ "$VIASH_PAR_W_NOCDS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--w_nocds' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_W_COORDS" ]]; then + if ! [[ "$VIASH_PAR_W_COORDS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--w_coords' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_STOP_DOT" ]]; then + if ! [[ "$VIASH_PAR_STOP_DOT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--stop_dot' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ID_VERSION" ]]; then + if ! [[ "$VIASH_PAR_ID_VERSION" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--id_version' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GTF_OUTPUT" ]]; then + if ! [[ "$VIASH_PAR_GTF_OUTPUT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--gtf_output' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BED" ]]; then + if ! [[ "$VIASH_PAR_BED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--bed' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TLF" ]]; then + if ! [[ "$VIASH_PAR_TLF" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--tlf' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_EXPOSE_DUPS" ]]; then + if ! [[ "$VIASH_PAR_EXPOSE_DUPS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--expose_dups' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAXINTRON" ]]; then + if ! [[ "$VIASH_PAR_MAXINTRON" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--maxintron' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MINLEN" ]]; then + if ! [[ "$VIASH_PAR_MINLEN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--minlen' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_STRICT_RANGE" ]]; then + if ! [[ "$VIASH_PAR_STRICT_RANGE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--strict_range' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_SINGLE_EXON" ]]; then + if ! [[ "$VIASH_PAR_NO_SINGLE_EXON" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_single_exon' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CODING" ]]; then + if ! [[ "$VIASH_PAR_CODING" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--coding' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NC" ]]; then + if ! [[ "$VIASH_PAR_NC" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--nc' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_IGNORE_LOCUS" ]]; then + if ! [[ "$VIASH_PAR_IGNORE_LOCUS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--ignore_locus' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DESCRIPTION" ]]; then + if ! [[ "$VIASH_PAR_DESCRIPTION" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--description' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SORT_ALPHA" ]]; then + if ! [[ "$VIASH_PAR_SORT_ALPHA" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--sort_alpha' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_KEEP_ATTRS" ]]; then + if ! [[ "$VIASH_PAR_KEEP_ATTRS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--keep_attrs' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_KEEP_EXON_ATTRS" ]]; then + if ! [[ "$VIASH_PAR_KEEP_EXON_ATTRS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--keep_exon_attrs' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_EXON_ATTRS" ]]; then + if ! [[ "$VIASH_PAR_NO_EXON_ATTRS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_exon_attrs' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_KEEP_GENES" ]]; then + if ! [[ "$VIASH_PAR_KEEP_GENES" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--keep_genes' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_KEEP_COMMENTS" ]]; then + if ! [[ "$VIASH_PAR_KEEP_COMMENTS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--keep_comments' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PROCESS_OTHER" ]]; then + if ! [[ "$VIASH_PAR_PROCESS_OTHER" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--process_other' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_RM_STOP_CODONS" ]]; then + if ! [[ "$VIASH_PAR_RM_STOP_CODONS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--rm_stop_codons' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ADJ_CDS_START" ]]; then + if ! [[ "$VIASH_PAR_ADJ_CDS_START" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--adj_cds_start' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OPPOSITE_STRAND" ]]; then + if ! [[ "$VIASH_PAR_OPPOSITE_STRAND" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--opposite_strand' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CODING_STATUS" ]]; then + if ! [[ "$VIASH_PAR_CODING_STATUS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--coding_status' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ADD_HASCDS" ]]; then + if ! [[ "$VIASH_PAR_ADD_HASCDS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--add_hasCDS' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ADJ_STOP" ]]; then + if ! [[ "$VIASH_PAR_ADJ_STOP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--adj_stop' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_RM_NONCANON" ]]; then + if ! [[ "$VIASH_PAR_RM_NONCANON" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--rm_noncanon' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_COMPLETE_CDS" ]]; then + if ! [[ "$VIASH_PAR_COMPLETE_CDS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--complete_cds' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_PSEUDO" ]]; then + if ! [[ "$VIASH_PAR_NO_PSEUDO" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_pseudo' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_IN_BED" ]]; then + if ! [[ "$VIASH_PAR_IN_BED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--in_bed' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_IN_TLF" ]]; then + if ! [[ "$VIASH_PAR_IN_TLF" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--in_tlf' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_STREAM" ]]; then + if ! [[ "$VIASH_PAR_STREAM" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--stream' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MERGE" ]]; then + if ! [[ "$VIASH_PAR_MERGE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--merge' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CLUSTER_ONLY" ]]; then + if ! [[ "$VIASH_PAR_CLUSTER_ONLY" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--cluster_only' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_RM_REDUNDANT" ]]; then + if ! [[ "$VIASH_PAR_RM_REDUNDANT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--rm_redundant' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_BOUNDARY" ]]; then + if ! [[ "$VIASH_PAR_NO_BOUNDARY" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_boundary' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_OVERLAP" ]]; then + if ! [[ "$VIASH_PAR_NO_OVERLAP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_overlap' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTFILE" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTFILE")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTFILE")" +fi +if [ ! -z "$VIASH_PAR_SPLICED_EXONS" ] && [ ! -d "$(dirname "$VIASH_PAR_SPLICED_EXONS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_SPLICED_EXONS")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_CHR_MAPPING" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_CHR_MAPPING")" ) + VIASH_PAR_CHR_MAPPING=$(ViashDockerAutodetectMount "$VIASH_PAR_CHR_MAPPING") +fi +if [ ! -z "$VIASH_PAR_SEQ_INFO" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SEQ_INFO")" ) + VIASH_PAR_SEQ_INFO=$(ViashDockerAutodetectMount "$VIASH_PAR_SEQ_INFO") +fi +if [ ! -z "$VIASH_PAR_GENOME" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_GENOME")" ) + VIASH_PAR_GENOME=$(ViashDockerAutodetectMount "$VIASH_PAR_GENOME") +fi +if [ ! -z "$VIASH_PAR_OUTFILE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTFILE")" ) + VIASH_PAR_OUTFILE=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTFILE") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTFILE" ) +fi +if [ ! -z "$VIASH_PAR_SPLICED_EXONS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SPLICED_EXONS")" ) + VIASH_PAR_SPLICED_EXONS=$(ViashDockerAutodetectMount "$VIASH_PAR_SPLICED_EXONS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_SPLICED_EXONS" ) +fi +if [ ! -z "$VIASH_PAR_SPLICED_CDS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SPLICED_CDS")" ) + VIASH_PAR_SPLICED_CDS=$(ViashDockerAutodetectMount "$VIASH_PAR_SPLICED_CDS") +fi +if [ ! -z "$VIASH_PAR_TR_CDS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_TR_CDS")" ) + VIASH_PAR_TR_CDS=$(ViashDockerAutodetectMount "$VIASH_PAR_TR_CDS") +fi +if [ ! -z "$VIASH_PAR_IDS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_IDS")" ) + VIASH_PAR_IDS=$(ViashDockerAutodetectMount "$VIASH_PAR_IDS") +fi +if [ ! -z "$VIASH_PAR_NIDS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_NIDS")" ) + VIASH_PAR_NIDS=$(ViashDockerAutodetectMount "$VIASH_PAR_NIDS") +fi +if [ ! -z "$VIASH_PAR_SORT_BY" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SORT_BY")" ) + VIASH_PAR_SORT_BY=$(ViashDockerAutodetectMount "$VIASH_PAR_SORT_BY") +fi +if [ ! -z "$VIASH_PAR_DUPINFO" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_DUPINFO")" ) + VIASH_PAR_DUPINFO=$(ViashDockerAutodetectMount "$VIASH_PAR_DUPINFO") +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-gffread-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_CHR_MAPPING+x} ]; then echo "${VIASH_PAR_CHR_MAPPING}" | sed "s#'#'\"'\"'#g;s#.*#par_chr_mapping='&'#" ; else echo "# par_chr_mapping="; fi ) +$( if [ ! -z ${VIASH_PAR_SEQ_INFO+x} ]; then echo "${VIASH_PAR_SEQ_INFO}" | sed "s#'#'\"'\"'#g;s#.*#par_seq_info='&'#" ; else echo "# par_seq_info="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOME+x} ]; then echo "${VIASH_PAR_GENOME}" | sed "s#'#'\"'\"'#g;s#.*#par_genome='&'#" ; else echo "# par_genome="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTFILE+x} ]; then echo "${VIASH_PAR_OUTFILE}" | sed "s#'#'\"'\"'#g;s#.*#par_outfile='&'#" ; else echo "# par_outfile="; fi ) +$( if [ ! -z ${VIASH_PAR_FORCE_EXONS+x} ]; then echo "${VIASH_PAR_FORCE_EXONS}" | sed "s#'#'\"'\"'#g;s#.*#par_force_exons='&'#" ; else echo "# par_force_exons="; fi ) +$( if [ ! -z ${VIASH_PAR_GENE2EXON+x} ]; then echo "${VIASH_PAR_GENE2EXON}" | sed "s#'#'\"'\"'#g;s#.*#par_gene2exon='&'#" ; else echo "# par_gene2exon="; fi ) +$( if [ ! -z ${VIASH_PAR_T_ADOPT+x} ]; then echo "${VIASH_PAR_T_ADOPT}" | sed "s#'#'\"'\"'#g;s#.*#par_t_adopt='&'#" ; else echo "# par_t_adopt="; fi ) +$( if [ ! -z ${VIASH_PAR_DECODE+x} ]; then echo "${VIASH_PAR_DECODE}" | sed "s#'#'\"'\"'#g;s#.*#par_decode='&'#" ; else echo "# par_decode="; fi ) +$( if [ ! -z ${VIASH_PAR_MERGE_EXONS+x} ]; then echo "${VIASH_PAR_MERGE_EXONS}" | sed "s#'#'\"'\"'#g;s#.*#par_merge_exons='&'#" ; else echo "# par_merge_exons="; fi ) +$( if [ ! -z ${VIASH_PAR_JUNCTIONS+x} ]; then echo "${VIASH_PAR_JUNCTIONS}" | sed "s#'#'\"'\"'#g;s#.*#par_junctions='&'#" ; else echo "# par_junctions="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLICED_EXONS+x} ]; then echo "${VIASH_PAR_SPLICED_EXONS}" | sed "s#'#'\"'\"'#g;s#.*#par_spliced_exons='&'#" ; else echo "# par_spliced_exons="; fi ) +$( if [ ! -z ${VIASH_PAR_W_ADD+x} ]; then echo "${VIASH_PAR_W_ADD}" | sed "s#'#'\"'\"'#g;s#.*#par_w_add='&'#" ; else echo "# par_w_add="; fi ) +$( if [ ! -z ${VIASH_PAR_W_NOCDS+x} ]; then echo "${VIASH_PAR_W_NOCDS}" | sed "s#'#'\"'\"'#g;s#.*#par_w_nocds='&'#" ; else echo "# par_w_nocds="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLICED_CDS+x} ]; then echo "${VIASH_PAR_SPLICED_CDS}" | sed "s#'#'\"'\"'#g;s#.*#par_spliced_cds='&'#" ; else echo "# par_spliced_cds="; fi ) +$( if [ ! -z ${VIASH_PAR_TR_CDS+x} ]; then echo "${VIASH_PAR_TR_CDS}" | sed "s#'#'\"'\"'#g;s#.*#par_tr_cds='&'#" ; else echo "# par_tr_cds="; fi ) +$( if [ ! -z ${VIASH_PAR_W_COORDS+x} ]; then echo "${VIASH_PAR_W_COORDS}" | sed "s#'#'\"'\"'#g;s#.*#par_w_coords='&'#" ; else echo "# par_w_coords="; fi ) +$( if [ ! -z ${VIASH_PAR_STOP_DOT+x} ]; then echo "${VIASH_PAR_STOP_DOT}" | sed "s#'#'\"'\"'#g;s#.*#par_stop_dot='&'#" ; else echo "# par_stop_dot="; fi ) +$( if [ ! -z ${VIASH_PAR_ID_VERSION+x} ]; then echo "${VIASH_PAR_ID_VERSION}" | sed "s#'#'\"'\"'#g;s#.*#par_id_version='&'#" ; else echo "# par_id_version="; fi ) +$( if [ ! -z ${VIASH_PAR_TRACKNAME+x} ]; then echo "${VIASH_PAR_TRACKNAME}" | sed "s#'#'\"'\"'#g;s#.*#par_trackname='&'#" ; else echo "# par_trackname="; fi ) +$( if [ ! -z ${VIASH_PAR_GTF_OUTPUT+x} ]; then echo "${VIASH_PAR_GTF_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_gtf_output='&'#" ; else echo "# par_gtf_output="; fi ) +$( if [ ! -z ${VIASH_PAR_BED+x} ]; then echo "${VIASH_PAR_BED}" | sed "s#'#'\"'\"'#g;s#.*#par_bed='&'#" ; else echo "# par_bed="; fi ) +$( if [ ! -z ${VIASH_PAR_TLF+x} ]; then echo "${VIASH_PAR_TLF}" | sed "s#'#'\"'\"'#g;s#.*#par_tlf='&'#" ; else echo "# par_tlf="; fi ) +$( if [ ! -z ${VIASH_PAR_TABLE+x} ]; then echo "${VIASH_PAR_TABLE}" | sed "s#'#'\"'\"'#g;s#.*#par_table='&'#" ; else echo "# par_table="; fi ) +$( if [ ! -z ${VIASH_PAR_EXPOSE_DUPS+x} ]; then echo "${VIASH_PAR_EXPOSE_DUPS}" | sed "s#'#'\"'\"'#g;s#.*#par_expose_dups='&'#" ; else echo "# par_expose_dups="; fi ) +$( if [ ! -z ${VIASH_PAR_IDS+x} ]; then echo "${VIASH_PAR_IDS}" | sed "s#'#'\"'\"'#g;s#.*#par_ids='&'#" ; else echo "# par_ids="; fi ) +$( if [ ! -z ${VIASH_PAR_NIDS+x} ]; then echo "${VIASH_PAR_NIDS}" | sed "s#'#'\"'\"'#g;s#.*#par_nids='&'#" ; else echo "# par_nids="; fi ) +$( if [ ! -z ${VIASH_PAR_MAXINTRON+x} ]; then echo "${VIASH_PAR_MAXINTRON}" | sed "s#'#'\"'\"'#g;s#.*#par_maxintron='&'#" ; else echo "# par_maxintron="; fi ) +$( if [ ! -z ${VIASH_PAR_MINLEN+x} ]; then echo "${VIASH_PAR_MINLEN}" | sed "s#'#'\"'\"'#g;s#.*#par_minlen='&'#" ; else echo "# par_minlen="; fi ) +$( if [ ! -z ${VIASH_PAR_RANGE+x} ]; then echo "${VIASH_PAR_RANGE}" | sed "s#'#'\"'\"'#g;s#.*#par_range='&'#" ; else echo "# par_range="; fi ) +$( if [ ! -z ${VIASH_PAR_STRICT_RANGE+x} ]; then echo "${VIASH_PAR_STRICT_RANGE}" | sed "s#'#'\"'\"'#g;s#.*#par_strict_range='&'#" ; else echo "# par_strict_range="; fi ) +$( if [ ! -z ${VIASH_PAR_JMATCH+x} ]; then echo "${VIASH_PAR_JMATCH}" | sed "s#'#'\"'\"'#g;s#.*#par_jmatch='&'#" ; else echo "# par_jmatch="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_SINGLE_EXON+x} ]; then echo "${VIASH_PAR_NO_SINGLE_EXON}" | sed "s#'#'\"'\"'#g;s#.*#par_no_single_exon='&'#" ; else echo "# par_no_single_exon="; fi ) +$( if [ ! -z ${VIASH_PAR_CODING+x} ]; then echo "${VIASH_PAR_CODING}" | sed "s#'#'\"'\"'#g;s#.*#par_coding='&'#" ; else echo "# par_coding="; fi ) +$( if [ ! -z ${VIASH_PAR_NC+x} ]; then echo "${VIASH_PAR_NC}" | sed "s#'#'\"'\"'#g;s#.*#par_nc='&'#" ; else echo "# par_nc="; fi ) +$( if [ ! -z ${VIASH_PAR_IGNORE_LOCUS+x} ]; then echo "${VIASH_PAR_IGNORE_LOCUS}" | sed "s#'#'\"'\"'#g;s#.*#par_ignore_locus='&'#" ; else echo "# par_ignore_locus="; fi ) +$( if [ ! -z ${VIASH_PAR_DESCRIPTION+x} ]; then echo "${VIASH_PAR_DESCRIPTION}" | sed "s#'#'\"'\"'#g;s#.*#par_description='&'#" ; else echo "# par_description="; fi ) +$( if [ ! -z ${VIASH_PAR_SORT_ALPHA+x} ]; then echo "${VIASH_PAR_SORT_ALPHA}" | sed "s#'#'\"'\"'#g;s#.*#par_sort_alpha='&'#" ; else echo "# par_sort_alpha="; fi ) +$( if [ ! -z ${VIASH_PAR_SORT_BY+x} ]; then echo "${VIASH_PAR_SORT_BY}" | sed "s#'#'\"'\"'#g;s#.*#par_sort_by='&'#" ; else echo "# par_sort_by="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_ATTRS+x} ]; then echo "${VIASH_PAR_KEEP_ATTRS}" | sed "s#'#'\"'\"'#g;s#.*#par_keep_attrs='&'#" ; else echo "# par_keep_attrs="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_EXON_ATTRS+x} ]; then echo "${VIASH_PAR_KEEP_EXON_ATTRS}" | sed "s#'#'\"'\"'#g;s#.*#par_keep_exon_attrs='&'#" ; else echo "# par_keep_exon_attrs="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_EXON_ATTRS+x} ]; then echo "${VIASH_PAR_NO_EXON_ATTRS}" | sed "s#'#'\"'\"'#g;s#.*#par_no_exon_attrs='&'#" ; else echo "# par_no_exon_attrs="; fi ) +$( if [ ! -z ${VIASH_PAR_ATTRS+x} ]; then echo "${VIASH_PAR_ATTRS}" | sed "s#'#'\"'\"'#g;s#.*#par_attrs='&'#" ; else echo "# par_attrs="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_GENES+x} ]; then echo "${VIASH_PAR_KEEP_GENES}" | sed "s#'#'\"'\"'#g;s#.*#par_keep_genes='&'#" ; else echo "# par_keep_genes="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_COMMENTS+x} ]; then echo "${VIASH_PAR_KEEP_COMMENTS}" | sed "s#'#'\"'\"'#g;s#.*#par_keep_comments='&'#" ; else echo "# par_keep_comments="; fi ) +$( if [ ! -z ${VIASH_PAR_PROCESS_OTHER+x} ]; then echo "${VIASH_PAR_PROCESS_OTHER}" | sed "s#'#'\"'\"'#g;s#.*#par_process_other='&'#" ; else echo "# par_process_other="; fi ) +$( if [ ! -z ${VIASH_PAR_RM_STOP_CODONS+x} ]; then echo "${VIASH_PAR_RM_STOP_CODONS}" | sed "s#'#'\"'\"'#g;s#.*#par_rm_stop_codons='&'#" ; else echo "# par_rm_stop_codons="; fi ) +$( if [ ! -z ${VIASH_PAR_ADJ_CDS_START+x} ]; then echo "${VIASH_PAR_ADJ_CDS_START}" | sed "s#'#'\"'\"'#g;s#.*#par_adj_cds_start='&'#" ; else echo "# par_adj_cds_start="; fi ) +$( if [ ! -z ${VIASH_PAR_OPPOSITE_STRAND+x} ]; then echo "${VIASH_PAR_OPPOSITE_STRAND}" | sed "s#'#'\"'\"'#g;s#.*#par_opposite_strand='&'#" ; else echo "# par_opposite_strand="; fi ) +$( if [ ! -z ${VIASH_PAR_CODING_STATUS+x} ]; then echo "${VIASH_PAR_CODING_STATUS}" | sed "s#'#'\"'\"'#g;s#.*#par_coding_status='&'#" ; else echo "# par_coding_status="; fi ) +$( if [ ! -z ${VIASH_PAR_ADD_HASCDS+x} ]; then echo "${VIASH_PAR_ADD_HASCDS}" | sed "s#'#'\"'\"'#g;s#.*#par_add_hasCDS='&'#" ; else echo "# par_add_hasCDS="; fi ) +$( if [ ! -z ${VIASH_PAR_ADJ_STOP+x} ]; then echo "${VIASH_PAR_ADJ_STOP}" | sed "s#'#'\"'\"'#g;s#.*#par_adj_stop='&'#" ; else echo "# par_adj_stop="; fi ) +$( if [ ! -z ${VIASH_PAR_RM_NONCANON+x} ]; then echo "${VIASH_PAR_RM_NONCANON}" | sed "s#'#'\"'\"'#g;s#.*#par_rm_noncanon='&'#" ; else echo "# par_rm_noncanon="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPLETE_CDS+x} ]; then echo "${VIASH_PAR_COMPLETE_CDS}" | sed "s#'#'\"'\"'#g;s#.*#par_complete_cds='&'#" ; else echo "# par_complete_cds="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_PSEUDO+x} ]; then echo "${VIASH_PAR_NO_PSEUDO}" | sed "s#'#'\"'\"'#g;s#.*#par_no_pseudo='&'#" ; else echo "# par_no_pseudo="; fi ) +$( if [ ! -z ${VIASH_PAR_IN_BED+x} ]; then echo "${VIASH_PAR_IN_BED}" | sed "s#'#'\"'\"'#g;s#.*#par_in_bed='&'#" ; else echo "# par_in_bed="; fi ) +$( if [ ! -z ${VIASH_PAR_IN_TLF+x} ]; then echo "${VIASH_PAR_IN_TLF}" | sed "s#'#'\"'\"'#g;s#.*#par_in_tlf='&'#" ; else echo "# par_in_tlf="; fi ) +$( if [ ! -z ${VIASH_PAR_STREAM+x} ]; then echo "${VIASH_PAR_STREAM}" | sed "s#'#'\"'\"'#g;s#.*#par_stream='&'#" ; else echo "# par_stream="; fi ) +$( if [ ! -z ${VIASH_PAR_MERGE+x} ]; then echo "${VIASH_PAR_MERGE}" | sed "s#'#'\"'\"'#g;s#.*#par_merge='&'#" ; else echo "# par_merge="; fi ) +$( if [ ! -z ${VIASH_PAR_DUPINFO+x} ]; then echo "${VIASH_PAR_DUPINFO}" | sed "s#'#'\"'\"'#g;s#.*#par_dupinfo='&'#" ; else echo "# par_dupinfo="; fi ) +$( if [ ! -z ${VIASH_PAR_CLUSTER_ONLY+x} ]; then echo "${VIASH_PAR_CLUSTER_ONLY}" | sed "s#'#'\"'\"'#g;s#.*#par_cluster_only='&'#" ; else echo "# par_cluster_only="; fi ) +$( if [ ! -z ${VIASH_PAR_RM_REDUNDANT+x} ]; then echo "${VIASH_PAR_RM_REDUNDANT}" | sed "s#'#'\"'\"'#g;s#.*#par_rm_redundant='&'#" ; else echo "# par_rm_redundant="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_BOUNDARY+x} ]; then echo "${VIASH_PAR_NO_BOUNDARY}" | sed "s#'#'\"'\"'#g;s#.*#par_no_boundary='&'#" ; else echo "# par_no_boundary="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_OVERLAP+x} ]; then echo "${VIASH_PAR_NO_OVERLAP}" | sed "s#'#'\"'\"'#g;s#.*#par_no_overlap='&'#" ; else echo "# par_no_overlap="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# unset flags +[[ "\$par_coding" == "false" ]] && unset par_coding +[[ "\$par_strict_range" == "false" ]] && unset par_strict_range +[[ "\$par_no_single_exon" == "false" ]] && unset par_no_single_exon +[[ "\$par_no_exon_attrs" == "false" ]] && unset par_no_exon_attrs +[[ "\$par_nc" == "false" ]] && unset par_nc +[[ "\$par_ignore_locus" == "false" ]] && unset par_ignore_locus +[[ "\$par_description" == "false" ]] && unset par_description +[[ "\$par_sort_alpha" == "false" ]] && unset par_sort_alpha +[[ "\$par_keep_genes" == "false" ]] && unset par_keep_genes +[[ "\$par_keep_attrs" == "false" ]] && unset par_keep_attrs +[[ "\$par_keep_exon_attrs" == "false" ]] && unset par_keep_exon_attrs +[[ "\$par_keep_comments" == "false" ]] && unset par_keep_comments +[[ "\$par_process_other" == "false" ]] && unset par_process_other +[[ "\$par_rm_stop_codons" == "false" ]] && unset par_rm_stop_codons +[[ "\$par_adj_cds_start" == "false" ]] && unset par_adj_cds_start +[[ "\$par_opposite_strand" == "false" ]] && unset par_opposite_strand +[[ "\$par_coding_status" == "false" ]] && unset par_coding_status +[[ "\$par_add_hasCDS" == "false" ]] && unset par_add_hasCDS +[[ "\$par_adj_stop" == "false" ]] && unset par_adj_stop +[[ "\$par_rm_noncanon" == "false" ]] && unset par_rm_noncanon +[[ "\$par_complete_cds" == "false" ]] && unset par_complete_cds +[[ "\$par_no_pseudo" == "false" ]] && unset par_no_pseudo +[[ "\$par_in_bed" == "false" ]] && unset par_in_bed +[[ "\$par_in_tlf" == "false" ]] && unset par_in_tlf +[[ "\$par_stream" == "false" ]] && unset par_stream +[[ "\$par_merge" == "false" ]] && unset par_merge +[[ "\$par_rm_redundant" == "false" ]] && unset par_rm_redundant +[[ "\$par_no_boundary" == "false" ]] && unset par_no_boundary +[[ "\$par_no_overlap" == "false" ]] && unset par_no_overlap +[[ "\$par_force_exons" == "false" ]] && unset par_force_exons +[[ "\$par_gene2exon" == "false" ]] && unset par_gene2exon +[[ "\$par_t_adopt" == "false" ]] && unset par_t_adopt +[[ "\$par_decode" == "false" ]] && unset par_decode +[[ "\$par_merge_exons" == "false" ]] && unset par_merge_exons +[[ "\$par_junctions" == "false" ]] && unset par_junctions +[[ "\$par_w_nocds" == "false" ]] && unset par_w_nocds +[[ "\$par_tr_cds" == "false" ]] && unset par_tr_cds +[[ "\$par_w_coords" == "false" ]] && unset par_w_coords +[[ "\$par_stop_dot" == "false" ]] && unset par_stop_dot +[[ "\$par_id_version" == "false" ]] && unset par_id_version +[[ "\$par_gtf_output" == "false" ]] && unset par_gtf_output +[[ "\$par_bed" == "false" ]] && unset par_bed +[[ "\$par_tlf" == "false" ]] && unset par_tlf +[[ "\$par_expose_dups" == "false" ]] && unset par_expose_dups +[[ "\$par_cluster_only" == "false" ]] && unset par_cluster_only + + +\$(which gffread) \\ + "\$par_input" \\ + \${par_chr_mapping:+-m "\$par_chr_mapping"} \\ + \${par_seq_info:+-s "\$par_seq_info"} \\ + -o "\$par_outfile" \\ + \${par_force_exons:+--force-exons} \\ + \${par_gene2exon:+--gene2exon} \\ + \${par_t_adopt:+--t-adopt} \\ + \${par_decode:+-D} \\ + \${par_merge_exons:+-Z} \\ + \${par_genome:+-g "\$par_genome"} \\ + \${par_junctions:+-j} \\ + \${par_spliced_exons:+-w "\$par_spliced_exons"} \\ + \${par_w_add:+--w-add "\$par_w_add"} \\ + \${par_w_nocds:+--w-nocds} \\ + \${par_spliced_cds:+-x "\$par_spliced_cds"} \\ + \${par_tr_cds:+-y "\$par_tr_cds"} \\ + \${par_w_coords:+-W} \\ + \${par_stop_dot:+-S} \\ + \${par_id_version:+-L} \\ + \${par_trackname:+-t "\$par_trackname"} \\ + \${par_gtf_output:+-T} \\ + \${par_bed:+--bed} \\ + \${par_tlf:+--tlf} \\ + \${par_table:+--table "\$par_table"} \\ + \${par_expose_dups:+-E} \\ + \${par_ids:+--ids "\$par_ids"} \\ + \${par_nids:+--nids "\$par_nids"} \\ + \${par_maxintron:+-i "\$par_maxintron"} \\ + \${par_minlen:+-l "\$par_minlen"} \\ + \${par_range:+-r "\$par_range"} \\ + \${par_strict_range:+-R} \\ + \${par_jmatch:+--jmatch "\$par_jmatch"} \\ + \${par_no_single_exon:+-U} \\ + \${par_coding:+-C} \\ + \${par_nc:+--nc} \\ + \${par_ignore_locus:+--ignore-locus} \\ + \${par_description:+-A} \\ + \${par_sort_alpha:+--sort-alpha} \\ + \${par_sort_by:+--sort-by "\$par_sort_by"} \\ + \${par_keep_attrs:+-F} \\ + \${par_keep_exon_attrs:+--keep-exon-attrs} \\ + \${par_no_exon_attrs:+-G} \\ + \${par_attrs:+--attrs "\$par_attrs"} \\ + \${par_keep_genes:+--keep-genes} \\ + \${par_keep_comments:+--keep-comments} \\ + \${par_process_other:+-O} \\ + \${par_rm_stop_codons:+-V} \\ + \${par_adj_cds_start:+-H} \\ + \${par_opposite_strand:+-B} \\ + \${par_coding_status:+-P} \\ + \${par_add_hasCDS:+--add-hasCDS} \\ + \${par_adj_stop:+--adj-stop} \\ + \${par_rm_noncanon:+-N} \\ + \${par_complete_cds:+-J} \\ + \${par_no_pseudo:+--no-pseudo} \\ + \${par_in_bed:+--in-bed} \\ + \${par_in_tlf:+--in-tlf} \\ + \${par_stream:+--stream} \\ + \${par_merge:+-M} \\ + \${par_dupinfo:+-d "\$par_dupinfo"} \\ + \${par_cluster_only:+--cluster-only} \\ + \${par_rm_redundant:+-K} \\ + \${par_no_boundary:+-Q} \\ + \${par_no_overlap:+-Y} +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_CHR_MAPPING" ]; then + VIASH_PAR_CHR_MAPPING=$(ViashDockerStripAutomount "$VIASH_PAR_CHR_MAPPING") + fi + if [ ! -z "$VIASH_PAR_SEQ_INFO" ]; then + VIASH_PAR_SEQ_INFO=$(ViashDockerStripAutomount "$VIASH_PAR_SEQ_INFO") + fi + if [ ! -z "$VIASH_PAR_GENOME" ]; then + VIASH_PAR_GENOME=$(ViashDockerStripAutomount "$VIASH_PAR_GENOME") + fi + if [ ! -z "$VIASH_PAR_OUTFILE" ]; then + VIASH_PAR_OUTFILE=$(ViashDockerStripAutomount "$VIASH_PAR_OUTFILE") + fi + if [ ! -z "$VIASH_PAR_SPLICED_EXONS" ]; then + VIASH_PAR_SPLICED_EXONS=$(ViashDockerStripAutomount "$VIASH_PAR_SPLICED_EXONS") + fi + if [ ! -z "$VIASH_PAR_SPLICED_CDS" ]; then + VIASH_PAR_SPLICED_CDS=$(ViashDockerStripAutomount "$VIASH_PAR_SPLICED_CDS") + fi + if [ ! -z "$VIASH_PAR_TR_CDS" ]; then + VIASH_PAR_TR_CDS=$(ViashDockerStripAutomount "$VIASH_PAR_TR_CDS") + fi + if [ ! -z "$VIASH_PAR_IDS" ]; then + VIASH_PAR_IDS=$(ViashDockerStripAutomount "$VIASH_PAR_IDS") + fi + if [ ! -z "$VIASH_PAR_NIDS" ]; then + VIASH_PAR_NIDS=$(ViashDockerStripAutomount "$VIASH_PAR_NIDS") + fi + if [ ! -z "$VIASH_PAR_SORT_BY" ]; then + VIASH_PAR_SORT_BY=$(ViashDockerStripAutomount "$VIASH_PAR_SORT_BY") + fi + if [ ! -z "$VIASH_PAR_DUPINFO" ]; then + VIASH_PAR_DUPINFO=$(ViashDockerStripAutomount "$VIASH_PAR_DUPINFO") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTFILE" ] && [ ! -e "$VIASH_PAR_OUTFILE" ]; then + ViashError "Output file '$VIASH_PAR_OUTFILE' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/lofreq/lofreq_call/.config.vsh.yaml b/target/executable/lofreq/lofreq_call/.config.vsh.yaml new file mode 100644 index 00000000..f0933ee4 --- /dev/null +++ b/target/executable/lofreq/lofreq_call/.config.vsh.yaml @@ -0,0 +1,515 @@ +name: "lofreq_call" +namespace: "lofreq" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "Input BAM file.\n" + info: null + example: + - "normal.bam" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--input_bai" + description: "Index file for the input BAM file.\n" + info: null + example: + - "normal.bai" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--ref" + alternatives: + - "-f" + description: "Indexed reference fasta file (gzip supported). Default: none.\n" + info: null + example: + - "reference.fasta" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--out" + alternatives: + - "-o" + description: "Vcf output file. Default: stdout.\n" + info: null + example: + - "output.vcf" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Arguments" + arguments: + - type: "string" + name: "--region" + alternatives: + - "-r" + description: "Limit calls to this region (chrom:start-end). Default: none.\n" + info: null + example: + - "chr1:1000-2000" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--bed" + alternatives: + - "-l" + description: "List of positions (chr pos) or regions (BED). Default: none.\n" + info: null + example: + - "regions.bed" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_bq" + alternatives: + - "-q" + description: "Skip any base with baseQ smaller than INT. Default: 6.\n" + info: null + example: + - 6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_alt_bq" + alternatives: + - "-Q" + description: "Skip alternate bases with baseQ smaller than INT. Default: 6.\n" + info: null + example: + - 6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--def_alt_bq" + alternatives: + - "-R" + description: "Overwrite baseQs of alternate bases (that passed bq filter) with\ + \ this value (-1: use median ref-bq; 0: keep). Default: 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_jq" + alternatives: + - "-j" + description: "Skip any base with joinedQ smaller than INT. Default: 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_alt_jq" + alternatives: + - "-J" + description: "Skip alternate bases with joinedQ smaller than INT. Default: 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--def_alt_jq" + alternatives: + - "-K" + description: "Overwrite joinedQs of alternate bases (that passed jq filter) with\ + \ this value (-1: use median ref-bq; 0: keep). Default: 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_baq" + alternatives: + - "-B" + description: "Disable use of base-alignment quality (BAQ).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_idaq" + alternatives: + - "-A" + description: "Don't use IDAQ values (NOT recommended under ANY circumstances other\ + \ than debugging).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--del_baq" + alternatives: + - "-D" + description: "Delete pre-existing BAQ values, i.e. compute even if already present\ + \ in BAM.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_ext_baq" + alternatives: + - "-e" + description: "Use 'normal' BAQ (samtools default) instead of extended BAQ (both\ + \ computed on the fly if not already present in lb tag).\n" + info: null + direction: "input" + - type: "integer" + name: "--min_mq" + alternatives: + - "-m" + description: "Skip reads with mapping quality smaller than INT. Default: 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_mq" + alternatives: + - "-M" + description: "Cap mapping quality at INT. Default: 255.\n" + info: null + example: + - 255 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_mq" + alternatives: + - "-N" + description: "Don't merge mapping quality in LoFreq's model.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--call_indels" + description: "Enable indel calls (note: preprocess your file to include indel\ + \ alignment qualities!).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--only_indels" + description: "Only call indels; no SNVs.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--src_qual" + alternatives: + - "-s" + description: "Enable computation of source quality.\n" + info: null + direction: "input" + - type: "file" + name: "--ign_vcf" + alternatives: + - "-S" + description: "Ignore variants in this vcf file for source quality computation.\ + \ Multiple files can be given separated by commas.\n" + info: null + example: + - "variants.vcf" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--def_nm_q" + alternatives: + - "-T" + description: "If >= 0, then replace non-match base qualities with this default\ + \ value. Default: -1.\n" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--sig" + alternatives: + - "-a" + description: "P-Value cutoff / significance level. Default: 0.010000.\n" + info: null + example: + - 0.01 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--bonf" + alternatives: + - "-b" + description: "Bonferroni factor. 'dynamic' (increase per actually performed test)\ + \ or INT. Default: Dynamic.\n" + info: null + example: + - "dynamic" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_cov" + alternatives: + - "-C" + description: "Test only positions having at least this coverage. Default: 1.\n\ + (note: without --no-default-filter default filters (incl. coverage) kick in\ + \ after predictions are done).\n" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_depth" + alternatives: + - "-d" + description: "Cap coverage at this depth. Default: 1000000.\n" + info: null + example: + - 1000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--illumina_13" + description: "Assume the quality is Illumina-1.3-1.7/ASCII+64 encoded.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--use_orphan" + description: "Count anomalous read pairs (i.e. where mate is not aligned properly).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--plp_summary_only" + description: "No variant calling. Just output pileup summary per column.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_default_filter" + description: "Don't run default 'lofreq filter' automatically after calling variants.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--force_overwrite" + description: "Overwrite any existing output.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--verbose" + description: "Be verbose.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--debug" + description: "Enable debugging.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Call variants from a BAM file.\n\nLoFreq* (i.e. LoFreq version 2) is\ + \ a fast and sensitive variant-caller for inferring SNVs and indels from next-generation\ + \ sequencing data. It makes full use of base-call qualities and other sources of\ + \ errors inherent in sequencing (e.g. mapping or base/indel alignment uncertainty),\ + \ which are usually ignored by other methods or only used for filtering.\n\nLoFreq*\ + \ can run on almost any type of aligned sequencing data (e.g. Illumina, IonTorrent\ + \ or Pacbio) since no machine- or sequencing-technology dependent thresholds are\ + \ used. It automatically adapts to changes in coverage and sequencing quality and\ + \ can therefore be applied to a variety of data-sets e.g. viral/quasispecies, bacterial,\ + \ metagenomics or somatic data.\n\nLoFreq* is very sensitive; most notably, it is\ + \ able to predict variants below the average base-call quality (i.e. sequencing\ + \ error rate). Each variant call is assigned a p-value which allows for rigorous\ + \ false positive control. Even though it uses no approximations or heuristics, it\ + \ is very efficient due to several runtime optimizations and also provides a (pseudo-)parallel\ + \ implementation. LoFreq* is generic and fast enough to be applied to high-coverage\ + \ data and large genomes. On a single processor it takes a minute to analyze Dengue\ + \ genome sequencing data with nearly 4000X coverage, roughly one hour to call SNVs\ + \ on a 600X coverage E.coli genome and also roughly an hour to run on a 100X coverage\ + \ human exome dataset.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "variant calling" +- "low frequancy variant calling" +- "lofreq" +- "lofreq/call" +license: "MIT" +references: + doi: + - "10.1093/nar/gks918" +links: + repository: "https://github.com/viash-hub/biobox" + homepage: "https://csb5.github.io/lofreq/" + documentation: "https://csb5.github.io/lofreq/commands/" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/lofreq:2.1.5--py38h794fc9e_10" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "version=$(lofreq version | grep 'version' | sed 's/version: //') && \\\necho\ + \ \"lofreq: $version\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/lofreq/call/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/lofreq/lofreq_call" + executable: "target/executable/lofreq/lofreq_call/lofreq_call" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/lofreq/lofreq_call/lofreq_call b/target/executable/lofreq/lofreq_call/lofreq_call new file mode 100755 index 00000000..3364f9b7 --- /dev/null +++ b/target/executable/lofreq/lofreq_call/lofreq_call @@ -0,0 +1,1989 @@ +#!/usr/bin/env bash + +# lofreq_call main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="lofreq_call" +VIASH_META_FUNCTIONALITY_NAME="lofreq_call" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "lofreq_call main" + echo "" + echo "Call variants from a BAM file." + echo "" + echo "LoFreq* (i.e. LoFreq version 2) is a fast and sensitive variant-caller for" + echo "inferring SNVs and indels from next-generation sequencing data. It makes full" + echo "use of base-call qualities and other sources of errors inherent in sequencing" + echo "(e.g. mapping or base/indel alignment uncertainty), which are usually ignored by" + echo "other methods or only used for filtering." + echo "" + echo "LoFreq* can run on almost any type of aligned sequencing data (e.g. Illumina," + echo "IonTorrent or Pacbio) since no machine- or sequencing-technology dependent" + echo "thresholds are used. It automatically adapts to changes in coverage and" + echo "sequencing quality and can therefore be applied to a variety of data-sets e.g." + echo "viral/quasispecies, bacterial, metagenomics or somatic data." + echo "" + echo "LoFreq* is very sensitive; most notably, it is able to predict variants below" + echo "the average base-call quality (i.e. sequencing error rate). Each variant call is" + echo "assigned a p-value which allows for rigorous false positive control. Even though" + echo "it uses no approximations or heuristics, it is very efficient due to several" + echo "runtime optimizations and also provides a (pseudo-)parallel implementation." + echo "LoFreq* is generic and fast enough to be applied to high-coverage data and large" + echo "genomes. On a single processor it takes a minute to analyze Dengue genome" + echo "sequencing data with nearly 4000X coverage, roughly one hour to call SNVs on a" + echo "600X coverage E.coli genome and also roughly an hour to run on a 100X coverage" + echo "human exome dataset." + echo "" + echo "Inputs:" + echo " --input" + echo " type: file, required parameter, file must exist" + echo " example: normal.bam" + echo " Input BAM file." + echo "" + echo " --input_bai" + echo " type: file, required parameter, file must exist" + echo " example: normal.bai" + echo " Index file for the input BAM file." + echo "" + echo " -f, --ref" + echo " type: file, required parameter, file must exist" + echo " example: reference.fasta" + echo " Indexed reference fasta file (gzip supported). Default: none." + echo "" + echo "Outputs:" + echo " -o, --out" + echo " type: file, required parameter, output, file must exist" + echo " example: output.vcf" + echo " Vcf output file. Default: stdout." + echo "" + echo "Arguments:" + echo " -r, --region" + echo " type: string" + echo " example: chr1:1000-2000" + echo " Limit calls to this region (chrom:start-end). Default: none." + echo "" + echo " -l, --bed" + echo " type: file, file must exist" + echo " example: regions.bed" + echo " List of positions (chr pos) or regions (BED). Default: none." + echo "" + echo " -q, --min_bq" + echo " type: integer" + echo " example: 6" + echo " Skip any base with baseQ smaller than INT. Default: 6." + echo "" + echo " -Q, --min_alt_bq" + echo " type: integer" + echo " example: 6" + echo " Skip alternate bases with baseQ smaller than INT. Default: 6." + echo "" + echo " -R, --def_alt_bq" + echo " type: integer" + echo " example: 0" + echo " Overwrite baseQs of alternate bases (that passed bq filter) with this" + echo " value (-1: use median ref-bq; 0: keep). Default: 0." + echo "" + echo " -j, --min_jq" + echo " type: integer" + echo " example: 0" + echo " Skip any base with joinedQ smaller than INT. Default: 0." + echo "" + echo " -J, --min_alt_jq" + echo " type: integer" + echo " example: 0" + echo " Skip alternate bases with joinedQ smaller than INT. Default: 0." + echo "" + echo " -K, --def_alt_jq" + echo " type: integer" + echo " example: 0" + echo " Overwrite joinedQs of alternate bases (that passed jq filter) with this" + echo " value (-1: use median ref-bq; 0: keep). Default: 0." + echo "" + echo " -B, --no_baq" + echo " type: boolean_true" + echo " Disable use of base-alignment quality (BAQ)." + echo "" + echo " -A, --no_idaq" + echo " type: boolean_true" + echo " Don't use IDAQ values (NOT recommended under ANY circumstances other" + echo " than debugging)." + echo "" + echo " -D, --del_baq" + echo " type: boolean_true" + echo " Delete pre-existing BAQ values, i.e. compute even if already present in" + echo " BAM." + echo "" + echo " -e, --no_ext_baq" + echo " type: boolean_true" + echo " Use 'normal' BAQ (samtools default) instead of extended BAQ (both" + echo " computed on the fly if not already present in lb tag)." + echo "" + echo " -m, --min_mq" + echo " type: integer" + echo " example: 0" + echo " Skip reads with mapping quality smaller than INT. Default: 0." + echo "" + echo " -M, --max_mq" + echo " type: integer" + echo " example: 255" + echo " Cap mapping quality at INT. Default: 255." + echo "" + echo " -N, --no_mq" + echo " type: boolean_true" + echo " Don't merge mapping quality in LoFreq's model." + echo "" + echo " --call_indels" + echo " type: boolean_true" + echo " Enable indel calls (note: preprocess your file to include indel" + echo " alignment qualities!)." + echo "" + echo " --only_indels" + echo " type: boolean_true" + echo " Only call indels; no SNVs." + echo "" + echo " -s, --src_qual" + echo " type: boolean_true" + echo " Enable computation of source quality." + echo "" + echo " -S, --ign_vcf" + echo " type: file, file must exist" + echo " example: variants.vcf" + echo " Ignore variants in this vcf file for source quality computation." + echo " Multiple files can be given separated by commas." + echo "" + echo " -T, --def_nm_q" + echo " type: integer" + echo " example: -1" + echo " If >= 0, then replace non-match base qualities with this default value." + echo " Default: -1." + echo "" + echo " -a, --sig" + echo " type: double" + echo " example: 0.01" + echo " P-Value cutoff / significance level. Default: 0.010000." + echo "" + echo " -b, --bonf" + echo " type: string" + echo " example: dynamic" + echo " Bonferroni factor. 'dynamic' (increase per actually performed test) or" + echo " INT. Default: Dynamic." + echo "" + echo " -C, --min_cov" + echo " type: integer" + echo " example: 1" + echo " Test only positions having at least this coverage. Default: 1." + echo " (note: without --no-default-filter default filters (incl. coverage) kick" + echo " in after predictions are done)." + echo "" + echo " -d, --max_depth" + echo " type: integer" + echo " example: 1000000" + echo " Cap coverage at this depth. Default: 1000000." + echo "" + echo " --illumina_13" + echo " type: boolean_true" + echo " Assume the quality is Illumina-1.3-1.7/ASCII+64 encoded." + echo "" + echo " --use_orphan" + echo " type: boolean_true" + echo " Count anomalous read pairs (i.e. where mate is not aligned properly)." + echo "" + echo " --plp_summary_only" + echo " type: boolean_true" + echo " No variant calling. Just output pileup summary per column." + echo "" + echo " --no_default_filter" + echo " type: boolean_true" + echo " Don't run default 'lofreq filter' automatically after calling variants." + echo "" + echo " --force_overwrite" + echo " type: boolean_true" + echo " Overwrite any existing output." + echo "" + echo " --verbose" + echo " type: boolean_true" + echo " Be verbose." + echo "" + echo " --debug" + echo " type: boolean_true" + echo " Enable debugging." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/lofreq:2.1.5--py38h794fc9e_10 +ENTRYPOINT [] +RUN version=$(lofreq version | grep 'version' | sed 's/version: //') && \ +echo "lofreq: $version" > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component lofreq lofreq_call" +LABEL org.opencontainers.image.created="2024-06-24T08:36:46Z" +LABEL org.opencontainers.image.source="https://github.com/viash-hub/biobox" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "lofreq_call main" + exit + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --input_bai) + [ -n "$VIASH_PAR_INPUT_BAI" ] && ViashError Bad arguments for option \'--input_bai\': \'$VIASH_PAR_INPUT_BAI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_BAI="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input_bai. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input_bai=*) + [ -n "$VIASH_PAR_INPUT_BAI" ] && ViashError Bad arguments for option \'--input_bai=*\': \'$VIASH_PAR_INPUT_BAI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_BAI=$(ViashRemoveFlags "$1") + shift 1 + ;; + --ref) + [ -n "$VIASH_PAR_REF" ] && ViashError Bad arguments for option \'--ref\': \'$VIASH_PAR_REF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --ref. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ref=*) + [ -n "$VIASH_PAR_REF" ] && ViashError Bad arguments for option \'--ref=*\': \'$VIASH_PAR_REF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF=$(ViashRemoveFlags "$1") + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_REF" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_REF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -f. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --out) + [ -n "$VIASH_PAR_OUT" ] && ViashError Bad arguments for option \'--out\': \'$VIASH_PAR_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --out. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --out=*) + [ -n "$VIASH_PAR_OUT" ] && ViashError Bad arguments for option \'--out=*\': \'$VIASH_PAR_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUT" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --region) + [ -n "$VIASH_PAR_REGION" ] && ViashError Bad arguments for option \'--region\': \'$VIASH_PAR_REGION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REGION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --region. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --region=*) + [ -n "$VIASH_PAR_REGION" ] && ViashError Bad arguments for option \'--region=*\': \'$VIASH_PAR_REGION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REGION=$(ViashRemoveFlags "$1") + shift 1 + ;; + -r) + [ -n "$VIASH_PAR_REGION" ] && ViashError Bad arguments for option \'-r\': \'$VIASH_PAR_REGION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REGION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -r. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bed) + [ -n "$VIASH_PAR_BED" ] && ViashError Bad arguments for option \'--bed\': \'$VIASH_PAR_BED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bed. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bed=*) + [ -n "$VIASH_PAR_BED" ] && ViashError Bad arguments for option \'--bed=*\': \'$VIASH_PAR_BED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BED=$(ViashRemoveFlags "$1") + shift 1 + ;; + -l) + [ -n "$VIASH_PAR_BED" ] && ViashError Bad arguments for option \'-l\': \'$VIASH_PAR_BED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -l. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_bq) + [ -n "$VIASH_PAR_MIN_BQ" ] && ViashError Bad arguments for option \'--min_bq\': \'$VIASH_PAR_MIN_BQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_BQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_bq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_bq=*) + [ -n "$VIASH_PAR_MIN_BQ" ] && ViashError Bad arguments for option \'--min_bq=*\': \'$VIASH_PAR_MIN_BQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_BQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -q) + [ -n "$VIASH_PAR_MIN_BQ" ] && ViashError Bad arguments for option \'-q\': \'$VIASH_PAR_MIN_BQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_BQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -q. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_alt_bq) + [ -n "$VIASH_PAR_MIN_ALT_BQ" ] && ViashError Bad arguments for option \'--min_alt_bq\': \'$VIASH_PAR_MIN_ALT_BQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ALT_BQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_alt_bq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_alt_bq=*) + [ -n "$VIASH_PAR_MIN_ALT_BQ" ] && ViashError Bad arguments for option \'--min_alt_bq=*\': \'$VIASH_PAR_MIN_ALT_BQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ALT_BQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -Q) + [ -n "$VIASH_PAR_MIN_ALT_BQ" ] && ViashError Bad arguments for option \'-Q\': \'$VIASH_PAR_MIN_ALT_BQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ALT_BQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -Q. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --def_alt_bq) + [ -n "$VIASH_PAR_DEF_ALT_BQ" ] && ViashError Bad arguments for option \'--def_alt_bq\': \'$VIASH_PAR_DEF_ALT_BQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEF_ALT_BQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --def_alt_bq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --def_alt_bq=*) + [ -n "$VIASH_PAR_DEF_ALT_BQ" ] && ViashError Bad arguments for option \'--def_alt_bq=*\': \'$VIASH_PAR_DEF_ALT_BQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEF_ALT_BQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -R) + [ -n "$VIASH_PAR_DEF_ALT_BQ" ] && ViashError Bad arguments for option \'-R\': \'$VIASH_PAR_DEF_ALT_BQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEF_ALT_BQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -R. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_jq) + [ -n "$VIASH_PAR_MIN_JQ" ] && ViashError Bad arguments for option \'--min_jq\': \'$VIASH_PAR_MIN_JQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_JQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_jq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_jq=*) + [ -n "$VIASH_PAR_MIN_JQ" ] && ViashError Bad arguments for option \'--min_jq=*\': \'$VIASH_PAR_MIN_JQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_JQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -j) + [ -n "$VIASH_PAR_MIN_JQ" ] && ViashError Bad arguments for option \'-j\': \'$VIASH_PAR_MIN_JQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_JQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -j. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_alt_jq) + [ -n "$VIASH_PAR_MIN_ALT_JQ" ] && ViashError Bad arguments for option \'--min_alt_jq\': \'$VIASH_PAR_MIN_ALT_JQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ALT_JQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_alt_jq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_alt_jq=*) + [ -n "$VIASH_PAR_MIN_ALT_JQ" ] && ViashError Bad arguments for option \'--min_alt_jq=*\': \'$VIASH_PAR_MIN_ALT_JQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ALT_JQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -J) + [ -n "$VIASH_PAR_MIN_ALT_JQ" ] && ViashError Bad arguments for option \'-J\': \'$VIASH_PAR_MIN_ALT_JQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ALT_JQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -J. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --def_alt_jq) + [ -n "$VIASH_PAR_DEF_ALT_JQ" ] && ViashError Bad arguments for option \'--def_alt_jq\': \'$VIASH_PAR_DEF_ALT_JQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEF_ALT_JQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --def_alt_jq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --def_alt_jq=*) + [ -n "$VIASH_PAR_DEF_ALT_JQ" ] && ViashError Bad arguments for option \'--def_alt_jq=*\': \'$VIASH_PAR_DEF_ALT_JQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEF_ALT_JQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -K) + [ -n "$VIASH_PAR_DEF_ALT_JQ" ] && ViashError Bad arguments for option \'-K\': \'$VIASH_PAR_DEF_ALT_JQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEF_ALT_JQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -K. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --no_baq) + [ -n "$VIASH_PAR_NO_BAQ" ] && ViashError Bad arguments for option \'--no_baq\': \'$VIASH_PAR_NO_BAQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_BAQ=true + shift 1 + ;; + -B) + [ -n "$VIASH_PAR_NO_BAQ" ] && ViashError Bad arguments for option \'-B\': \'$VIASH_PAR_NO_BAQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_BAQ=true + shift 1 + ;; + --no_idaq) + [ -n "$VIASH_PAR_NO_IDAQ" ] && ViashError Bad arguments for option \'--no_idaq\': \'$VIASH_PAR_NO_IDAQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_IDAQ=true + shift 1 + ;; + -A) + [ -n "$VIASH_PAR_NO_IDAQ" ] && ViashError Bad arguments for option \'-A\': \'$VIASH_PAR_NO_IDAQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_IDAQ=true + shift 1 + ;; + --del_baq) + [ -n "$VIASH_PAR_DEL_BAQ" ] && ViashError Bad arguments for option \'--del_baq\': \'$VIASH_PAR_DEL_BAQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEL_BAQ=true + shift 1 + ;; + -D) + [ -n "$VIASH_PAR_DEL_BAQ" ] && ViashError Bad arguments for option \'-D\': \'$VIASH_PAR_DEL_BAQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEL_BAQ=true + shift 1 + ;; + --no_ext_baq) + [ -n "$VIASH_PAR_NO_EXT_BAQ" ] && ViashError Bad arguments for option \'--no_ext_baq\': \'$VIASH_PAR_NO_EXT_BAQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_EXT_BAQ=true + shift 1 + ;; + -e) + [ -n "$VIASH_PAR_NO_EXT_BAQ" ] && ViashError Bad arguments for option \'-e\': \'$VIASH_PAR_NO_EXT_BAQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_EXT_BAQ=true + shift 1 + ;; + --min_mq) + [ -n "$VIASH_PAR_MIN_MQ" ] && ViashError Bad arguments for option \'--min_mq\': \'$VIASH_PAR_MIN_MQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_MQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_mq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_mq=*) + [ -n "$VIASH_PAR_MIN_MQ" ] && ViashError Bad arguments for option \'--min_mq=*\': \'$VIASH_PAR_MIN_MQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_MQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -m) + [ -n "$VIASH_PAR_MIN_MQ" ] && ViashError Bad arguments for option \'-m\': \'$VIASH_PAR_MIN_MQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_MQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -m. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_mq) + [ -n "$VIASH_PAR_MAX_MQ" ] && ViashError Bad arguments for option \'--max_mq\': \'$VIASH_PAR_MAX_MQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_MQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_mq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_mq=*) + [ -n "$VIASH_PAR_MAX_MQ" ] && ViashError Bad arguments for option \'--max_mq=*\': \'$VIASH_PAR_MAX_MQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_MQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -M) + [ -n "$VIASH_PAR_MAX_MQ" ] && ViashError Bad arguments for option \'-M\': \'$VIASH_PAR_MAX_MQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_MQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -M. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --no_mq) + [ -n "$VIASH_PAR_NO_MQ" ] && ViashError Bad arguments for option \'--no_mq\': \'$VIASH_PAR_NO_MQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_MQ=true + shift 1 + ;; + -N) + [ -n "$VIASH_PAR_NO_MQ" ] && ViashError Bad arguments for option \'-N\': \'$VIASH_PAR_NO_MQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_MQ=true + shift 1 + ;; + --call_indels) + [ -n "$VIASH_PAR_CALL_INDELS" ] && ViashError Bad arguments for option \'--call_indels\': \'$VIASH_PAR_CALL_INDELS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CALL_INDELS=true + shift 1 + ;; + --only_indels) + [ -n "$VIASH_PAR_ONLY_INDELS" ] && ViashError Bad arguments for option \'--only_indels\': \'$VIASH_PAR_ONLY_INDELS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ONLY_INDELS=true + shift 1 + ;; + --src_qual) + [ -n "$VIASH_PAR_SRC_QUAL" ] && ViashError Bad arguments for option \'--src_qual\': \'$VIASH_PAR_SRC_QUAL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SRC_QUAL=true + shift 1 + ;; + -s) + [ -n "$VIASH_PAR_SRC_QUAL" ] && ViashError Bad arguments for option \'-s\': \'$VIASH_PAR_SRC_QUAL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SRC_QUAL=true + shift 1 + ;; + --ign_vcf) + [ -n "$VIASH_PAR_IGN_VCF" ] && ViashError Bad arguments for option \'--ign_vcf\': \'$VIASH_PAR_IGN_VCF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IGN_VCF="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --ign_vcf. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ign_vcf=*) + [ -n "$VIASH_PAR_IGN_VCF" ] && ViashError Bad arguments for option \'--ign_vcf=*\': \'$VIASH_PAR_IGN_VCF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IGN_VCF=$(ViashRemoveFlags "$1") + shift 1 + ;; + -S) + [ -n "$VIASH_PAR_IGN_VCF" ] && ViashError Bad arguments for option \'-S\': \'$VIASH_PAR_IGN_VCF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IGN_VCF="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -S. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --def_nm_q) + [ -n "$VIASH_PAR_DEF_NM_Q" ] && ViashError Bad arguments for option \'--def_nm_q\': \'$VIASH_PAR_DEF_NM_Q\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEF_NM_Q="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --def_nm_q. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --def_nm_q=*) + [ -n "$VIASH_PAR_DEF_NM_Q" ] && ViashError Bad arguments for option \'--def_nm_q=*\': \'$VIASH_PAR_DEF_NM_Q\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEF_NM_Q=$(ViashRemoveFlags "$1") + shift 1 + ;; + -T) + [ -n "$VIASH_PAR_DEF_NM_Q" ] && ViashError Bad arguments for option \'-T\': \'$VIASH_PAR_DEF_NM_Q\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEF_NM_Q="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -T. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sig) + [ -n "$VIASH_PAR_SIG" ] && ViashError Bad arguments for option \'--sig\': \'$VIASH_PAR_SIG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SIG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sig. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sig=*) + [ -n "$VIASH_PAR_SIG" ] && ViashError Bad arguments for option \'--sig=*\': \'$VIASH_PAR_SIG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SIG=$(ViashRemoveFlags "$1") + shift 1 + ;; + -a) + [ -n "$VIASH_PAR_SIG" ] && ViashError Bad arguments for option \'-a\': \'$VIASH_PAR_SIG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SIG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -a. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bonf) + [ -n "$VIASH_PAR_BONF" ] && ViashError Bad arguments for option \'--bonf\': \'$VIASH_PAR_BONF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BONF="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bonf. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bonf=*) + [ -n "$VIASH_PAR_BONF" ] && ViashError Bad arguments for option \'--bonf=*\': \'$VIASH_PAR_BONF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BONF=$(ViashRemoveFlags "$1") + shift 1 + ;; + -b) + [ -n "$VIASH_PAR_BONF" ] && ViashError Bad arguments for option \'-b\': \'$VIASH_PAR_BONF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BONF="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -b. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_cov) + [ -n "$VIASH_PAR_MIN_COV" ] && ViashError Bad arguments for option \'--min_cov\': \'$VIASH_PAR_MIN_COV\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_COV="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_cov. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_cov=*) + [ -n "$VIASH_PAR_MIN_COV" ] && ViashError Bad arguments for option \'--min_cov=*\': \'$VIASH_PAR_MIN_COV\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_COV=$(ViashRemoveFlags "$1") + shift 1 + ;; + -C) + [ -n "$VIASH_PAR_MIN_COV" ] && ViashError Bad arguments for option \'-C\': \'$VIASH_PAR_MIN_COV\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_COV="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -C. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_depth) + [ -n "$VIASH_PAR_MAX_DEPTH" ] && ViashError Bad arguments for option \'--max_depth\': \'$VIASH_PAR_MAX_DEPTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_DEPTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_depth. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_depth=*) + [ -n "$VIASH_PAR_MAX_DEPTH" ] && ViashError Bad arguments for option \'--max_depth=*\': \'$VIASH_PAR_MAX_DEPTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_DEPTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -d) + [ -n "$VIASH_PAR_MAX_DEPTH" ] && ViashError Bad arguments for option \'-d\': \'$VIASH_PAR_MAX_DEPTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_DEPTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -d. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --illumina_13) + [ -n "$VIASH_PAR_ILLUMINA_13" ] && ViashError Bad arguments for option \'--illumina_13\': \'$VIASH_PAR_ILLUMINA_13\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ILLUMINA_13=true + shift 1 + ;; + --use_orphan) + [ -n "$VIASH_PAR_USE_ORPHAN" ] && ViashError Bad arguments for option \'--use_orphan\': \'$VIASH_PAR_USE_ORPHAN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_USE_ORPHAN=true + shift 1 + ;; + --plp_summary_only) + [ -n "$VIASH_PAR_PLP_SUMMARY_ONLY" ] && ViashError Bad arguments for option \'--plp_summary_only\': \'$VIASH_PAR_PLP_SUMMARY_ONLY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PLP_SUMMARY_ONLY=true + shift 1 + ;; + --no_default_filter) + [ -n "$VIASH_PAR_NO_DEFAULT_FILTER" ] && ViashError Bad arguments for option \'--no_default_filter\': \'$VIASH_PAR_NO_DEFAULT_FILTER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_DEFAULT_FILTER=true + shift 1 + ;; + --force_overwrite) + [ -n "$VIASH_PAR_FORCE_OVERWRITE" ] && ViashError Bad arguments for option \'--force_overwrite\': \'$VIASH_PAR_FORCE_OVERWRITE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORCE_OVERWRITE=true + shift 1 + ;; + --verbose) + [ -n "$VIASH_PAR_VERBOSE" ] && ViashError Bad arguments for option \'--verbose\': \'$VIASH_PAR_VERBOSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_VERBOSE=true + shift 1 + ;; + --debug) + [ -n "$VIASH_PAR_DEBUG" ] && ViashError Bad arguments for option \'--debug\': \'$VIASH_PAR_DEBUG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEBUG=true + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/lofreq/lofreq_call:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_INPUT_BAI+x} ]; then + ViashError '--input_bai' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_REF+x} ]; then + ViashError '--ref' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUT+x} ]; then + ViashError '--out' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_NO_BAQ+x} ]; then + VIASH_PAR_NO_BAQ="false" +fi +if [ -z ${VIASH_PAR_NO_IDAQ+x} ]; then + VIASH_PAR_NO_IDAQ="false" +fi +if [ -z ${VIASH_PAR_DEL_BAQ+x} ]; then + VIASH_PAR_DEL_BAQ="false" +fi +if [ -z ${VIASH_PAR_NO_EXT_BAQ+x} ]; then + VIASH_PAR_NO_EXT_BAQ="false" +fi +if [ -z ${VIASH_PAR_NO_MQ+x} ]; then + VIASH_PAR_NO_MQ="false" +fi +if [ -z ${VIASH_PAR_CALL_INDELS+x} ]; then + VIASH_PAR_CALL_INDELS="false" +fi +if [ -z ${VIASH_PAR_ONLY_INDELS+x} ]; then + VIASH_PAR_ONLY_INDELS="false" +fi +if [ -z ${VIASH_PAR_SRC_QUAL+x} ]; then + VIASH_PAR_SRC_QUAL="false" +fi +if [ -z ${VIASH_PAR_ILLUMINA_13+x} ]; then + VIASH_PAR_ILLUMINA_13="false" +fi +if [ -z ${VIASH_PAR_USE_ORPHAN+x} ]; then + VIASH_PAR_USE_ORPHAN="false" +fi +if [ -z ${VIASH_PAR_PLP_SUMMARY_ONLY+x} ]; then + VIASH_PAR_PLP_SUMMARY_ONLY="false" +fi +if [ -z ${VIASH_PAR_NO_DEFAULT_FILTER+x} ]; then + VIASH_PAR_NO_DEFAULT_FILTER="false" +fi +if [ -z ${VIASH_PAR_FORCE_OVERWRITE+x} ]; then + VIASH_PAR_FORCE_OVERWRITE="false" +fi +if [ -z ${VIASH_PAR_VERBOSE+x} ]; then + VIASH_PAR_VERBOSE="false" +fi +if [ -z ${VIASH_PAR_DEBUG+x} ]; then + VIASH_PAR_DEBUG="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_INPUT_BAI" ] && [ ! -e "$VIASH_PAR_INPUT_BAI" ]; then + ViashError "Input file '$VIASH_PAR_INPUT_BAI' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REF" ] && [ ! -e "$VIASH_PAR_REF" ]; then + ViashError "Input file '$VIASH_PAR_REF' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_BED" ] && [ ! -e "$VIASH_PAR_BED" ]; then + ViashError "Input file '$VIASH_PAR_BED' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_IGN_VCF" ] && [ ! -e "$VIASH_PAR_IGN_VCF" ]; then + ViashError "Input file '$VIASH_PAR_IGN_VCF' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_MIN_BQ" ]]; then + if ! [[ "$VIASH_PAR_MIN_BQ" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_bq' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_ALT_BQ" ]]; then + if ! [[ "$VIASH_PAR_MIN_ALT_BQ" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_alt_bq' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DEF_ALT_BQ" ]]; then + if ! [[ "$VIASH_PAR_DEF_ALT_BQ" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--def_alt_bq' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_JQ" ]]; then + if ! [[ "$VIASH_PAR_MIN_JQ" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_jq' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_ALT_JQ" ]]; then + if ! [[ "$VIASH_PAR_MIN_ALT_JQ" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_alt_jq' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DEF_ALT_JQ" ]]; then + if ! [[ "$VIASH_PAR_DEF_ALT_JQ" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--def_alt_jq' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_BAQ" ]]; then + if ! [[ "$VIASH_PAR_NO_BAQ" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_baq' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_IDAQ" ]]; then + if ! [[ "$VIASH_PAR_NO_IDAQ" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_idaq' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DEL_BAQ" ]]; then + if ! [[ "$VIASH_PAR_DEL_BAQ" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--del_baq' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_EXT_BAQ" ]]; then + if ! [[ "$VIASH_PAR_NO_EXT_BAQ" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_ext_baq' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_MQ" ]]; then + if ! [[ "$VIASH_PAR_MIN_MQ" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_mq' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_MQ" ]]; then + if ! [[ "$VIASH_PAR_MAX_MQ" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_mq' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_MQ" ]]; then + if ! [[ "$VIASH_PAR_NO_MQ" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_mq' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CALL_INDELS" ]]; then + if ! [[ "$VIASH_PAR_CALL_INDELS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--call_indels' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ONLY_INDELS" ]]; then + if ! [[ "$VIASH_PAR_ONLY_INDELS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--only_indels' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SRC_QUAL" ]]; then + if ! [[ "$VIASH_PAR_SRC_QUAL" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--src_qual' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DEF_NM_Q" ]]; then + if ! [[ "$VIASH_PAR_DEF_NM_Q" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--def_nm_q' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SIG" ]]; then + if ! [[ "$VIASH_PAR_SIG" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--sig' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_COV" ]]; then + if ! [[ "$VIASH_PAR_MIN_COV" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_cov' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_DEPTH" ]]; then + if ! [[ "$VIASH_PAR_MAX_DEPTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_depth' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ILLUMINA_13" ]]; then + if ! [[ "$VIASH_PAR_ILLUMINA_13" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--illumina_13' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_USE_ORPHAN" ]]; then + if ! [[ "$VIASH_PAR_USE_ORPHAN" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--use_orphan' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PLP_SUMMARY_ONLY" ]]; then + if ! [[ "$VIASH_PAR_PLP_SUMMARY_ONLY" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--plp_summary_only' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_DEFAULT_FILTER" ]]; then + if ! [[ "$VIASH_PAR_NO_DEFAULT_FILTER" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_default_filter' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FORCE_OVERWRITE" ]]; then + if ! [[ "$VIASH_PAR_FORCE_OVERWRITE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--force_overwrite' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_VERBOSE" ]]; then + if ! [[ "$VIASH_PAR_VERBOSE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--verbose' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DEBUG" ]]; then + if ! [[ "$VIASH_PAR_DEBUG" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--debug' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_INPUT_BAI" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT_BAI")" ) + VIASH_PAR_INPUT_BAI=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT_BAI") +fi +if [ ! -z "$VIASH_PAR_REF" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REF")" ) + VIASH_PAR_REF=$(ViashDockerAutodetectMount "$VIASH_PAR_REF") +fi +if [ ! -z "$VIASH_PAR_OUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUT")" ) + VIASH_PAR_OUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUT" ) +fi +if [ ! -z "$VIASH_PAR_BED" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_BED")" ) + VIASH_PAR_BED=$(ViashDockerAutodetectMount "$VIASH_PAR_BED") +fi +if [ ! -z "$VIASH_PAR_IGN_VCF" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_IGN_VCF")" ) + VIASH_PAR_IGN_VCF=$(ViashDockerAutodetectMount "$VIASH_PAR_IGN_VCF") +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-lofreq_call-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_BAI+x} ]; then echo "${VIASH_PAR_INPUT_BAI}" | sed "s#'#'\"'\"'#g;s#.*#par_input_bai='&'#" ; else echo "# par_input_bai="; fi ) +$( if [ ! -z ${VIASH_PAR_REF+x} ]; then echo "${VIASH_PAR_REF}" | sed "s#'#'\"'\"'#g;s#.*#par_ref='&'#" ; else echo "# par_ref="; fi ) +$( if [ ! -z ${VIASH_PAR_OUT+x} ]; then echo "${VIASH_PAR_OUT}" | sed "s#'#'\"'\"'#g;s#.*#par_out='&'#" ; else echo "# par_out="; fi ) +$( if [ ! -z ${VIASH_PAR_REGION+x} ]; then echo "${VIASH_PAR_REGION}" | sed "s#'#'\"'\"'#g;s#.*#par_region='&'#" ; else echo "# par_region="; fi ) +$( if [ ! -z ${VIASH_PAR_BED+x} ]; then echo "${VIASH_PAR_BED}" | sed "s#'#'\"'\"'#g;s#.*#par_bed='&'#" ; else echo "# par_bed="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_BQ+x} ]; then echo "${VIASH_PAR_MIN_BQ}" | sed "s#'#'\"'\"'#g;s#.*#par_min_bq='&'#" ; else echo "# par_min_bq="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ALT_BQ+x} ]; then echo "${VIASH_PAR_MIN_ALT_BQ}" | sed "s#'#'\"'\"'#g;s#.*#par_min_alt_bq='&'#" ; else echo "# par_min_alt_bq="; fi ) +$( if [ ! -z ${VIASH_PAR_DEF_ALT_BQ+x} ]; then echo "${VIASH_PAR_DEF_ALT_BQ}" | sed "s#'#'\"'\"'#g;s#.*#par_def_alt_bq='&'#" ; else echo "# par_def_alt_bq="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_JQ+x} ]; then echo "${VIASH_PAR_MIN_JQ}" | sed "s#'#'\"'\"'#g;s#.*#par_min_jq='&'#" ; else echo "# par_min_jq="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ALT_JQ+x} ]; then echo "${VIASH_PAR_MIN_ALT_JQ}" | sed "s#'#'\"'\"'#g;s#.*#par_min_alt_jq='&'#" ; else echo "# par_min_alt_jq="; fi ) +$( if [ ! -z ${VIASH_PAR_DEF_ALT_JQ+x} ]; then echo "${VIASH_PAR_DEF_ALT_JQ}" | sed "s#'#'\"'\"'#g;s#.*#par_def_alt_jq='&'#" ; else echo "# par_def_alt_jq="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_BAQ+x} ]; then echo "${VIASH_PAR_NO_BAQ}" | sed "s#'#'\"'\"'#g;s#.*#par_no_baq='&'#" ; else echo "# par_no_baq="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_IDAQ+x} ]; then echo "${VIASH_PAR_NO_IDAQ}" | sed "s#'#'\"'\"'#g;s#.*#par_no_idaq='&'#" ; else echo "# par_no_idaq="; fi ) +$( if [ ! -z ${VIASH_PAR_DEL_BAQ+x} ]; then echo "${VIASH_PAR_DEL_BAQ}" | sed "s#'#'\"'\"'#g;s#.*#par_del_baq='&'#" ; else echo "# par_del_baq="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_EXT_BAQ+x} ]; then echo "${VIASH_PAR_NO_EXT_BAQ}" | sed "s#'#'\"'\"'#g;s#.*#par_no_ext_baq='&'#" ; else echo "# par_no_ext_baq="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_MQ+x} ]; then echo "${VIASH_PAR_MIN_MQ}" | sed "s#'#'\"'\"'#g;s#.*#par_min_mq='&'#" ; else echo "# par_min_mq="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_MQ+x} ]; then echo "${VIASH_PAR_MAX_MQ}" | sed "s#'#'\"'\"'#g;s#.*#par_max_mq='&'#" ; else echo "# par_max_mq="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_MQ+x} ]; then echo "${VIASH_PAR_NO_MQ}" | sed "s#'#'\"'\"'#g;s#.*#par_no_mq='&'#" ; else echo "# par_no_mq="; fi ) +$( if [ ! -z ${VIASH_PAR_CALL_INDELS+x} ]; then echo "${VIASH_PAR_CALL_INDELS}" | sed "s#'#'\"'\"'#g;s#.*#par_call_indels='&'#" ; else echo "# par_call_indels="; fi ) +$( if [ ! -z ${VIASH_PAR_ONLY_INDELS+x} ]; then echo "${VIASH_PAR_ONLY_INDELS}" | sed "s#'#'\"'\"'#g;s#.*#par_only_indels='&'#" ; else echo "# par_only_indels="; fi ) +$( if [ ! -z ${VIASH_PAR_SRC_QUAL+x} ]; then echo "${VIASH_PAR_SRC_QUAL}" | sed "s#'#'\"'\"'#g;s#.*#par_src_qual='&'#" ; else echo "# par_src_qual="; fi ) +$( if [ ! -z ${VIASH_PAR_IGN_VCF+x} ]; then echo "${VIASH_PAR_IGN_VCF}" | sed "s#'#'\"'\"'#g;s#.*#par_ign_vcf='&'#" ; else echo "# par_ign_vcf="; fi ) +$( if [ ! -z ${VIASH_PAR_DEF_NM_Q+x} ]; then echo "${VIASH_PAR_DEF_NM_Q}" | sed "s#'#'\"'\"'#g;s#.*#par_def_nm_q='&'#" ; else echo "# par_def_nm_q="; fi ) +$( if [ ! -z ${VIASH_PAR_SIG+x} ]; then echo "${VIASH_PAR_SIG}" | sed "s#'#'\"'\"'#g;s#.*#par_sig='&'#" ; else echo "# par_sig="; fi ) +$( if [ ! -z ${VIASH_PAR_BONF+x} ]; then echo "${VIASH_PAR_BONF}" | sed "s#'#'\"'\"'#g;s#.*#par_bonf='&'#" ; else echo "# par_bonf="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_COV+x} ]; then echo "${VIASH_PAR_MIN_COV}" | sed "s#'#'\"'\"'#g;s#.*#par_min_cov='&'#" ; else echo "# par_min_cov="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_DEPTH+x} ]; then echo "${VIASH_PAR_MAX_DEPTH}" | sed "s#'#'\"'\"'#g;s#.*#par_max_depth='&'#" ; else echo "# par_max_depth="; fi ) +$( if [ ! -z ${VIASH_PAR_ILLUMINA_13+x} ]; then echo "${VIASH_PAR_ILLUMINA_13}" | sed "s#'#'\"'\"'#g;s#.*#par_illumina_13='&'#" ; else echo "# par_illumina_13="; fi ) +$( if [ ! -z ${VIASH_PAR_USE_ORPHAN+x} ]; then echo "${VIASH_PAR_USE_ORPHAN}" | sed "s#'#'\"'\"'#g;s#.*#par_use_orphan='&'#" ; else echo "# par_use_orphan="; fi ) +$( if [ ! -z ${VIASH_PAR_PLP_SUMMARY_ONLY+x} ]; then echo "${VIASH_PAR_PLP_SUMMARY_ONLY}" | sed "s#'#'\"'\"'#g;s#.*#par_plp_summary_only='&'#" ; else echo "# par_plp_summary_only="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_DEFAULT_FILTER+x} ]; then echo "${VIASH_PAR_NO_DEFAULT_FILTER}" | sed "s#'#'\"'\"'#g;s#.*#par_no_default_filter='&'#" ; else echo "# par_no_default_filter="; fi ) +$( if [ ! -z ${VIASH_PAR_FORCE_OVERWRITE+x} ]; then echo "${VIASH_PAR_FORCE_OVERWRITE}" | sed "s#'#'\"'\"'#g;s#.*#par_force_overwrite='&'#" ; else echo "# par_force_overwrite="; fi ) +$( if [ ! -z ${VIASH_PAR_VERBOSE+x} ]; then echo "${VIASH_PAR_VERBOSE}" | sed "s#'#'\"'\"'#g;s#.*#par_verbose='&'#" ; else echo "# par_verbose="; fi ) +$( if [ ! -z ${VIASH_PAR_DEBUG+x} ]; then echo "${VIASH_PAR_DEBUG}" | sed "s#'#'\"'\"'#g;s#.*#par_debug='&'#" ; else echo "# par_debug="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# Unset all parameters that are set to "false" +[[ "\$par_no_baq" == "false" ]] && unset par_no_baq +[[ "\$par_no_idaq" == "false" ]] && unset par_no_idaq +[[ "\$par_del_baq" == "false" ]] && unset par_del_baq +[[ "\$par_no_ext_baq" == "false" ]] && unset par_no_ext_baq +[[ "\$par_no_mq" == "false" ]] && unset par_no_mq +[[ "\$par_call_indels" == "false" ]] && unset par_call_indels +[[ "\$par_only_indels" == "false" ]] && unset par_only_indels +[[ "\$par_src_qual" == "false" ]] && unset par_src_qual +[[ "\$par_illumina_13" == "false" ]] && unset par_illumina_13 +[[ "\$par_use_orphan" == "false" ]] && unset par_use_orphan +[[ "\$par_plp_summary_only" == "false" ]] && unset par_plp_summary_only +[[ "\$par_no_default_filter" == "false" ]] && unset par_no_default_filter +[[ "\$par_force_overwrite" == "false" ]] && unset par_force_overwrite +[[ "\$par_verbose" == "false" ]] && unset par_verbose +[[ "\$par_debug" == "false" ]] && unset par_debug + +# Run lofreq call +lofreq call \\ + -f "\$par_ref" \\ + -o "\$par_out" \\ + \${par_region:+-r "\${par_region}"} \\ + \${par_bed:+-l "\${par_bed}"} \\ + \${par_min_bq:+-q "\${par_min_bq}"} \\ + \${par_min_alt_bq:+-Q "\${par_min_alt_bq}"} \\ + \${par_def_alt_bq:+-R "\${par_def_alt_bq}"} \\ + \${par_min_jq:+-j "\${par_min_jq}"} \\ + \${par_alt_jq:+-K "\${par_alt_jq}"} \\ + \${par_no_baq:+-B} \\ + \${par_no_idaq:+-A} \\ + \${par_del_baq:+-D} \\ + \${par_no_ext_baq:+-e} \\ + \${par_min_mq:+-m "\${par_min_mq}"} \\ + \${par_max_mq:+-M "\${par_max_mq}"} \\ + \${par_no_mq:+-N} \\ + \${par_call_indels:+--call-indels} \\ + \${par_only_indels:+--only-indels} \\ + \${par_src_qual:+-s} \\ + \${par_ign_vcf:+-S "\${par_ign_vcf}"} \\ + \${par_def_nm_q:+-T "\${par_def_nm_q}"} \\ + \${par_sig:+-a "\${par_sig}"} \\ + \${par_bonf:+-b "\${par_bonf}"} \\ + \${par_min_cov:+-C "\${par_min_cov}"} \\ + \${par_max_depth:+-d "\${par_max_depth}"} \\ + \${par_illumina_13:+--illumina-1.3} \\ + \${par_use_orphan:+--use-orphan} \\ + \${par_plp_summary_only:+--plp-summary-only} \\ + \${par_no_default_filter:+--no-default-filter} \\ + \${par_force_overwrite:+--force-overwrite} \\ + \${par_verbose:+--verbose} \\ + \${par_debug:+--debug} \\ + "\$par_input" +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_INPUT_BAI" ]; then + VIASH_PAR_INPUT_BAI=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT_BAI") + fi + if [ ! -z "$VIASH_PAR_REF" ]; then + VIASH_PAR_REF=$(ViashDockerStripAutomount "$VIASH_PAR_REF") + fi + if [ ! -z "$VIASH_PAR_OUT" ]; then + VIASH_PAR_OUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUT") + fi + if [ ! -z "$VIASH_PAR_BED" ]; then + VIASH_PAR_BED=$(ViashDockerStripAutomount "$VIASH_PAR_BED") + fi + if [ ! -z "$VIASH_PAR_IGN_VCF" ]; then + VIASH_PAR_IGN_VCF=$(ViashDockerStripAutomount "$VIASH_PAR_IGN_VCF") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUT" ] && [ ! -e "$VIASH_PAR_OUT" ]; then + ViashError "Output file '$VIASH_PAR_OUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/lofreq/lofreq_indelqual/.config.vsh.yaml b/target/executable/lofreq/lofreq_indelqual/.config.vsh.yaml new file mode 100644 index 00000000..ff268c44 --- /dev/null +++ b/target/executable/lofreq/lofreq_indelqual/.config.vsh.yaml @@ -0,0 +1,223 @@ +name: "lofreq_indelqual" +namespace: "lofreq" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "Input BAM file.\n" + info: null + example: + - "normal.bam" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--ref" + alternatives: + - "-f" + description: "Reference sequence used for mapping (Only required for --dindel).\n" + info: null + example: + - "reference.fasta" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--out" + alternatives: + - "-o" + description: "Output BAM file.\n" + info: null + example: + - "output.bam" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Arguments" + arguments: + - type: "string" + name: "--uniform" + alternatives: + - "-u" + description: "Add this indel quality uniformly to all bases. Use two comma separated\ + \ values to specify insertion and deletion quality separately. (clashes with\ + \ --dindel).\n" + info: null + example: + - "50,50" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--dindel" + description: "Add Dindel's indel qualities (Illumina specific) (clashes with -u;\ + \ needs --ref).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--verbose" + description: "Be verbose.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Insert indel qualities into BAM file (required for indel predictions).\n\ + \nThe preferred way of inserting indel qualities should be via GATK's BQSR (>=2)\ + \ If that's not possible, use this subcommand.\nThe command has two modes: 'uniform'\ + \ and 'dindel':\n- 'uniform' will assign a given value uniformly, whereas\n- 'dindel'\ + \ will insert indel qualities based on Dindel (PMID 20980555).\nBoth will overwrite\ + \ any existing values.\nDo not realign your BAM file afterwards!\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "bam" +- "indel" +- "qualities" +- "indelqual" +- "lofreq" +- "lofreq/indelqual" +license: "MIT" +references: + doi: + - "10.1093/nar/gks918" +links: + repository: "https://github.com/viash-hub/biobox" + homepage: "https://csb5.github.io/lofreq/" + documentation: "https://csb5.github.io/lofreq/commands/" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/lofreq:2.1.5--py38h794fc9e_10" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "version=$(lofreq version | grep 'version' | sed 's/version: //') && \\\necho\ + \ \"lofreq: $version\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/lofreq/indelqual/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/lofreq/lofreq_indelqual" + executable: "target/executable/lofreq/lofreq_indelqual/lofreq_indelqual" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/lofreq/lofreq_indelqual/lofreq_indelqual b/target/executable/lofreq/lofreq_indelqual/lofreq_indelqual new file mode 100755 index 00000000..ea48dc2d --- /dev/null +++ b/target/executable/lofreq/lofreq_indelqual/lofreq_indelqual @@ -0,0 +1,1174 @@ +#!/usr/bin/env bash + +# lofreq_indelqual main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="lofreq_indelqual" +VIASH_META_FUNCTIONALITY_NAME="lofreq_indelqual" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "lofreq_indelqual main" + echo "" + echo "Insert indel qualities into BAM file (required for indel predictions)." + echo "" + echo "The preferred way of inserting indel qualities should be via GATK's BQSR (>=2)" + echo "If that's not possible, use this subcommand." + echo "The command has two modes: 'uniform' and 'dindel':" + echo "- 'uniform' will assign a given value uniformly, whereas" + echo "- 'dindel' will insert indel qualities based on Dindel (PMID 20980555)." + echo "Both will overwrite any existing values." + echo "Do not realign your BAM file afterwards!" + echo "" + echo "Inputs:" + echo " --input" + echo " type: file, required parameter, file must exist" + echo " example: normal.bam" + echo " Input BAM file." + echo "" + echo " -f, --ref" + echo " type: file, file must exist" + echo " example: reference.fasta" + echo " Reference sequence used for mapping (Only required for --dindel)." + echo "" + echo "Outputs:" + echo " -o, --out" + echo " type: file, required parameter, output, file must exist" + echo " example: output.bam" + echo " Output BAM file." + echo "" + echo "Arguments:" + echo " -u, --uniform" + echo " type: string" + echo " example: 50,50" + echo " Add this indel quality uniformly to all bases. Use two comma separated" + echo " values to specify insertion and deletion quality separately. (clashes" + echo " with --dindel)." + echo "" + echo " --dindel" + echo " type: boolean_true" + echo " Add Dindel's indel qualities (Illumina specific) (clashes with -u; needs" + echo " --ref)." + echo "" + echo " --verbose" + echo " type: boolean_true" + echo " Be verbose." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/lofreq:2.1.5--py38h794fc9e_10 +ENTRYPOINT [] +RUN version=$(lofreq version | grep 'version' | sed 's/version: //') && \ +echo "lofreq: $version" > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component lofreq lofreq_indelqual" +LABEL org.opencontainers.image.created="2024-06-24T08:36:46Z" +LABEL org.opencontainers.image.source="https://github.com/viash-hub/biobox" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "lofreq_indelqual main" + exit + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --ref) + [ -n "$VIASH_PAR_REF" ] && ViashError Bad arguments for option \'--ref\': \'$VIASH_PAR_REF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --ref. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ref=*) + [ -n "$VIASH_PAR_REF" ] && ViashError Bad arguments for option \'--ref=*\': \'$VIASH_PAR_REF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF=$(ViashRemoveFlags "$1") + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_REF" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_REF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -f. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --out) + [ -n "$VIASH_PAR_OUT" ] && ViashError Bad arguments for option \'--out\': \'$VIASH_PAR_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --out. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --out=*) + [ -n "$VIASH_PAR_OUT" ] && ViashError Bad arguments for option \'--out=*\': \'$VIASH_PAR_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUT" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --uniform) + [ -n "$VIASH_PAR_UNIFORM" ] && ViashError Bad arguments for option \'--uniform\': \'$VIASH_PAR_UNIFORM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNIFORM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --uniform. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --uniform=*) + [ -n "$VIASH_PAR_UNIFORM" ] && ViashError Bad arguments for option \'--uniform=*\': \'$VIASH_PAR_UNIFORM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNIFORM=$(ViashRemoveFlags "$1") + shift 1 + ;; + -u) + [ -n "$VIASH_PAR_UNIFORM" ] && ViashError Bad arguments for option \'-u\': \'$VIASH_PAR_UNIFORM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNIFORM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -u. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --dindel) + [ -n "$VIASH_PAR_DINDEL" ] && ViashError Bad arguments for option \'--dindel\': \'$VIASH_PAR_DINDEL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DINDEL=true + shift 1 + ;; + --verbose) + [ -n "$VIASH_PAR_VERBOSE" ] && ViashError Bad arguments for option \'--verbose\': \'$VIASH_PAR_VERBOSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_VERBOSE=true + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/lofreq/lofreq_indelqual:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUT+x} ]; then + ViashError '--out' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_DINDEL+x} ]; then + VIASH_PAR_DINDEL="false" +fi +if [ -z ${VIASH_PAR_VERBOSE+x} ]; then + VIASH_PAR_VERBOSE="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REF" ] && [ ! -e "$VIASH_PAR_REF" ]; then + ViashError "Input file '$VIASH_PAR_REF' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_DINDEL" ]]; then + if ! [[ "$VIASH_PAR_DINDEL" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--dindel' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_VERBOSE" ]]; then + if ! [[ "$VIASH_PAR_VERBOSE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--verbose' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_REF" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REF")" ) + VIASH_PAR_REF=$(ViashDockerAutodetectMount "$VIASH_PAR_REF") +fi +if [ ! -z "$VIASH_PAR_OUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUT")" ) + VIASH_PAR_OUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUT" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-lofreq_indelqual-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_REF+x} ]; then echo "${VIASH_PAR_REF}" | sed "s#'#'\"'\"'#g;s#.*#par_ref='&'#" ; else echo "# par_ref="; fi ) +$( if [ ! -z ${VIASH_PAR_OUT+x} ]; then echo "${VIASH_PAR_OUT}" | sed "s#'#'\"'\"'#g;s#.*#par_out='&'#" ; else echo "# par_out="; fi ) +$( if [ ! -z ${VIASH_PAR_UNIFORM+x} ]; then echo "${VIASH_PAR_UNIFORM}" | sed "s#'#'\"'\"'#g;s#.*#par_uniform='&'#" ; else echo "# par_uniform="; fi ) +$( if [ ! -z ${VIASH_PAR_DINDEL+x} ]; then echo "${VIASH_PAR_DINDEL}" | sed "s#'#'\"'\"'#g;s#.*#par_dindel='&'#" ; else echo "# par_dindel="; fi ) +$( if [ ! -z ${VIASH_PAR_VERBOSE+x} ]; then echo "${VIASH_PAR_VERBOSE}" | sed "s#'#'\"'\"'#g;s#.*#par_verbose='&'#" ; else echo "# par_verbose="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# Unset all parameters that are set to "false" +[[ "\$par_dindel" == "false" ]] && unset par_dindel +[[ "\$par_verbose" == "false" ]] && unset par_verbose + +# run lofreq indelqual +lofreq indelqual \\ + -o "\$par_out" \\ + \${par_uniform:+-u "\${par_uniform}"} \\ + \${par_dindel:+--dindel} \\ + \${par_ref:+-f "\${par_ref}"} \\ + \${par_verbose:+--verbose} \\ + "\$par_input" +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_REF" ]; then + VIASH_PAR_REF=$(ViashDockerStripAutomount "$VIASH_PAR_REF") + fi + if [ ! -z "$VIASH_PAR_OUT" ]; then + VIASH_PAR_OUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUT") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUT" ] && [ ! -e "$VIASH_PAR_OUT" ]; then + ViashError "Output file '$VIASH_PAR_OUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/multiqc/.config.vsh.yaml b/target/executable/multiqc/.config.vsh.yaml new file mode 100644 index 00000000..847d7d67 --- /dev/null +++ b/target/executable/multiqc/.config.vsh.yaml @@ -0,0 +1,464 @@ +name: "multiqc" +version: "main" +argument_groups: +- name: "Input" + arguments: + - type: "file" + name: "--input" + description: "File paths to be searched for analysis results to be included in\ + \ the report.\n" + info: null + example: + - "data/results" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Ouput" + arguments: + - type: "file" + name: "--output_report" + description: "Filepath of the generated report.\n" + info: null + example: + - "multiqc_report.html" + must_exist: false + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--output_data" + description: "Output directory for parsed data files. If not provided, parsed\ + \ data will not be published.\n" + info: null + example: + - "multiqc_data" + must_exist: false + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--output_plots" + description: "Output directory for generated plots. If not provided, plots will\ + \ not be published.\n" + info: null + example: + - "multiqc_plots" + must_exist: false + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Modules and analyses to run" + arguments: + - type: "string" + name: "--include_modules" + description: "Use only these module" + info: null + example: + - "fastqc,cutadapt" + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "string" + name: "--exclude_modules" + description: "Do not use only these modules" + info: null + example: + - "fastqc,cutadapt" + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "string" + name: "--ignore_analysis" + info: null + example: + - "run_one/*,run_two/*" + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "string" + name: "--ignore_samples" + info: null + example: + - "sample_1*,sample_3*" + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "boolean_true" + name: "--ignore_symlinks" + description: "Ignore symlinked directories and files" + info: null + direction: "input" +- name: "Sample name handling" + arguments: + - type: "boolean_true" + name: "--dirs" + description: "Prepend directory to sample names to avoid clashing filenames" + info: null + direction: "input" + - type: "integer" + name: "--dirs_depth" + description: "Prepend n directories to sample names. Negative number to take from\ + \ start of path." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--full_names" + description: "Do not clean the sample names (leave as full file name)" + info: null + direction: "input" + - type: "boolean_true" + name: "--fn_as_s_name" + description: "Use the log filename as the sample name" + info: null + direction: "input" + - type: "file" + name: "--replace_names" + description: "TSV file to rename sample names during report generation" + info: null + example: + - "replace_names.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Report Customisation" + arguments: + - type: "string" + name: "--title" + description: "Report title. Printed as page header, used for filename if not otherwise\ + \ specified.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--comment" + description: "Custom comment, will be printed at the top of the report.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--template" + description: "Report template to use.\n" + info: null + required: false + choices: + - "default" + - "gathered" + - "geo" + - "highcharts" + - "sections" + - "simple" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--sample_names" + description: "TSV file containing alternative sample names for renaming buttons\ + \ in the report.\n" + info: null + example: + - "sample_names.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--sample_filters" + description: "TSV file containing show/hide patterns for the report\n" + info: null + example: + - "sample_filters.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--custom_css_file" + description: "Custom CSS file to add to the final report\n" + info: null + example: + - "custom_style_sheet.css" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--profile_runtime" + description: "Add analysis of how long MultiQC takes to run to the report\n" + info: null + direction: "input" +- name: "MultiQC behaviour" + arguments: + - type: "boolean_true" + name: "--verbose" + description: "Increase output verbosity.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--quiet" + description: "Only show log warnings\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--strict" + description: "Don't catch exceptions, run additional code checks to help development.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--development" + description: "Development mode. Do not compress and minimise JS, export uncompressed\ + \ plot data.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--require_logs" + description: "Require all explicitly requested modules to have log files. If not,\ + \ MultiQC will exit with an error.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_megaqc_upload" + description: "Don't upload generated report to MegaQC, even if MegaQC options\ + \ are found.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_ansi" + description: "Disable coloured log output.\n" + info: null + direction: "input" + - type: "string" + name: "--cl_config" + description: "YAML formatted string that allows to customize MultiQC behaviour\ + \ like input file detection.\n" + info: null + example: + - "qualimap_config: { general_stats_coverage: [20,40,200] }" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output format" + arguments: + - type: "boolean_true" + name: "--flat" + description: "Use only flat plots (static images).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--interactive" + description: "Use only interactive plots (in-browser Javascript).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--data_dir" + description: "Force the parsed data directory to be created.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_data_dir" + description: "Prevent the parsed data directory from being created.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--zip_data_dir" + description: "Compress the data directory.\n" + info: null + direction: "input" + - type: "string" + name: "--data_format" + description: "Output parsed data in a different format than the default 'txt'.\n" + info: null + required: false + choices: + - "tsv" + - "csv" + - "json" + - "yaml" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--pdf" + description: "Creates PDF report with the 'simple' template. Requires Pandoc to\ + \ be installed.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "MultiQC aggregates results from bioinformatics analyses across many\ + \ samples into a single report.\nIt searches a given directory for analysis logs\ + \ and compiles a HTML report. It's a general use tool, perfect for summarising the\ + \ output from numerous bioinformatics tools.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: + keywords: + - "QC" + - "html report" + - "aggregate analysis" + links: + homepage: "https://multiqc.info/" + documentation: "https://multiqc.info/docs/" + repository: "https://github.com/MultiQC/MultiQC" + references: + doi: "10.1093/bioinformatics/btw354" + licence: "GPL v3 or later" +status: "enabled" +requirements: + commands: + - "ps" +license: "MIT" +links: + repository: "https://github.com/viash-hub/biobox" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/multiqc:1.21--pyhdfd78af_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "multiqc --version | sed 's/multiqc, version\\s\\(.*\\)/multiqc: \"\\1\"/' >\ + \ /var/software_versions.txt\n" + test_setup: + - type: "apt" + packages: + - "jq" + interactive: false + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/multiqc/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/multiqc" + executable: "target/executable/multiqc/multiqc" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/multiqc/multiqc b/target/executable/multiqc/multiqc new file mode 100755 index 00000000..f2859389 --- /dev/null +++ b/target/executable/multiqc/multiqc @@ -0,0 +1,1945 @@ +#!/usr/bin/env bash + +# multiqc main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="multiqc" +VIASH_META_FUNCTIONALITY_NAME="multiqc" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "multiqc main" + echo "" + echo "MultiQC aggregates results from bioinformatics analyses across many samples into" + echo "a single report." + echo "It searches a given directory for analysis logs and compiles a HTML report. It's" + echo "a general use tool, perfect for summarising the output from numerous" + echo "bioinformatics tools." + echo "" + echo "Input:" + echo " --input" + echo " type: file, required parameter, multiple values allowed, file must exist" + echo " example: data/results" + echo " File paths to be searched for analysis results to be included in the" + echo " report." + echo "" + echo "Ouput:" + echo " --output_report" + echo " type: file, output" + echo " example: multiqc_report.html" + echo " Filepath of the generated report." + echo "" + echo " --output_data" + echo " type: file, output" + echo " example: multiqc_data" + echo " Output directory for parsed data files. If not provided, parsed data" + echo " will not be published." + echo "" + echo " --output_plots" + echo " type: file, output" + echo " example: multiqc_plots" + echo " Output directory for generated plots. If not provided, plots will not be" + echo " published." + echo "" + echo "Modules and analyses to run:" + echo " --include_modules" + echo " type: string, multiple values allowed" + echo " example: fastqc,cutadapt" + echo " Use only these module" + echo "" + echo " --exclude_modules" + echo " type: string, multiple values allowed" + echo " example: fastqc,cutadapt" + echo " Do not use only these modules" + echo "" + echo " --ignore_analysis" + echo " type: string, multiple values allowed" + echo " example: run_one/*,run_two/*" + echo "" + echo " --ignore_samples" + echo " type: string, multiple values allowed" + echo " example: sample_1*,sample_3*" + echo "" + echo " --ignore_symlinks" + echo " type: boolean_true" + echo " Ignore symlinked directories and files" + echo "" + echo "Sample name handling:" + echo " --dirs" + echo " type: boolean_true" + echo " Prepend directory to sample names to avoid clashing filenames" + echo "" + echo " --dirs_depth" + echo " type: integer" + echo " Prepend n directories to sample names. Negative number to take from" + echo " start of path." + echo "" + echo " --full_names" + echo " type: boolean_true" + echo " Do not clean the sample names (leave as full file name)" + echo "" + echo " --fn_as_s_name" + echo " type: boolean_true" + echo " Use the log filename as the sample name" + echo "" + echo " --replace_names" + echo " type: file, file must exist" + echo " example: replace_names.tsv" + echo " TSV file to rename sample names during report generation" + echo "" + echo "Report Customisation:" + echo " --title" + echo " type: string" + echo " Report title. Printed as page header, used for filename if not otherwise" + echo " specified." + echo "" + echo " --comment" + echo " type: string" + echo " Custom comment, will be printed at the top of the report." + echo "" + echo " --template" + echo " type: string" + echo " choices: [ default, gathered, geo, highcharts, sections, simple ]" + echo " Report template to use." + echo "" + echo " --sample_names" + echo " type: file, file must exist" + echo " example: sample_names.tsv" + echo " TSV file containing alternative sample names for renaming buttons in the" + echo " report." + echo "" + echo " --sample_filters" + echo " type: file, file must exist" + echo " example: sample_filters.tsv" + echo " TSV file containing show/hide patterns for the report" + echo "" + echo " --custom_css_file" + echo " type: file, file must exist" + echo " example: custom_style_sheet.css" + echo " Custom CSS file to add to the final report" + echo "" + echo " --profile_runtime" + echo " type: boolean_true" + echo " Add analysis of how long MultiQC takes to run to the report" + echo "" + echo "MultiQC behaviour:" + echo " --verbose" + echo " type: boolean_true" + echo " Increase output verbosity." + echo "" + echo " --quiet" + echo " type: boolean_true" + echo " Only show log warnings" + echo "" + echo " --strict" + echo " type: boolean_true" + echo " Don't catch exceptions, run additional code checks to help development." + echo "" + echo " --development" + echo " type: boolean_true" + echo " Development mode. Do not compress and minimise JS, export uncompressed" + echo " plot data." + echo "" + echo " --require_logs" + echo " type: boolean_true" + echo " Require all explicitly requested modules to have log files. If not," + echo " MultiQC will exit with an error." + echo "" + echo " --no_megaqc_upload" + echo " type: boolean_true" + echo " Don't upload generated report to MegaQC, even if MegaQC options are" + echo " found." + echo "" + echo " --no_ansi" + echo " type: boolean_true" + echo " Disable coloured log output." + echo "" + echo " --cl_config" + echo " type: string" + echo " example: qualimap_config: { general_stats_coverage: [20,40,200] }" + echo " YAML formatted string that allows to customize MultiQC behaviour like" + echo " input file detection." + echo "" + echo "Output format:" + echo " --flat" + echo " type: boolean_true" + echo " Use only flat plots (static images)." + echo "" + echo " --interactive" + echo " type: boolean_true" + echo " Use only interactive plots (in-browser Javascript)." + echo "" + echo " --data_dir" + echo " type: boolean_true" + echo " Force the parsed data directory to be created." + echo "" + echo " --no_data_dir" + echo " type: boolean_true" + echo " Prevent the parsed data directory from being created." + echo "" + echo " --zip_data_dir" + echo " type: boolean_true" + echo " Compress the data directory." + echo "" + echo " --data_format" + echo " type: string" + echo " choices: [ tsv, csv, json, yaml ]" + echo " Output parsed data in a different format than the default 'txt'." + echo "" + echo " --pdf" + echo " type: boolean_true" + echo " Creates PDF report with the 'simple' template. Requires Pandoc to be" + echo " installed." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/multiqc:1.21--pyhdfd78af_0 +ENTRYPOINT [] +RUN multiqc --version | sed 's/multiqc, version\s\(.*\)/multiqc: "\1"/' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component multiqc" +LABEL org.opencontainers.image.created="2024-06-24T08:36:38Z" +LABEL org.opencontainers.image.source="https://github.com/viash-hub/biobox" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "multiqc main" + exit + ;; + --input) + if [ -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT="$2" + else + VIASH_PAR_INPUT="$VIASH_PAR_INPUT;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + if [ -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + else + VIASH_PAR_INPUT="$VIASH_PAR_INPUT;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --output_report) + [ -n "$VIASH_PAR_OUTPUT_REPORT" ] && ViashError Bad arguments for option \'--output_report\': \'$VIASH_PAR_OUTPUT_REPORT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_REPORT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_report. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_report=*) + [ -n "$VIASH_PAR_OUTPUT_REPORT" ] && ViashError Bad arguments for option \'--output_report=*\': \'$VIASH_PAR_OUTPUT_REPORT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_REPORT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output_data) + [ -n "$VIASH_PAR_OUTPUT_DATA" ] && ViashError Bad arguments for option \'--output_data\': \'$VIASH_PAR_OUTPUT_DATA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_DATA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_data. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_data=*) + [ -n "$VIASH_PAR_OUTPUT_DATA" ] && ViashError Bad arguments for option \'--output_data=*\': \'$VIASH_PAR_OUTPUT_DATA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_DATA=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output_plots) + [ -n "$VIASH_PAR_OUTPUT_PLOTS" ] && ViashError Bad arguments for option \'--output_plots\': \'$VIASH_PAR_OUTPUT_PLOTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_PLOTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_plots. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_plots=*) + [ -n "$VIASH_PAR_OUTPUT_PLOTS" ] && ViashError Bad arguments for option \'--output_plots=*\': \'$VIASH_PAR_OUTPUT_PLOTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_PLOTS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --include_modules) + if [ -z "$VIASH_PAR_INCLUDE_MODULES" ]; then + VIASH_PAR_INCLUDE_MODULES="$2" + else + VIASH_PAR_INCLUDE_MODULES="$VIASH_PAR_INCLUDE_MODULES,""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --include_modules. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --include_modules=*) + if [ -z "$VIASH_PAR_INCLUDE_MODULES" ]; then + VIASH_PAR_INCLUDE_MODULES=$(ViashRemoveFlags "$1") + else + VIASH_PAR_INCLUDE_MODULES="$VIASH_PAR_INCLUDE_MODULES,"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --exclude_modules) + if [ -z "$VIASH_PAR_EXCLUDE_MODULES" ]; then + VIASH_PAR_EXCLUDE_MODULES="$2" + else + VIASH_PAR_EXCLUDE_MODULES="$VIASH_PAR_EXCLUDE_MODULES,""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --exclude_modules. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --exclude_modules=*) + if [ -z "$VIASH_PAR_EXCLUDE_MODULES" ]; then + VIASH_PAR_EXCLUDE_MODULES=$(ViashRemoveFlags "$1") + else + VIASH_PAR_EXCLUDE_MODULES="$VIASH_PAR_EXCLUDE_MODULES,"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --ignore_analysis) + if [ -z "$VIASH_PAR_IGNORE_ANALYSIS" ]; then + VIASH_PAR_IGNORE_ANALYSIS="$2" + else + VIASH_PAR_IGNORE_ANALYSIS="$VIASH_PAR_IGNORE_ANALYSIS,""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --ignore_analysis. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ignore_analysis=*) + if [ -z "$VIASH_PAR_IGNORE_ANALYSIS" ]; then + VIASH_PAR_IGNORE_ANALYSIS=$(ViashRemoveFlags "$1") + else + VIASH_PAR_IGNORE_ANALYSIS="$VIASH_PAR_IGNORE_ANALYSIS,"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --ignore_samples) + if [ -z "$VIASH_PAR_IGNORE_SAMPLES" ]; then + VIASH_PAR_IGNORE_SAMPLES="$2" + else + VIASH_PAR_IGNORE_SAMPLES="$VIASH_PAR_IGNORE_SAMPLES,""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --ignore_samples. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ignore_samples=*) + if [ -z "$VIASH_PAR_IGNORE_SAMPLES" ]; then + VIASH_PAR_IGNORE_SAMPLES=$(ViashRemoveFlags "$1") + else + VIASH_PAR_IGNORE_SAMPLES="$VIASH_PAR_IGNORE_SAMPLES,"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --ignore_symlinks) + [ -n "$VIASH_PAR_IGNORE_SYMLINKS" ] && ViashError Bad arguments for option \'--ignore_symlinks\': \'$VIASH_PAR_IGNORE_SYMLINKS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_IGNORE_SYMLINKS=true + shift 1 + ;; + --dirs) + [ -n "$VIASH_PAR_DIRS" ] && ViashError Bad arguments for option \'--dirs\': \'$VIASH_PAR_DIRS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DIRS=true + shift 1 + ;; + --dirs_depth) + [ -n "$VIASH_PAR_DIRS_DEPTH" ] && ViashError Bad arguments for option \'--dirs_depth\': \'$VIASH_PAR_DIRS_DEPTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DIRS_DEPTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --dirs_depth. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --dirs_depth=*) + [ -n "$VIASH_PAR_DIRS_DEPTH" ] && ViashError Bad arguments for option \'--dirs_depth=*\': \'$VIASH_PAR_DIRS_DEPTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DIRS_DEPTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + --full_names) + [ -n "$VIASH_PAR_FULL_NAMES" ] && ViashError Bad arguments for option \'--full_names\': \'$VIASH_PAR_FULL_NAMES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FULL_NAMES=true + shift 1 + ;; + --fn_as_s_name) + [ -n "$VIASH_PAR_FN_AS_S_NAME" ] && ViashError Bad arguments for option \'--fn_as_s_name\': \'$VIASH_PAR_FN_AS_S_NAME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FN_AS_S_NAME=true + shift 1 + ;; + --replace_names) + [ -n "$VIASH_PAR_REPLACE_NAMES" ] && ViashError Bad arguments for option \'--replace_names\': \'$VIASH_PAR_REPLACE_NAMES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REPLACE_NAMES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --replace_names. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --replace_names=*) + [ -n "$VIASH_PAR_REPLACE_NAMES" ] && ViashError Bad arguments for option \'--replace_names=*\': \'$VIASH_PAR_REPLACE_NAMES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REPLACE_NAMES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --title) + [ -n "$VIASH_PAR_TITLE" ] && ViashError Bad arguments for option \'--title\': \'$VIASH_PAR_TITLE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TITLE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --title. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --title=*) + [ -n "$VIASH_PAR_TITLE" ] && ViashError Bad arguments for option \'--title=*\': \'$VIASH_PAR_TITLE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TITLE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --comment) + [ -n "$VIASH_PAR_COMMENT" ] && ViashError Bad arguments for option \'--comment\': \'$VIASH_PAR_COMMENT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMMENT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --comment. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --comment=*) + [ -n "$VIASH_PAR_COMMENT" ] && ViashError Bad arguments for option \'--comment=*\': \'$VIASH_PAR_COMMENT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMMENT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --template) + [ -n "$VIASH_PAR_TEMPLATE" ] && ViashError Bad arguments for option \'--template\': \'$VIASH_PAR_TEMPLATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TEMPLATE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --template. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --template=*) + [ -n "$VIASH_PAR_TEMPLATE" ] && ViashError Bad arguments for option \'--template=*\': \'$VIASH_PAR_TEMPLATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TEMPLATE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sample_names) + [ -n "$VIASH_PAR_SAMPLE_NAMES" ] && ViashError Bad arguments for option \'--sample_names\': \'$VIASH_PAR_SAMPLE_NAMES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_NAMES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sample_names. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sample_names=*) + [ -n "$VIASH_PAR_SAMPLE_NAMES" ] && ViashError Bad arguments for option \'--sample_names=*\': \'$VIASH_PAR_SAMPLE_NAMES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_NAMES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sample_filters) + [ -n "$VIASH_PAR_SAMPLE_FILTERS" ] && ViashError Bad arguments for option \'--sample_filters\': \'$VIASH_PAR_SAMPLE_FILTERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_FILTERS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sample_filters. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sample_filters=*) + [ -n "$VIASH_PAR_SAMPLE_FILTERS" ] && ViashError Bad arguments for option \'--sample_filters=*\': \'$VIASH_PAR_SAMPLE_FILTERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_FILTERS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --custom_css_file) + [ -n "$VIASH_PAR_CUSTOM_CSS_FILE" ] && ViashError Bad arguments for option \'--custom_css_file\': \'$VIASH_PAR_CUSTOM_CSS_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUSTOM_CSS_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --custom_css_file. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --custom_css_file=*) + [ -n "$VIASH_PAR_CUSTOM_CSS_FILE" ] && ViashError Bad arguments for option \'--custom_css_file=*\': \'$VIASH_PAR_CUSTOM_CSS_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUSTOM_CSS_FILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --profile_runtime) + [ -n "$VIASH_PAR_PROFILE_RUNTIME" ] && ViashError Bad arguments for option \'--profile_runtime\': \'$VIASH_PAR_PROFILE_RUNTIME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PROFILE_RUNTIME=true + shift 1 + ;; + --verbose) + [ -n "$VIASH_PAR_VERBOSE" ] && ViashError Bad arguments for option \'--verbose\': \'$VIASH_PAR_VERBOSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_VERBOSE=true + shift 1 + ;; + --quiet) + [ -n "$VIASH_PAR_QUIET" ] && ViashError Bad arguments for option \'--quiet\': \'$VIASH_PAR_QUIET\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUIET=true + shift 1 + ;; + --strict) + [ -n "$VIASH_PAR_STRICT" ] && ViashError Bad arguments for option \'--strict\': \'$VIASH_PAR_STRICT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_STRICT=true + shift 1 + ;; + --development) + [ -n "$VIASH_PAR_DEVELOPMENT" ] && ViashError Bad arguments for option \'--development\': \'$VIASH_PAR_DEVELOPMENT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DEVELOPMENT=true + shift 1 + ;; + --require_logs) + [ -n "$VIASH_PAR_REQUIRE_LOGS" ] && ViashError Bad arguments for option \'--require_logs\': \'$VIASH_PAR_REQUIRE_LOGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REQUIRE_LOGS=true + shift 1 + ;; + --no_megaqc_upload) + [ -n "$VIASH_PAR_NO_MEGAQC_UPLOAD" ] && ViashError Bad arguments for option \'--no_megaqc_upload\': \'$VIASH_PAR_NO_MEGAQC_UPLOAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_MEGAQC_UPLOAD=true + shift 1 + ;; + --no_ansi) + [ -n "$VIASH_PAR_NO_ANSI" ] && ViashError Bad arguments for option \'--no_ansi\': \'$VIASH_PAR_NO_ANSI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_ANSI=true + shift 1 + ;; + --cl_config) + [ -n "$VIASH_PAR_CL_CONFIG" ] && ViashError Bad arguments for option \'--cl_config\': \'$VIASH_PAR_CL_CONFIG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CL_CONFIG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cl_config. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cl_config=*) + [ -n "$VIASH_PAR_CL_CONFIG" ] && ViashError Bad arguments for option \'--cl_config=*\': \'$VIASH_PAR_CL_CONFIG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CL_CONFIG=$(ViashRemoveFlags "$1") + shift 1 + ;; + --flat) + [ -n "$VIASH_PAR_FLAT" ] && ViashError Bad arguments for option \'--flat\': \'$VIASH_PAR_FLAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FLAT=true + shift 1 + ;; + --interactive) + [ -n "$VIASH_PAR_INTERACTIVE" ] && ViashError Bad arguments for option \'--interactive\': \'$VIASH_PAR_INTERACTIVE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INTERACTIVE=true + shift 1 + ;; + --data_dir) + [ -n "$VIASH_PAR_DATA_DIR" ] && ViashError Bad arguments for option \'--data_dir\': \'$VIASH_PAR_DATA_DIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DATA_DIR=true + shift 1 + ;; + --no_data_dir) + [ -n "$VIASH_PAR_NO_DATA_DIR" ] && ViashError Bad arguments for option \'--no_data_dir\': \'$VIASH_PAR_NO_DATA_DIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_DATA_DIR=true + shift 1 + ;; + --zip_data_dir) + [ -n "$VIASH_PAR_ZIP_DATA_DIR" ] && ViashError Bad arguments for option \'--zip_data_dir\': \'$VIASH_PAR_ZIP_DATA_DIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ZIP_DATA_DIR=true + shift 1 + ;; + --data_format) + [ -n "$VIASH_PAR_DATA_FORMAT" ] && ViashError Bad arguments for option \'--data_format\': \'$VIASH_PAR_DATA_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DATA_FORMAT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --data_format. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --data_format=*) + [ -n "$VIASH_PAR_DATA_FORMAT" ] && ViashError Bad arguments for option \'--data_format=*\': \'$VIASH_PAR_DATA_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DATA_FORMAT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --pdf) + [ -n "$VIASH_PAR_PDF" ] && ViashError Bad arguments for option \'--pdf\': \'$VIASH_PAR_PDF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PDF=true + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/multiqc:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_IGNORE_SYMLINKS+x} ]; then + VIASH_PAR_IGNORE_SYMLINKS="false" +fi +if [ -z ${VIASH_PAR_DIRS+x} ]; then + VIASH_PAR_DIRS="false" +fi +if [ -z ${VIASH_PAR_FULL_NAMES+x} ]; then + VIASH_PAR_FULL_NAMES="false" +fi +if [ -z ${VIASH_PAR_FN_AS_S_NAME+x} ]; then + VIASH_PAR_FN_AS_S_NAME="false" +fi +if [ -z ${VIASH_PAR_PROFILE_RUNTIME+x} ]; then + VIASH_PAR_PROFILE_RUNTIME="false" +fi +if [ -z ${VIASH_PAR_VERBOSE+x} ]; then + VIASH_PAR_VERBOSE="false" +fi +if [ -z ${VIASH_PAR_QUIET+x} ]; then + VIASH_PAR_QUIET="false" +fi +if [ -z ${VIASH_PAR_STRICT+x} ]; then + VIASH_PAR_STRICT="false" +fi +if [ -z ${VIASH_PAR_DEVELOPMENT+x} ]; then + VIASH_PAR_DEVELOPMENT="false" +fi +if [ -z ${VIASH_PAR_REQUIRE_LOGS+x} ]; then + VIASH_PAR_REQUIRE_LOGS="false" +fi +if [ -z ${VIASH_PAR_NO_MEGAQC_UPLOAD+x} ]; then + VIASH_PAR_NO_MEGAQC_UPLOAD="false" +fi +if [ -z ${VIASH_PAR_NO_ANSI+x} ]; then + VIASH_PAR_NO_ANSI="false" +fi +if [ -z ${VIASH_PAR_FLAT+x} ]; then + VIASH_PAR_FLAT="false" +fi +if [ -z ${VIASH_PAR_INTERACTIVE+x} ]; then + VIASH_PAR_INTERACTIVE="false" +fi +if [ -z ${VIASH_PAR_DATA_DIR+x} ]; then + VIASH_PAR_DATA_DIR="false" +fi +if [ -z ${VIASH_PAR_NO_DATA_DIR+x} ]; then + VIASH_PAR_NO_DATA_DIR="false" +fi +if [ -z ${VIASH_PAR_ZIP_DATA_DIR+x} ]; then + VIASH_PAR_ZIP_DATA_DIR="false" +fi +if [ -z ${VIASH_PAR_PDF+x} ]; then + VIASH_PAR_PDF="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_INPUT; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi +if [ ! -z "$VIASH_PAR_REPLACE_NAMES" ] && [ ! -e "$VIASH_PAR_REPLACE_NAMES" ]; then + ViashError "Input file '$VIASH_PAR_REPLACE_NAMES' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_SAMPLE_NAMES" ] && [ ! -e "$VIASH_PAR_SAMPLE_NAMES" ]; then + ViashError "Input file '$VIASH_PAR_SAMPLE_NAMES' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_SAMPLE_FILTERS" ] && [ ! -e "$VIASH_PAR_SAMPLE_FILTERS" ]; then + ViashError "Input file '$VIASH_PAR_SAMPLE_FILTERS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_CUSTOM_CSS_FILE" ] && [ ! -e "$VIASH_PAR_CUSTOM_CSS_FILE" ]; then + ViashError "Input file '$VIASH_PAR_CUSTOM_CSS_FILE' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_IGNORE_SYMLINKS" ]]; then + if ! [[ "$VIASH_PAR_IGNORE_SYMLINKS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--ignore_symlinks' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DIRS" ]]; then + if ! [[ "$VIASH_PAR_DIRS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--dirs' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DIRS_DEPTH" ]]; then + if ! [[ "$VIASH_PAR_DIRS_DEPTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--dirs_depth' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FULL_NAMES" ]]; then + if ! [[ "$VIASH_PAR_FULL_NAMES" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--full_names' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FN_AS_S_NAME" ]]; then + if ! [[ "$VIASH_PAR_FN_AS_S_NAME" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--fn_as_s_name' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PROFILE_RUNTIME" ]]; then + if ! [[ "$VIASH_PAR_PROFILE_RUNTIME" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--profile_runtime' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_VERBOSE" ]]; then + if ! [[ "$VIASH_PAR_VERBOSE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--verbose' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_QUIET" ]]; then + if ! [[ "$VIASH_PAR_QUIET" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--quiet' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_STRICT" ]]; then + if ! [[ "$VIASH_PAR_STRICT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--strict' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DEVELOPMENT" ]]; then + if ! [[ "$VIASH_PAR_DEVELOPMENT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--development' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_REQUIRE_LOGS" ]]; then + if ! [[ "$VIASH_PAR_REQUIRE_LOGS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--require_logs' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_MEGAQC_UPLOAD" ]]; then + if ! [[ "$VIASH_PAR_NO_MEGAQC_UPLOAD" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_megaqc_upload' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_ANSI" ]]; then + if ! [[ "$VIASH_PAR_NO_ANSI" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_ansi' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FLAT" ]]; then + if ! [[ "$VIASH_PAR_FLAT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--flat' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_INTERACTIVE" ]]; then + if ! [[ "$VIASH_PAR_INTERACTIVE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--interactive' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DATA_DIR" ]]; then + if ! [[ "$VIASH_PAR_DATA_DIR" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--data_dir' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_DATA_DIR" ]]; then + if ! [[ "$VIASH_PAR_NO_DATA_DIR" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_data_dir' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ZIP_DATA_DIR" ]]; then + if ! [[ "$VIASH_PAR_ZIP_DATA_DIR" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--zip_data_dir' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PDF" ]]; then + if ! [[ "$VIASH_PAR_PDF" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--pdf' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# check whether value is belongs to a set of choices +if [ ! -z "$VIASH_PAR_TEMPLATE" ]; then + VIASH_PAR_TEMPLATE_CHOICES=("default;gathered;geo;highcharts;sections;simple") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_TEMPLATE_CHOICES[*]};" =~ ";$VIASH_PAR_TEMPLATE;" ]]; then + ViashError '--template' specified value of \'$VIASH_PAR_TEMPLATE\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +if [ ! -z "$VIASH_PAR_DATA_FORMAT" ]; then + VIASH_PAR_DATA_FORMAT_CHOICES=("tsv;csv;json;yaml") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_DATA_FORMAT_CHOICES[*]};" =~ ";$VIASH_PAR_DATA_FORMAT;" ]]; then + ViashError '--data_format' specified value of \'$VIASH_PAR_DATA_FORMAT\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT_REPORT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT_REPORT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT_REPORT")" +fi +if [ ! -z "$VIASH_PAR_OUTPUT_DATA" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT_DATA")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT_DATA")" +fi +if [ ! -z "$VIASH_PAR_OUTPUT_PLOTS" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT_PLOTS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT_PLOTS")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_TEST_INPUT=() + IFS=';' + for var in $VIASH_PAR_INPUT; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_INPUT+=( "$var" ) + done + VIASH_PAR_INPUT=$(IFS=';' ; echo "${VIASH_TEST_INPUT[*]}") +fi +if [ ! -z "$VIASH_PAR_OUTPUT_REPORT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT_REPORT")" ) + VIASH_PAR_OUTPUT_REPORT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT_REPORT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT_REPORT" ) +fi +if [ ! -z "$VIASH_PAR_OUTPUT_DATA" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT_DATA")" ) + VIASH_PAR_OUTPUT_DATA=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT_DATA") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT_DATA" ) +fi +if [ ! -z "$VIASH_PAR_OUTPUT_PLOTS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT_PLOTS")" ) + VIASH_PAR_OUTPUT_PLOTS=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT_PLOTS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT_PLOTS" ) +fi +if [ ! -z "$VIASH_PAR_REPLACE_NAMES" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REPLACE_NAMES")" ) + VIASH_PAR_REPLACE_NAMES=$(ViashDockerAutodetectMount "$VIASH_PAR_REPLACE_NAMES") +fi +if [ ! -z "$VIASH_PAR_SAMPLE_NAMES" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SAMPLE_NAMES")" ) + VIASH_PAR_SAMPLE_NAMES=$(ViashDockerAutodetectMount "$VIASH_PAR_SAMPLE_NAMES") +fi +if [ ! -z "$VIASH_PAR_SAMPLE_FILTERS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SAMPLE_FILTERS")" ) + VIASH_PAR_SAMPLE_FILTERS=$(ViashDockerAutodetectMount "$VIASH_PAR_SAMPLE_FILTERS") +fi +if [ ! -z "$VIASH_PAR_CUSTOM_CSS_FILE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_CUSTOM_CSS_FILE")" ) + VIASH_PAR_CUSTOM_CSS_FILE=$(ViashDockerAutodetectMount "$VIASH_PAR_CUSTOM_CSS_FILE") +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-multiqc-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_REPORT+x} ]; then echo "${VIASH_PAR_OUTPUT_REPORT}" | sed "s#'#'\"'\"'#g;s#.*#par_output_report='&'#" ; else echo "# par_output_report="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_DATA+x} ]; then echo "${VIASH_PAR_OUTPUT_DATA}" | sed "s#'#'\"'\"'#g;s#.*#par_output_data='&'#" ; else echo "# par_output_data="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_PLOTS+x} ]; then echo "${VIASH_PAR_OUTPUT_PLOTS}" | sed "s#'#'\"'\"'#g;s#.*#par_output_plots='&'#" ; else echo "# par_output_plots="; fi ) +$( if [ ! -z ${VIASH_PAR_INCLUDE_MODULES+x} ]; then echo "${VIASH_PAR_INCLUDE_MODULES}" | sed "s#'#'\"'\"'#g;s#.*#par_include_modules='&'#" ; else echo "# par_include_modules="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCLUDE_MODULES+x} ]; then echo "${VIASH_PAR_EXCLUDE_MODULES}" | sed "s#'#'\"'\"'#g;s#.*#par_exclude_modules='&'#" ; else echo "# par_exclude_modules="; fi ) +$( if [ ! -z ${VIASH_PAR_IGNORE_ANALYSIS+x} ]; then echo "${VIASH_PAR_IGNORE_ANALYSIS}" | sed "s#'#'\"'\"'#g;s#.*#par_ignore_analysis='&'#" ; else echo "# par_ignore_analysis="; fi ) +$( if [ ! -z ${VIASH_PAR_IGNORE_SAMPLES+x} ]; then echo "${VIASH_PAR_IGNORE_SAMPLES}" | sed "s#'#'\"'\"'#g;s#.*#par_ignore_samples='&'#" ; else echo "# par_ignore_samples="; fi ) +$( if [ ! -z ${VIASH_PAR_IGNORE_SYMLINKS+x} ]; then echo "${VIASH_PAR_IGNORE_SYMLINKS}" | sed "s#'#'\"'\"'#g;s#.*#par_ignore_symlinks='&'#" ; else echo "# par_ignore_symlinks="; fi ) +$( if [ ! -z ${VIASH_PAR_DIRS+x} ]; then echo "${VIASH_PAR_DIRS}" | sed "s#'#'\"'\"'#g;s#.*#par_dirs='&'#" ; else echo "# par_dirs="; fi ) +$( if [ ! -z ${VIASH_PAR_DIRS_DEPTH+x} ]; then echo "${VIASH_PAR_DIRS_DEPTH}" | sed "s#'#'\"'\"'#g;s#.*#par_dirs_depth='&'#" ; else echo "# par_dirs_depth="; fi ) +$( if [ ! -z ${VIASH_PAR_FULL_NAMES+x} ]; then echo "${VIASH_PAR_FULL_NAMES}" | sed "s#'#'\"'\"'#g;s#.*#par_full_names='&'#" ; else echo "# par_full_names="; fi ) +$( if [ ! -z ${VIASH_PAR_FN_AS_S_NAME+x} ]; then echo "${VIASH_PAR_FN_AS_S_NAME}" | sed "s#'#'\"'\"'#g;s#.*#par_fn_as_s_name='&'#" ; else echo "# par_fn_as_s_name="; fi ) +$( if [ ! -z ${VIASH_PAR_REPLACE_NAMES+x} ]; then echo "${VIASH_PAR_REPLACE_NAMES}" | sed "s#'#'\"'\"'#g;s#.*#par_replace_names='&'#" ; else echo "# par_replace_names="; fi ) +$( if [ ! -z ${VIASH_PAR_TITLE+x} ]; then echo "${VIASH_PAR_TITLE}" | sed "s#'#'\"'\"'#g;s#.*#par_title='&'#" ; else echo "# par_title="; fi ) +$( if [ ! -z ${VIASH_PAR_COMMENT+x} ]; then echo "${VIASH_PAR_COMMENT}" | sed "s#'#'\"'\"'#g;s#.*#par_comment='&'#" ; else echo "# par_comment="; fi ) +$( if [ ! -z ${VIASH_PAR_TEMPLATE+x} ]; then echo "${VIASH_PAR_TEMPLATE}" | sed "s#'#'\"'\"'#g;s#.*#par_template='&'#" ; else echo "# par_template="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_NAMES+x} ]; then echo "${VIASH_PAR_SAMPLE_NAMES}" | sed "s#'#'\"'\"'#g;s#.*#par_sample_names='&'#" ; else echo "# par_sample_names="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_FILTERS+x} ]; then echo "${VIASH_PAR_SAMPLE_FILTERS}" | sed "s#'#'\"'\"'#g;s#.*#par_sample_filters='&'#" ; else echo "# par_sample_filters="; fi ) +$( if [ ! -z ${VIASH_PAR_CUSTOM_CSS_FILE+x} ]; then echo "${VIASH_PAR_CUSTOM_CSS_FILE}" | sed "s#'#'\"'\"'#g;s#.*#par_custom_css_file='&'#" ; else echo "# par_custom_css_file="; fi ) +$( if [ ! -z ${VIASH_PAR_PROFILE_RUNTIME+x} ]; then echo "${VIASH_PAR_PROFILE_RUNTIME}" | sed "s#'#'\"'\"'#g;s#.*#par_profile_runtime='&'#" ; else echo "# par_profile_runtime="; fi ) +$( if [ ! -z ${VIASH_PAR_VERBOSE+x} ]; then echo "${VIASH_PAR_VERBOSE}" | sed "s#'#'\"'\"'#g;s#.*#par_verbose='&'#" ; else echo "# par_verbose="; fi ) +$( if [ ! -z ${VIASH_PAR_QUIET+x} ]; then echo "${VIASH_PAR_QUIET}" | sed "s#'#'\"'\"'#g;s#.*#par_quiet='&'#" ; else echo "# par_quiet="; fi ) +$( if [ ! -z ${VIASH_PAR_STRICT+x} ]; then echo "${VIASH_PAR_STRICT}" | sed "s#'#'\"'\"'#g;s#.*#par_strict='&'#" ; else echo "# par_strict="; fi ) +$( if [ ! -z ${VIASH_PAR_DEVELOPMENT+x} ]; then echo "${VIASH_PAR_DEVELOPMENT}" | sed "s#'#'\"'\"'#g;s#.*#par_development='&'#" ; else echo "# par_development="; fi ) +$( if [ ! -z ${VIASH_PAR_REQUIRE_LOGS+x} ]; then echo "${VIASH_PAR_REQUIRE_LOGS}" | sed "s#'#'\"'\"'#g;s#.*#par_require_logs='&'#" ; else echo "# par_require_logs="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_MEGAQC_UPLOAD+x} ]; then echo "${VIASH_PAR_NO_MEGAQC_UPLOAD}" | sed "s#'#'\"'\"'#g;s#.*#par_no_megaqc_upload='&'#" ; else echo "# par_no_megaqc_upload="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_ANSI+x} ]; then echo "${VIASH_PAR_NO_ANSI}" | sed "s#'#'\"'\"'#g;s#.*#par_no_ansi='&'#" ; else echo "# par_no_ansi="; fi ) +$( if [ ! -z ${VIASH_PAR_CL_CONFIG+x} ]; then echo "${VIASH_PAR_CL_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#par_cl_config='&'#" ; else echo "# par_cl_config="; fi ) +$( if [ ! -z ${VIASH_PAR_FLAT+x} ]; then echo "${VIASH_PAR_FLAT}" | sed "s#'#'\"'\"'#g;s#.*#par_flat='&'#" ; else echo "# par_flat="; fi ) +$( if [ ! -z ${VIASH_PAR_INTERACTIVE+x} ]; then echo "${VIASH_PAR_INTERACTIVE}" | sed "s#'#'\"'\"'#g;s#.*#par_interactive='&'#" ; else echo "# par_interactive="; fi ) +$( if [ ! -z ${VIASH_PAR_DATA_DIR+x} ]; then echo "${VIASH_PAR_DATA_DIR}" | sed "s#'#'\"'\"'#g;s#.*#par_data_dir='&'#" ; else echo "# par_data_dir="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_DATA_DIR+x} ]; then echo "${VIASH_PAR_NO_DATA_DIR}" | sed "s#'#'\"'\"'#g;s#.*#par_no_data_dir='&'#" ; else echo "# par_no_data_dir="; fi ) +$( if [ ! -z ${VIASH_PAR_ZIP_DATA_DIR+x} ]; then echo "${VIASH_PAR_ZIP_DATA_DIR}" | sed "s#'#'\"'\"'#g;s#.*#par_zip_data_dir='&'#" ; else echo "# par_zip_data_dir="; fi ) +$( if [ ! -z ${VIASH_PAR_DATA_FORMAT+x} ]; then echo "${VIASH_PAR_DATA_FORMAT}" | sed "s#'#'\"'\"'#g;s#.*#par_data_format='&'#" ; else echo "# par_data_format="; fi ) +$( if [ ! -z ${VIASH_PAR_PDF+x} ]; then echo "${VIASH_PAR_PDF}" | sed "s#'#'\"'\"'#g;s#.*#par_pdf='&'#" ; else echo "# par_pdf="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END +#!/bin/bash + +# disable flags +[[ "\$par_ignore_symlinks" == "false" ]] && unset par_ignore_symlinks +[[ "\$par_dirs" == "false" ]] && unset par_dirs +[[ "\$par_full_names" == "false" ]] && unset par_full_names +[[ "\$par_fn_as_s_name" == "false" ]] && unset par_fn_as_s_name +[[ "\$par_profile_runtime" == "false" ]] && unset par_profile_runtime +[[ "\$par_verbose" == "false" ]] && unset par_verbose +[[ "\$par_quiet" == "false" ]] && unset par_quiet +[[ "\$par_strict" == "false" ]] && unset par_strict +[[ "\$par_development" == "false" ]] && unset par_development +[[ "\$par_require_logs" == "false" ]] && unset par_require_logs +[[ "\$par_no_megaqc_upload" == "false" ]] && unset par_no_megaqc_upload +[[ "\$par_no_ansi" == "false" ]] && unset par_no_ansi +[[ "\$par_flat" == "false" ]] && unset par_flat +[[ "\$par_interactive" == "false" ]] && unset par_interactive +[[ "\$par_static_plot_export" == "false" ]] && unset par_static_plot_export +[[ "\$par_data_dir" == "false" ]] && unset par_data_dir +[[ "\$par_no_data_dir" == "false" ]] && unset par_no_data_dir +[[ "\$par_zip_data_dir" == "false" ]] && unset par_zip_data_dir +[[ "\$par_pdf" == "false" ]] && unset par_pdf + + +# handle inputs +out_dir=\$(dirname "\$par_output_report") +output_report_file=\$(basename "\$par_output_report") +report_name="\${output_report_file%.*}" + +# handle outputs +[[ -z "\$par_output_report" ]] && no_report=true +[[ -z "\$par_output_data" ]] && no_data_dir=true +[[ ! -z "\$par_output_data" ]] && data_dir=true +[[ ! -z "\$par_output_plots" ]] && export=true + +# handle multiples +IFS=";" read -ra inputs <<< \$par_input + +if [[ -n "\$par_include_modules" ]]; then + include_modules="" + IFS="," read -ra incl_modules <<< \$par_include_modules + for i in "\${incl_modules[@]}"; do + include_modules+="--include \$i " + done + unset IFS +fi + +if [[ -n "\$par_exclude_modules" ]]; then + exclude_modules="" + IFS="," read -ra excl_modules <<< \$par_exclude_modules + for i in "\${excl_modules[@]}"; do + exclude_modules+="--exclude \$i" + done + unset IFS +fi + +if [[ -n "\$par_ignore_analysis" ]]; then + ignore="" + IFS="," read -ra ignore_analysis <<< \$par_ignore_analysis + for i in "\${ignore_analysis[@]}"; do + ignore+="--ignore \$i " + done + unset IFS +fi + +if [[ -n "\$par_ignore_samples" ]]; then + ignore_samples="" + IFS="," read -ra ign_samples <<< \$par_ignore_samples + for i in "\${ign_samples[@]}"; do + ignore_samples+="--ignore-samples \$i" + done + unset IFS +fi + +# run multiqc +multiqc \\ + \${par_output_report:+--filename "\$report_name"} \\ + \${out_dir:+--outdir "\$out_dir"} \\ + \${no_report:+--no-report} \\ + \${no_data_dir:+--no-data-dir} \\ + \${data_dir:+--data-dir} \\ + \${export:+--export} \\ + \${par_title:+--title "\$par_title"} \\ + \${par_comment:+--comment "\$par_comment"} \\ + \${par_template:+--template "\$par_template"} \\ + \${par_sample_names:+--sample-names "\$par_sample_names"} \\ + \${par_sample_filters:+--sample-filters "\$par_sample_filters"} \\ + \${par_custom_css_file:+--custom-css-file "\$par_custom_css_file"} \\ + \${par_profile_runtime:+--profile-runtime} \\ + \${par_dirs:+--dirs} \\ + \${par_dirs_depth:+--dirs-depth "\$par_dirs_depth"} \\ + \${par_full_names:+--full-names} \\ + \${par_fn_as_s_name:+--fn-as-s-name} \\ + \${par_ignore_names:+--ignore-names "\$par_ignore_names"} \\ + \${par_ignore_symlinks:+--ignore-symlinks} \\ + \${ignore_samples} \\ + \${ignore} \\ + \${exclude_modules} \\ + \${include_modules} \\ + \${par_include_modules:+--include-modules "\$par_include_modules"} \\ + \${par_data_format:+--data-format "\$par_data_format"} \\ + \${par_cl_config:+--cl-config "\$par_cl_config"} \\ + \${par_zip_data_dir:+--zip-data-dir} \\ + \${par_pdf:+--pdf} \\ + \${par_interactive:+--interactive} \\ + \${par_flat:+--flat} \\ + \${par_verbose:+--verbose} \\ + \${par_quiet:+--quiet} \\ + \${par_strict:+--strict} \\ + \${par_no_megaqc_upload:+--no-megaqc-upload} \\ + \${par_no_ansi:+--no-ansi} \\ + \${par_profile_runtime:+--profile-runtime} \\ + \${par_require_logs:+--require-logs} \\ + \${par_development:+--development} \\ + --force \\ + "\${inputs[@]}" + +# Move outputs + +if [[ -n "\$par_output_data" ]] && [[ -d "\${out_dir}/\${report_name}_data" ]]; then + mv "\${out_dir}/\${report_name}_data" "\$par_output_data" +elif [[ -n "\$par_output_data" ]] && [[ ! -d "\${out_dir}/\${report_name}_data" ]]; then + echo "WARNING: Data could not be saved because data folder was not generated by multiqc. This could be due to filtering out of modules or samples." +fi + +if [[ -n "\$par_output_plots" ]] && [[ -d "\${out_dir}/\${report_name}_plots" ]]; then + mv "\${out_dir}/\${report_name}_plots" "\$par_output_plots" +elif [[ -n "\$par_output_plots" ]] && [[ ! -d "\${out_dir}/\${report_name}_plots" ]]; then + echo "WARNING: Plots could not be saved because plots folder was not generated by multiqc. This could be due to filtering out of modules or samples." +fi +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + unset VIASH_TEST_INPUT + IFS=';' + for var in $VIASH_PAR_INPUT; do + unset IFS + if [ -z "$VIASH_TEST_INPUT" ]; then + VIASH_TEST_INPUT="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_INPUT="$VIASH_TEST_INPUT;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_INPUT="$VIASH_TEST_INPUT" + fi + if [ ! -z "$VIASH_PAR_OUTPUT_REPORT" ]; then + VIASH_PAR_OUTPUT_REPORT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT_REPORT") + fi + if [ ! -z "$VIASH_PAR_OUTPUT_DATA" ]; then + VIASH_PAR_OUTPUT_DATA=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT_DATA") + fi + if [ ! -z "$VIASH_PAR_OUTPUT_PLOTS" ]; then + VIASH_PAR_OUTPUT_PLOTS=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT_PLOTS") + fi + if [ ! -z "$VIASH_PAR_REPLACE_NAMES" ]; then + VIASH_PAR_REPLACE_NAMES=$(ViashDockerStripAutomount "$VIASH_PAR_REPLACE_NAMES") + fi + if [ ! -z "$VIASH_PAR_SAMPLE_NAMES" ]; then + VIASH_PAR_SAMPLE_NAMES=$(ViashDockerStripAutomount "$VIASH_PAR_SAMPLE_NAMES") + fi + if [ ! -z "$VIASH_PAR_SAMPLE_FILTERS" ]; then + VIASH_PAR_SAMPLE_FILTERS=$(ViashDockerStripAutomount "$VIASH_PAR_SAMPLE_FILTERS") + fi + if [ ! -z "$VIASH_PAR_CUSTOM_CSS_FILE" ]; then + VIASH_PAR_CUSTOM_CSS_FILE=$(ViashDockerStripAutomount "$VIASH_PAR_CUSTOM_CSS_FILE") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +exit 0 diff --git a/target/executable/pear/.config.vsh.yaml b/target/executable/pear/.config.vsh.yaml new file mode 100644 index 00000000..6387d96b --- /dev/null +++ b/target/executable/pear/.config.vsh.yaml @@ -0,0 +1,406 @@ +name: "pear" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--forward_fastq" + alternatives: + - "-f" + description: "Forward paired-end FASTQ file" + info: null + example: + - "forward.fastq" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reverse_fastq" + alternatives: + - "-r" + description: "Reverse paired-end FASTQ file" + info: null + example: + - "reverse.fastq" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--assembled" + description: "The output file containing assembled reads. Can be compressed with\ + \ gzip." + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unassembled_forward" + description: "The output file containing forward reads that could not be assembled.\ + \ Can be compressed with gzip." + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unassembled_reverse" + description: "The output file containing reverse reads that could not be assembled.\ + \ Can be compressed with gzip." + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--discarded" + description: "The output file containing reads that were discarded due to too\ + \ low quality or too many uncalled bases. Can be compressed with gzip." + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Arguments" + arguments: + - type: "double" + name: "--p_value" + alternatives: + - "-p" + description: "Specify a p-value for the statistical test. If the computed p-value\ + \ of a possible assembly exceeds the specified p-value then paired-end read\ + \ will not be assembled. Valid options are: 0.0001, 0.001, 0.01, 0.05 and 1.0.\ + \ Setting 1.0 disables the test.\n" + info: null + example: + - 0.01 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_overlap" + alternatives: + - "-v" + description: "Specify the minimum overlap size. The minimum overlap may be set\ + \ to 1 when the statistical test is used. However, further restricting the minimum\ + \ overlap size to a proper value may reduce false-positive assembles.\n" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_assembly_length" + alternatives: + - "-m" + description: "Specify the maximum possible length of the assembled sequences.\ + \ Setting this value to 0 disables the restriction and assembled sequences may\ + \ be arbitrary long.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_assembly_length" + alternatives: + - "-n" + description: "Specify the minimum possible length of the assembled sequences.\ + \ Setting this value to 0 disables the restriction and assembled sequences may\ + \ be arbitrary short.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_trim_length" + alternatives: + - "-t" + description: "Specify the minimum length of reads after trimming the low quality\ + \ part (see option -q)\n" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--quality_threshold" + alternatives: + - "-q" + description: "Specify the quality threshold for trimming the low quality part\ + \ of a read. If the quality scores of two consecutive bases are strictly less\ + \ than the specified threshold, the rest of the read will be trimmed.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--max_uncalled_base" + alternatives: + - "-u" + description: "Specify the maximal proportion of uncalled bases in a read. Setting\ + \ this value to 0 will cause PEAR to discard all reads containing uncalled bases.\ + \ The other extreme setting is 1 which causes PEAR to process all reads independent\ + \ on the number of uncalled bases.\n" + info: null + example: + - 1.0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--test_method" + alternatives: + - "-g" + description: "Specify the type of statistical test. Two options are available.\ + \ 1: Given the minimum allowed overlap, test using the highest OES. Note that\ + \ due to its discrete nature, this test usually yields a lower p-value for the\ + \ assembled read than the cut- off (specified by -p). For example, setting the\ + \ cut-off to 0.05 using this test, the assembled reads might have an actual\ + \ p-value of 0.02.\n2. Use the acceptance probability (m.a.p). This test methods\ + \ computes the same probability as test method 1. However, it assumes that the\ + \ minimal overlap is the observed overlap with the highest OES, instead of the\ + \ one specified by -v. Therefore, this is not a valid statistical test and the\ + \ 'p-value' is in fact the maximal probability for accepting the assembly. Nevertheless,\ + \ we observed in practice that for the case the actual overlap sizes are relatively\ + \ small, test 2 can correctly assemble more reads with only slightly higher\ + \ false-positive rate.\n" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--emperical_freqs" + alternatives: + - "-e" + description: "Disable empirical base frequencies.\n" + info: null + direction: "input" + - type: "integer" + name: "--score_method" + alternatives: + - "-s" + description: "Specify the scoring method. 1. OES with +1 for match and -1 for\ + \ mismatch. 2: Assembly score (AS). Use +1 for match and -1 for mismatch multiplied\ + \ by base quality scores. 3: Ignore quality scores and use +1 for a match and\ + \ -1 for a mismatch.\n" + info: null + example: + - 2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--phred_base" + alternatives: + - "-b" + description: "Base PHRED quality score.\n" + info: null + example: + - 33 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cap" + alternatives: + - "-c" + description: "Specify the upper bound for the resulting quality score. If set\ + \ to zero, capping is disabled.\n" + info: null + example: + - 40 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--nbase" + alternatives: + - "-z" + description: "When merging a base-pair that consists of two non-equal bases out\ + \ of which none is degenerate, set the merged base to N and use the highest\ + \ quality score of the two bases\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "PEAR is an ultrafast, memory-efficient and highly accurate pair-end\ + \ read merger. It is fully parallelized and can run with as low as just a few kilobytes\ + \ of memory.\n\nPEAR evaluates all possible paired-end read overlaps and without\ + \ requiring the target fragment size as input. In addition, it implements a statistical\ + \ test for minimizing false-positive results. Together with a highly optimized implementation,\ + \ it can merge millions of paired end reads within a couple of minutes on a standard\ + \ desktop computer.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "pair-end" +- "read" +- "merge" +license: "CC-BY-NC-SA-3.0" +references: + doi: + - "10.1093/bioinformatics/btt593" +links: + repository: "https://github.com/tseemann/PEAR" + homepage: "https://cme.h-its.org/exelixis/web/software/pear" + documentation: "https://cme.h-its.org/exelixis/web/software/pear/doc.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/pear:0.9.6--h9d449c0_10" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "version=$(pear -h | grep 'PEAR v' | sed 's/PEAR v//' | sed 's/ .*//') && \\\ + \necho \"pear: $version\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/pear/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/pear" + executable: "target/executable/pear/pear" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/pear/pear b/target/executable/pear/pear new file mode 100755 index 00000000..b031dc00 --- /dev/null +++ b/target/executable/pear/pear @@ -0,0 +1,1669 @@ +#!/usr/bin/env bash + +# pear main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="pear" +VIASH_META_FUNCTIONALITY_NAME="pear" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "pear main" + echo "" + echo "PEAR is an ultrafast, memory-efficient and highly accurate pair-end read merger." + echo "It is fully parallelized and can run with as low as just a few kilobytes of" + echo "memory." + echo "" + echo "PEAR evaluates all possible paired-end read overlaps and without requiring the" + echo "target fragment size as input. In addition, it implements a statistical test for" + echo "minimizing false-positive results. Together with a highly optimized" + echo "implementation, it can merge millions of paired end reads within a couple of" + echo "minutes on a standard desktop computer." + echo "" + echo "Inputs:" + echo " -f, --forward_fastq" + echo " type: file, required parameter, file must exist" + echo " example: forward.fastq" + echo " Forward paired-end FASTQ file" + echo "" + echo " -r, --reverse_fastq" + echo " type: file, required parameter, file must exist" + echo " example: reverse.fastq" + echo " Reverse paired-end FASTQ file" + echo "" + echo "Outputs:" + echo " --assembled" + echo " type: file, required parameter, output, file must exist" + echo " The output file containing assembled reads. Can be compressed with gzip." + echo "" + echo " --unassembled_forward" + echo " type: file, required parameter, output, file must exist" + echo " The output file containing forward reads that could not be assembled." + echo " Can be compressed with gzip." + echo "" + echo " --unassembled_reverse" + echo " type: file, required parameter, output, file must exist" + echo " The output file containing reverse reads that could not be assembled." + echo " Can be compressed with gzip." + echo "" + echo " --discarded" + echo " type: file, required parameter, output, file must exist" + echo " The output file containing reads that were discarded due to too low" + echo " quality or too many uncalled bases. Can be compressed with gzip." + echo "" + echo "Arguments:" + echo " -p, --p_value" + echo " type: double" + echo " example: 0.01" + echo " Specify a p-value for the statistical test. If the computed p-value of a" + echo " possible assembly exceeds the specified p-value then paired-end read" + echo " will not be assembled. Valid options are: 0.0001, 0.001, 0.01, 0.05 and" + echo " 1.0. Setting 1.0 disables the test." + echo "" + echo " -v, --min_overlap" + echo " type: integer" + echo " example: 10" + echo " Specify the minimum overlap size. The minimum overlap may be set to 1" + echo " when the statistical test is used. However, further restricting the" + echo " minimum overlap size to a proper value may reduce false-positive" + echo " assembles." + echo "" + echo " -m, --max_assembly_length" + echo " type: integer" + echo " example: 0" + echo " Specify the maximum possible length of the assembled sequences. Setting" + echo " this value to 0 disables the restriction and assembled sequences may be" + echo " arbitrary long." + echo "" + echo " -n, --min_assembly_length" + echo " type: integer" + echo " example: 0" + echo " Specify the minimum possible length of the assembled sequences. Setting" + echo " this value to 0 disables the restriction and assembled sequences may be" + echo " arbitrary short." + echo "" + echo " -t, --min_trim_length" + echo " type: integer" + echo " example: 1" + echo " Specify the minimum length of reads after trimming the low quality" + echo " part (see option -q)" + echo "" + echo " -q, --quality_threshold" + echo " type: integer" + echo " example: 0" + echo " Specify the quality threshold for trimming the low quality part of a" + echo " read. If the quality scores of two consecutive bases are strictly less" + echo " than the specified threshold, the rest of the read will be trimmed." + echo "" + echo " -u, --max_uncalled_base" + echo " type: double" + echo " example: 1.0" + echo " Specify the maximal proportion of uncalled bases in a read. Setting this" + echo " value to 0 will cause PEAR to discard all reads containing uncalled" + echo " bases. The other extreme setting is 1 which causes PEAR to process all" + echo " reads independent on the number of uncalled bases." + echo "" + echo " -g, --test_method" + echo " type: integer" + echo " example: 1" + echo " Specify the type of statistical test. Two options are available. 1:" + echo " Given the minimum allowed overlap, test using the highest OES. Note that" + echo " due to its discrete nature, this test usually yields a lower p-value for" + echo " the assembled read than the cut- off (specified by -p). For example," + echo " setting the cut-off to 0.05 using this test, the assembled reads might" + echo " have an actual p-value of 0.02." + echo " 2. Use the acceptance probability (m.a.p). This test methods computes" + echo " the same probability as test method 1. However, it assumes that the" + echo " minimal overlap is the observed overlap with the highest OES, instead of" + echo " the one specified by -v. Therefore, this is not a valid statistical test" + echo " and the 'p-value' is in fact the maximal probability for accepting the" + echo " assembly. Nevertheless, we observed in practice that for the case the" + echo " actual overlap sizes are relatively small, test 2 can correctly assemble" + echo " more reads with only slightly higher false-positive rate." + echo "" + echo " -e, --emperical_freqs" + echo " type: boolean_true" + echo " Disable empirical base frequencies." + echo "" + echo " -s, --score_method" + echo " type: integer" + echo " example: 2" + echo " Specify the scoring method. 1. OES with +1 for match and -1 for" + echo " mismatch. 2: Assembly score (AS). Use +1 for match and -1 for mismatch" + echo " multiplied by base quality scores. 3: Ignore quality scores and use +1" + echo " for a match and -1 for a mismatch." + echo "" + echo " -b, --phred_base" + echo " type: integer" + echo " example: 33" + echo " Base PHRED quality score." + echo "" + echo " -c, --cap" + echo " type: integer" + echo " example: 40" + echo " Specify the upper bound for the resulting quality score. If set to" + echo " zero, capping is disabled." + echo "" + echo " -z, --nbase" + echo " type: boolean_true" + echo " When merging a base-pair that consists of two non-equal bases out of" + echo " which none is degenerate, set the merged base to N and use the highest" + echo " quality score of the two bases" +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/pear:0.9.6--h9d449c0_10 +ENTRYPOINT [] +RUN version=$(pear -h | grep 'PEAR v' | sed 's/PEAR v//' | sed 's/ .*//') && \ +echo "pear: $version" > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component pear" +LABEL org.opencontainers.image.created="2024-06-24T08:36:41Z" +LABEL org.opencontainers.image.source="https://github.com/tseemann/PEAR" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "pear main" + exit + ;; + --forward_fastq) + [ -n "$VIASH_PAR_FORWARD_FASTQ" ] && ViashError Bad arguments for option \'--forward_fastq\': \'$VIASH_PAR_FORWARD_FASTQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORWARD_FASTQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --forward_fastq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --forward_fastq=*) + [ -n "$VIASH_PAR_FORWARD_FASTQ" ] && ViashError Bad arguments for option \'--forward_fastq=*\': \'$VIASH_PAR_FORWARD_FASTQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORWARD_FASTQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_FORWARD_FASTQ" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_FORWARD_FASTQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORWARD_FASTQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -f. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --reverse_fastq) + [ -n "$VIASH_PAR_REVERSE_FASTQ" ] && ViashError Bad arguments for option \'--reverse_fastq\': \'$VIASH_PAR_REVERSE_FASTQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REVERSE_FASTQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --reverse_fastq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --reverse_fastq=*) + [ -n "$VIASH_PAR_REVERSE_FASTQ" ] && ViashError Bad arguments for option \'--reverse_fastq=*\': \'$VIASH_PAR_REVERSE_FASTQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REVERSE_FASTQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -r) + [ -n "$VIASH_PAR_REVERSE_FASTQ" ] && ViashError Bad arguments for option \'-r\': \'$VIASH_PAR_REVERSE_FASTQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REVERSE_FASTQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -r. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --assembled) + [ -n "$VIASH_PAR_ASSEMBLED" ] && ViashError Bad arguments for option \'--assembled\': \'$VIASH_PAR_ASSEMBLED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ASSEMBLED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --assembled. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --assembled=*) + [ -n "$VIASH_PAR_ASSEMBLED" ] && ViashError Bad arguments for option \'--assembled=*\': \'$VIASH_PAR_ASSEMBLED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ASSEMBLED=$(ViashRemoveFlags "$1") + shift 1 + ;; + --unassembled_forward) + [ -n "$VIASH_PAR_UNASSEMBLED_FORWARD" ] && ViashError Bad arguments for option \'--unassembled_forward\': \'$VIASH_PAR_UNASSEMBLED_FORWARD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNASSEMBLED_FORWARD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --unassembled_forward. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unassembled_forward=*) + [ -n "$VIASH_PAR_UNASSEMBLED_FORWARD" ] && ViashError Bad arguments for option \'--unassembled_forward=*\': \'$VIASH_PAR_UNASSEMBLED_FORWARD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNASSEMBLED_FORWARD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --unassembled_reverse) + [ -n "$VIASH_PAR_UNASSEMBLED_REVERSE" ] && ViashError Bad arguments for option \'--unassembled_reverse\': \'$VIASH_PAR_UNASSEMBLED_REVERSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNASSEMBLED_REVERSE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --unassembled_reverse. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unassembled_reverse=*) + [ -n "$VIASH_PAR_UNASSEMBLED_REVERSE" ] && ViashError Bad arguments for option \'--unassembled_reverse=*\': \'$VIASH_PAR_UNASSEMBLED_REVERSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNASSEMBLED_REVERSE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --discarded) + [ -n "$VIASH_PAR_DISCARDED" ] && ViashError Bad arguments for option \'--discarded\': \'$VIASH_PAR_DISCARDED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISCARDED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --discarded. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --discarded=*) + [ -n "$VIASH_PAR_DISCARDED" ] && ViashError Bad arguments for option \'--discarded=*\': \'$VIASH_PAR_DISCARDED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISCARDED=$(ViashRemoveFlags "$1") + shift 1 + ;; + --p_value) + [ -n "$VIASH_PAR_P_VALUE" ] && ViashError Bad arguments for option \'--p_value\': \'$VIASH_PAR_P_VALUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_P_VALUE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --p_value. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --p_value=*) + [ -n "$VIASH_PAR_P_VALUE" ] && ViashError Bad arguments for option \'--p_value=*\': \'$VIASH_PAR_P_VALUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_P_VALUE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -p) + [ -n "$VIASH_PAR_P_VALUE" ] && ViashError Bad arguments for option \'-p\': \'$VIASH_PAR_P_VALUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_P_VALUE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -p. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_overlap) + [ -n "$VIASH_PAR_MIN_OVERLAP" ] && ViashError Bad arguments for option \'--min_overlap\': \'$VIASH_PAR_MIN_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_OVERLAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_overlap. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_overlap=*) + [ -n "$VIASH_PAR_MIN_OVERLAP" ] && ViashError Bad arguments for option \'--min_overlap=*\': \'$VIASH_PAR_MIN_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_OVERLAP=$(ViashRemoveFlags "$1") + shift 1 + ;; + -v) + [ -n "$VIASH_PAR_MIN_OVERLAP" ] && ViashError Bad arguments for option \'-v\': \'$VIASH_PAR_MIN_OVERLAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_OVERLAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -v. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_assembly_length) + [ -n "$VIASH_PAR_MAX_ASSEMBLY_LENGTH" ] && ViashError Bad arguments for option \'--max_assembly_length\': \'$VIASH_PAR_MAX_ASSEMBLY_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_ASSEMBLY_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_assembly_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_assembly_length=*) + [ -n "$VIASH_PAR_MAX_ASSEMBLY_LENGTH" ] && ViashError Bad arguments for option \'--max_assembly_length=*\': \'$VIASH_PAR_MAX_ASSEMBLY_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_ASSEMBLY_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -m) + [ -n "$VIASH_PAR_MAX_ASSEMBLY_LENGTH" ] && ViashError Bad arguments for option \'-m\': \'$VIASH_PAR_MAX_ASSEMBLY_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_ASSEMBLY_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -m. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_assembly_length) + [ -n "$VIASH_PAR_MIN_ASSEMBLY_LENGTH" ] && ViashError Bad arguments for option \'--min_assembly_length\': \'$VIASH_PAR_MIN_ASSEMBLY_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ASSEMBLY_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_assembly_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_assembly_length=*) + [ -n "$VIASH_PAR_MIN_ASSEMBLY_LENGTH" ] && ViashError Bad arguments for option \'--min_assembly_length=*\': \'$VIASH_PAR_MIN_ASSEMBLY_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ASSEMBLY_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -n) + [ -n "$VIASH_PAR_MIN_ASSEMBLY_LENGTH" ] && ViashError Bad arguments for option \'-n\': \'$VIASH_PAR_MIN_ASSEMBLY_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ASSEMBLY_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -n. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_trim_length) + [ -n "$VIASH_PAR_MIN_TRIM_LENGTH" ] && ViashError Bad arguments for option \'--min_trim_length\': \'$VIASH_PAR_MIN_TRIM_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_TRIM_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_trim_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_trim_length=*) + [ -n "$VIASH_PAR_MIN_TRIM_LENGTH" ] && ViashError Bad arguments for option \'--min_trim_length=*\': \'$VIASH_PAR_MIN_TRIM_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_TRIM_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -t) + [ -n "$VIASH_PAR_MIN_TRIM_LENGTH" ] && ViashError Bad arguments for option \'-t\': \'$VIASH_PAR_MIN_TRIM_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_TRIM_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -t. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quality_threshold) + [ -n "$VIASH_PAR_QUALITY_THRESHOLD" ] && ViashError Bad arguments for option \'--quality_threshold\': \'$VIASH_PAR_QUALITY_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_THRESHOLD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --quality_threshold. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quality_threshold=*) + [ -n "$VIASH_PAR_QUALITY_THRESHOLD" ] && ViashError Bad arguments for option \'--quality_threshold=*\': \'$VIASH_PAR_QUALITY_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_THRESHOLD=$(ViashRemoveFlags "$1") + shift 1 + ;; + -q) + [ -n "$VIASH_PAR_QUALITY_THRESHOLD" ] && ViashError Bad arguments for option \'-q\': \'$VIASH_PAR_QUALITY_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_THRESHOLD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -q. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_uncalled_base) + [ -n "$VIASH_PAR_MAX_UNCALLED_BASE" ] && ViashError Bad arguments for option \'--max_uncalled_base\': \'$VIASH_PAR_MAX_UNCALLED_BASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_UNCALLED_BASE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_uncalled_base. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_uncalled_base=*) + [ -n "$VIASH_PAR_MAX_UNCALLED_BASE" ] && ViashError Bad arguments for option \'--max_uncalled_base=*\': \'$VIASH_PAR_MAX_UNCALLED_BASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_UNCALLED_BASE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -u) + [ -n "$VIASH_PAR_MAX_UNCALLED_BASE" ] && ViashError Bad arguments for option \'-u\': \'$VIASH_PAR_MAX_UNCALLED_BASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_UNCALLED_BASE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -u. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --test_method) + [ -n "$VIASH_PAR_TEST_METHOD" ] && ViashError Bad arguments for option \'--test_method\': \'$VIASH_PAR_TEST_METHOD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TEST_METHOD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --test_method. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --test_method=*) + [ -n "$VIASH_PAR_TEST_METHOD" ] && ViashError Bad arguments for option \'--test_method=*\': \'$VIASH_PAR_TEST_METHOD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TEST_METHOD=$(ViashRemoveFlags "$1") + shift 1 + ;; + -g) + [ -n "$VIASH_PAR_TEST_METHOD" ] && ViashError Bad arguments for option \'-g\': \'$VIASH_PAR_TEST_METHOD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TEST_METHOD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -g. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --emperical_freqs) + [ -n "$VIASH_PAR_EMPERICAL_FREQS" ] && ViashError Bad arguments for option \'--emperical_freqs\': \'$VIASH_PAR_EMPERICAL_FREQS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EMPERICAL_FREQS=true + shift 1 + ;; + -e) + [ -n "$VIASH_PAR_EMPERICAL_FREQS" ] && ViashError Bad arguments for option \'-e\': \'$VIASH_PAR_EMPERICAL_FREQS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EMPERICAL_FREQS=true + shift 1 + ;; + --score_method) + [ -n "$VIASH_PAR_SCORE_METHOD" ] && ViashError Bad arguments for option \'--score_method\': \'$VIASH_PAR_SCORE_METHOD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCORE_METHOD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --score_method. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --score_method=*) + [ -n "$VIASH_PAR_SCORE_METHOD" ] && ViashError Bad arguments for option \'--score_method=*\': \'$VIASH_PAR_SCORE_METHOD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCORE_METHOD=$(ViashRemoveFlags "$1") + shift 1 + ;; + -s) + [ -n "$VIASH_PAR_SCORE_METHOD" ] && ViashError Bad arguments for option \'-s\': \'$VIASH_PAR_SCORE_METHOD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCORE_METHOD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -s. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --phred_base) + [ -n "$VIASH_PAR_PHRED_BASE" ] && ViashError Bad arguments for option \'--phred_base\': \'$VIASH_PAR_PHRED_BASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PHRED_BASE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --phred_base. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --phred_base=*) + [ -n "$VIASH_PAR_PHRED_BASE" ] && ViashError Bad arguments for option \'--phred_base=*\': \'$VIASH_PAR_PHRED_BASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PHRED_BASE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -b) + [ -n "$VIASH_PAR_PHRED_BASE" ] && ViashError Bad arguments for option \'-b\': \'$VIASH_PAR_PHRED_BASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PHRED_BASE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -b. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cap) + [ -n "$VIASH_PAR_CAP" ] && ViashError Bad arguments for option \'--cap\': \'$VIASH_PAR_CAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cap. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cap=*) + [ -n "$VIASH_PAR_CAP" ] && ViashError Bad arguments for option \'--cap=*\': \'$VIASH_PAR_CAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CAP=$(ViashRemoveFlags "$1") + shift 1 + ;; + -c) + [ -n "$VIASH_PAR_CAP" ] && ViashError Bad arguments for option \'-c\': \'$VIASH_PAR_CAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -c. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --nbase) + [ -n "$VIASH_PAR_NBASE" ] && ViashError Bad arguments for option \'--nbase\': \'$VIASH_PAR_NBASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NBASE=true + shift 1 + ;; + -z) + [ -n "$VIASH_PAR_NBASE" ] && ViashError Bad arguments for option \'-z\': \'$VIASH_PAR_NBASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NBASE=true + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/pear:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_FORWARD_FASTQ+x} ]; then + ViashError '--forward_fastq' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_REVERSE_FASTQ+x} ]; then + ViashError '--reverse_fastq' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_ASSEMBLED+x} ]; then + ViashError '--assembled' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_UNASSEMBLED_FORWARD+x} ]; then + ViashError '--unassembled_forward' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_UNASSEMBLED_REVERSE+x} ]; then + ViashError '--unassembled_reverse' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_DISCARDED+x} ]; then + ViashError '--discarded' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_EMPERICAL_FREQS+x} ]; then + VIASH_PAR_EMPERICAL_FREQS="false" +fi +if [ -z ${VIASH_PAR_NBASE+x} ]; then + VIASH_PAR_NBASE="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_FORWARD_FASTQ" ] && [ ! -e "$VIASH_PAR_FORWARD_FASTQ" ]; then + ViashError "Input file '$VIASH_PAR_FORWARD_FASTQ' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REVERSE_FASTQ" ] && [ ! -e "$VIASH_PAR_REVERSE_FASTQ" ]; then + ViashError "Input file '$VIASH_PAR_REVERSE_FASTQ' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_P_VALUE" ]]; then + if ! [[ "$VIASH_PAR_P_VALUE" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--p_value' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_OVERLAP" ]]; then + if ! [[ "$VIASH_PAR_MIN_OVERLAP" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_overlap' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_ASSEMBLY_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_MAX_ASSEMBLY_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_assembly_length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_ASSEMBLY_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_MIN_ASSEMBLY_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_assembly_length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_TRIM_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_MIN_TRIM_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_trim_length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_QUALITY_THRESHOLD" ]]; then + if ! [[ "$VIASH_PAR_QUALITY_THRESHOLD" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--quality_threshold' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_UNCALLED_BASE" ]]; then + if ! [[ "$VIASH_PAR_MAX_UNCALLED_BASE" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--max_uncalled_base' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TEST_METHOD" ]]; then + if ! [[ "$VIASH_PAR_TEST_METHOD" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--test_method' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_EMPERICAL_FREQS" ]]; then + if ! [[ "$VIASH_PAR_EMPERICAL_FREQS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--emperical_freqs' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCORE_METHOD" ]]; then + if ! [[ "$VIASH_PAR_SCORE_METHOD" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--score_method' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PHRED_BASE" ]]; then + if ! [[ "$VIASH_PAR_PHRED_BASE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--phred_base' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CAP" ]]; then + if ! [[ "$VIASH_PAR_CAP" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cap' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NBASE" ]]; then + if ! [[ "$VIASH_PAR_NBASE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--nbase' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_ASSEMBLED" ] && [ ! -d "$(dirname "$VIASH_PAR_ASSEMBLED")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_ASSEMBLED")" +fi +if [ ! -z "$VIASH_PAR_UNASSEMBLED_FORWARD" ] && [ ! -d "$(dirname "$VIASH_PAR_UNASSEMBLED_FORWARD")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_UNASSEMBLED_FORWARD")" +fi +if [ ! -z "$VIASH_PAR_UNASSEMBLED_REVERSE" ] && [ ! -d "$(dirname "$VIASH_PAR_UNASSEMBLED_REVERSE")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_UNASSEMBLED_REVERSE")" +fi +if [ ! -z "$VIASH_PAR_DISCARDED" ] && [ ! -d "$(dirname "$VIASH_PAR_DISCARDED")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_DISCARDED")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_FORWARD_FASTQ" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FORWARD_FASTQ")" ) + VIASH_PAR_FORWARD_FASTQ=$(ViashDockerAutodetectMount "$VIASH_PAR_FORWARD_FASTQ") +fi +if [ ! -z "$VIASH_PAR_REVERSE_FASTQ" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REVERSE_FASTQ")" ) + VIASH_PAR_REVERSE_FASTQ=$(ViashDockerAutodetectMount "$VIASH_PAR_REVERSE_FASTQ") +fi +if [ ! -z "$VIASH_PAR_ASSEMBLED" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_ASSEMBLED")" ) + VIASH_PAR_ASSEMBLED=$(ViashDockerAutodetectMount "$VIASH_PAR_ASSEMBLED") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_ASSEMBLED" ) +fi +if [ ! -z "$VIASH_PAR_UNASSEMBLED_FORWARD" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_UNASSEMBLED_FORWARD")" ) + VIASH_PAR_UNASSEMBLED_FORWARD=$(ViashDockerAutodetectMount "$VIASH_PAR_UNASSEMBLED_FORWARD") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_UNASSEMBLED_FORWARD" ) +fi +if [ ! -z "$VIASH_PAR_UNASSEMBLED_REVERSE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_UNASSEMBLED_REVERSE")" ) + VIASH_PAR_UNASSEMBLED_REVERSE=$(ViashDockerAutodetectMount "$VIASH_PAR_UNASSEMBLED_REVERSE") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_UNASSEMBLED_REVERSE" ) +fi +if [ ! -z "$VIASH_PAR_DISCARDED" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_DISCARDED")" ) + VIASH_PAR_DISCARDED=$(ViashDockerAutodetectMount "$VIASH_PAR_DISCARDED") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_DISCARDED" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-pear-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_FORWARD_FASTQ+x} ]; then echo "${VIASH_PAR_FORWARD_FASTQ}" | sed "s#'#'\"'\"'#g;s#.*#par_forward_fastq='&'#" ; else echo "# par_forward_fastq="; fi ) +$( if [ ! -z ${VIASH_PAR_REVERSE_FASTQ+x} ]; then echo "${VIASH_PAR_REVERSE_FASTQ}" | sed "s#'#'\"'\"'#g;s#.*#par_reverse_fastq='&'#" ; else echo "# par_reverse_fastq="; fi ) +$( if [ ! -z ${VIASH_PAR_ASSEMBLED+x} ]; then echo "${VIASH_PAR_ASSEMBLED}" | sed "s#'#'\"'\"'#g;s#.*#par_assembled='&'#" ; else echo "# par_assembled="; fi ) +$( if [ ! -z ${VIASH_PAR_UNASSEMBLED_FORWARD+x} ]; then echo "${VIASH_PAR_UNASSEMBLED_FORWARD}" | sed "s#'#'\"'\"'#g;s#.*#par_unassembled_forward='&'#" ; else echo "# par_unassembled_forward="; fi ) +$( if [ ! -z ${VIASH_PAR_UNASSEMBLED_REVERSE+x} ]; then echo "${VIASH_PAR_UNASSEMBLED_REVERSE}" | sed "s#'#'\"'\"'#g;s#.*#par_unassembled_reverse='&'#" ; else echo "# par_unassembled_reverse="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARDED+x} ]; then echo "${VIASH_PAR_DISCARDED}" | sed "s#'#'\"'\"'#g;s#.*#par_discarded='&'#" ; else echo "# par_discarded="; fi ) +$( if [ ! -z ${VIASH_PAR_P_VALUE+x} ]; then echo "${VIASH_PAR_P_VALUE}" | sed "s#'#'\"'\"'#g;s#.*#par_p_value='&'#" ; else echo "# par_p_value="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_OVERLAP+x} ]; then echo "${VIASH_PAR_MIN_OVERLAP}" | sed "s#'#'\"'\"'#g;s#.*#par_min_overlap='&'#" ; else echo "# par_min_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_ASSEMBLY_LENGTH+x} ]; then echo "${VIASH_PAR_MAX_ASSEMBLY_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_max_assembly_length='&'#" ; else echo "# par_max_assembly_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ASSEMBLY_LENGTH+x} ]; then echo "${VIASH_PAR_MIN_ASSEMBLY_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_min_assembly_length='&'#" ; else echo "# par_min_assembly_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_TRIM_LENGTH+x} ]; then echo "${VIASH_PAR_MIN_TRIM_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_min_trim_length='&'#" ; else echo "# par_min_trim_length="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALITY_THRESHOLD+x} ]; then echo "${VIASH_PAR_QUALITY_THRESHOLD}" | sed "s#'#'\"'\"'#g;s#.*#par_quality_threshold='&'#" ; else echo "# par_quality_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_UNCALLED_BASE+x} ]; then echo "${VIASH_PAR_MAX_UNCALLED_BASE}" | sed "s#'#'\"'\"'#g;s#.*#par_max_uncalled_base='&'#" ; else echo "# par_max_uncalled_base="; fi ) +$( if [ ! -z ${VIASH_PAR_TEST_METHOD+x} ]; then echo "${VIASH_PAR_TEST_METHOD}" | sed "s#'#'\"'\"'#g;s#.*#par_test_method='&'#" ; else echo "# par_test_method="; fi ) +$( if [ ! -z ${VIASH_PAR_EMPERICAL_FREQS+x} ]; then echo "${VIASH_PAR_EMPERICAL_FREQS}" | sed "s#'#'\"'\"'#g;s#.*#par_emperical_freqs='&'#" ; else echo "# par_emperical_freqs="; fi ) +$( if [ ! -z ${VIASH_PAR_SCORE_METHOD+x} ]; then echo "${VIASH_PAR_SCORE_METHOD}" | sed "s#'#'\"'\"'#g;s#.*#par_score_method='&'#" ; else echo "# par_score_method="; fi ) +$( if [ ! -z ${VIASH_PAR_PHRED_BASE+x} ]; then echo "${VIASH_PAR_PHRED_BASE}" | sed "s#'#'\"'\"'#g;s#.*#par_phred_base='&'#" ; else echo "# par_phred_base="; fi ) +$( if [ ! -z ${VIASH_PAR_CAP+x} ]; then echo "${VIASH_PAR_CAP}" | sed "s#'#'\"'\"'#g;s#.*#par_cap='&'#" ; else echo "# par_cap="; fi ) +$( if [ ! -z ${VIASH_PAR_NBASE+x} ]; then echo "${VIASH_PAR_NBASE}" | sed "s#'#'\"'\"'#g;s#.*#par_nbase='&'#" ; else echo "# par_nbase="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +[[ "\$par_emperical_freqs" == "false" ]] && unset par_emperical_freqs +[[ "\$par_nbase" == "false" ]] && unset par_nbase + +if [[ "\${par_forward_fastq##*.}" == "gz" ]]; then + gunzip \$par_forward_fastq + par_forward_fastq=\${par_forward_fastq%.*} +fi +if [[ "\${par_reverse_fastq##*.}" == "gz" ]]; then + gunzip \$par_reverse_fastq + par_reverse_fastq=\${par_reverse_fastq%.*} +fi + +output_dir=\$(mktemp -d -p "\$meta_temp_dir" "pear.XXXXXX") + +pear \\ + -f "\$par_forward_fastq" \\ + -r "\$par_reverse_fastq" \\ + -o "\$output_dir" \\ + \${par_p_value:+-p "\${par_p_value}"} \\ + \${par_min_overlap:+-v "\${par_min_overlap}"} \\ + \${par_max_assembly_length:+-m "\${par_max_assembly_length}"} \\ + \${par_min_assembly_length:+-n "\${par_min_assembly_length}"} \\ + \${par_min_trim_length:+-t "\${par_min_trim_length}"} \\ + \${par_quality_threshold:+-q "\${par_quality_threshold}"} \\ + \${par_max_uncalled_base:+-u "\${par_max_uncalled_base}"} \\ + \${par_test_method:+-g "\${par_test_method}"} \\ + \${par_score_method:+-s "\${par_score_method}"} \\ + \${par_phred_base:+-b "\${par_phred_base}"} \\ + \${meta_memory_mb:+--memory "\${meta_memory_mb}M"} \\ + \${par_cap:+-c "\${par_cap}"} \\ + \${meta_cpus:+-j "\${meta_cpus}"} \\ + \${par_emperical_freqs:+-e} \\ + \${par_nbase:+-z} + + +if [[ "\${par_assembled##*.}" == "gz" ]]; then + gzip -9 -c \${output_dir}.assembled.fastq > \${par_assembled} +else + mv \${output_dir}.assembled.fastq \${par_assembled} +fi + +if [[ "\${par_unassembled_forward##*.}" == "gz" ]]; then + gzip -9 -c \${output_dir}.unassembled.forward.fastq > \${par_unassembled_forward} +else + mv \${output_dir}.unassembled.forward.fastq \${par_unassembled_forward} +fi + +if [[ "\${par_unassembled_reverse##*.}" == "gz" ]]; then + gzip -9 -c \${output_dir}.unassembled.reverse.fastq > \${par_unassembled_reverse} +else + mv \${output_dir}.unassembled.reverse.fastq \${par_unassembled_reverse} +fi + +if [[ "\${par_discarded##*.}" == "gz" ]]; then + gzip -9 -c \${output_dir}.discarded.fastq > \${par_discarded} +else + mv \${output_dir}.discarded.fastq \${par_discarded} +fi +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_FORWARD_FASTQ" ]; then + VIASH_PAR_FORWARD_FASTQ=$(ViashDockerStripAutomount "$VIASH_PAR_FORWARD_FASTQ") + fi + if [ ! -z "$VIASH_PAR_REVERSE_FASTQ" ]; then + VIASH_PAR_REVERSE_FASTQ=$(ViashDockerStripAutomount "$VIASH_PAR_REVERSE_FASTQ") + fi + if [ ! -z "$VIASH_PAR_ASSEMBLED" ]; then + VIASH_PAR_ASSEMBLED=$(ViashDockerStripAutomount "$VIASH_PAR_ASSEMBLED") + fi + if [ ! -z "$VIASH_PAR_UNASSEMBLED_FORWARD" ]; then + VIASH_PAR_UNASSEMBLED_FORWARD=$(ViashDockerStripAutomount "$VIASH_PAR_UNASSEMBLED_FORWARD") + fi + if [ ! -z "$VIASH_PAR_UNASSEMBLED_REVERSE" ]; then + VIASH_PAR_UNASSEMBLED_REVERSE=$(ViashDockerStripAutomount "$VIASH_PAR_UNASSEMBLED_REVERSE") + fi + if [ ! -z "$VIASH_PAR_DISCARDED" ]; then + VIASH_PAR_DISCARDED=$(ViashDockerStripAutomount "$VIASH_PAR_DISCARDED") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_ASSEMBLED" ] && [ ! -e "$VIASH_PAR_ASSEMBLED" ]; then + ViashError "Output file '$VIASH_PAR_ASSEMBLED' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_UNASSEMBLED_FORWARD" ] && [ ! -e "$VIASH_PAR_UNASSEMBLED_FORWARD" ]; then + ViashError "Output file '$VIASH_PAR_UNASSEMBLED_FORWARD' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_UNASSEMBLED_REVERSE" ] && [ ! -e "$VIASH_PAR_UNASSEMBLED_REVERSE" ]; then + ViashError "Output file '$VIASH_PAR_UNASSEMBLED_REVERSE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_DISCARDED" ] && [ ! -e "$VIASH_PAR_DISCARDED" ]; then + ViashError "Output file '$VIASH_PAR_DISCARDED' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/salmon/salmon_index/.config.vsh.yaml b/target/executable/salmon/salmon_index/.config.vsh.yaml new file mode 100644 index 00000000..131fc613 --- /dev/null +++ b/target/executable/salmon/salmon_index/.config.vsh.yaml @@ -0,0 +1,289 @@ +name: "salmon_index" +namespace: "salmon" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--genome" + description: "Genome of the organism to prepare the set of decoy sequences. Required\ + \ to build decoy-aware transccriptome.\n" + info: null + example: + - "genome.fasta" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--transcripts" + alternatives: + - "-t" + description: "Transcript fasta file.\n" + info: null + example: + - "transcriptome.fasta" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--kmer_len" + alternatives: + - "-k" + description: "The size of k-mers that should be used for the quasi index.\n" + info: null + example: + - 31 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--gencode" + description: "This flag will expect the input transcript fasta to be in GENCODE\ + \ format, and will split the transcript name at the first '|' character. These\ + \ reduced names will be used in the output and when looking for these transcripts\ + \ in a gene to transcript GTF.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--features" + description: "This flag will expect the input reference to be in the tsv file\ + \ format, and will split the feature name at the first 'tab' character. These\ + \ reduced names will be used in the output and when looking for the sequence\ + \ of the features.GTF.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--keep_duplicates" + description: "This flag will disable the default indexing behavior of discarding\ + \ sequence-identical duplicate transcripts. If this flag is passed, then duplicate\ + \ transcripts that appear in the input will be retained and quantified separately.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--keep_fixed_fasta" + description: "Retain the fixed fasta file (without short transcripts and duplicates,\ + \ clipped, etc.) generated during indexing.\n" + info: null + direction: "input" + - type: "integer" + name: "--filter_size" + alternatives: + - "-f" + description: "The size of the Bloom filter that will be used by TwoPaCo during\ + \ indexing. The filter will be of size 2^{filter_size}. The default value of\ + \ -1 means that the filter size will be automatically set based on the number\ + \ of distinct k-mers in the input, as estimated by nthll.\n" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--sparse" + description: "Build the index using a sparse sampling of k-mer positions This\ + \ will require less memory (especially during quantification), but will take\ + \ longer to construct and can slow down mapping / alignment.\n" + info: null + direction: "input" + - type: "file" + name: "--decoys" + alternatives: + - "-d" + description: "Treat these sequences ids from the reference as the decoys that\ + \ may have sequence homologous to some known transcript. For example in case\ + \ of the genome, provide a list of chromosome names (one per line).\n" + info: null + example: + - "decoys.txt" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_clip" + description: "Don't clip poly-A tails from the ends of target sequences.\n" + info: null + direction: "input" + - type: "string" + name: "--type" + alternatives: + - "-n" + description: "The type of index to build; the only option is \"puff\" in this\ + \ version of salmon.\n" + info: null + example: + - "puff" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output" + arguments: + - type: "file" + name: "--index" + alternatives: + - "-i" + description: "Salmon index\n" + info: null + example: + - "Salmon_index" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Salmon is a tool for wicked-fast transcript quantification from RNA-seq\ + \ data. It can either make use of pre-computed alignments (in the form of a SAM/BAM\ + \ file) to the transcripts rather than the raw reads, or can be run in the mapping-based\ + \ mode. This component creates a salmon index for the transcriptome to use Salmon\ + \ in the mapping-based mode. It is generally recommend that you build a decoy-aware\ + \ transcriptome file. This is done using the entire genome of the organism as the\ + \ decoy sequence by concatenating the genome to the end of the transcriptome to\ + \ be indexed and populating the decoys.txt file with the chromosome names.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "Transcriptome" +- "Index" +license: "GPL-3.0" +references: + doi: + - "10.1038/nmeth.4197" +links: + repository: "https://github.com/COMBINE-lab/salmon" + homepage: "https://salmon.readthedocs.io/en/latest/salmon.html" + documentation: "https://salmon.readthedocs.io/en/latest/salmon.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/salmon:1.10.2--hecfa306_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "salmon index -v 2>&1 | sed 's/salmon \\([0-9.]*\\)/salmon: \\1/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/salmon/salmon_index/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/salmon/salmon_index" + executable: "target/executable/salmon/salmon_index/salmon_index" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/salmon/salmon_index/salmon_index b/target/executable/salmon/salmon_index/salmon_index new file mode 100755 index 00000000..ef87e13f --- /dev/null +++ b/target/executable/salmon/salmon_index/salmon_index @@ -0,0 +1,1388 @@ +#!/usr/bin/env bash + +# salmon_index main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="salmon_index" +VIASH_META_FUNCTIONALITY_NAME="salmon_index" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "salmon_index main" + echo "" + echo "Salmon is a tool for wicked-fast transcript quantification from RNA-seq data. It" + echo "can either make use of pre-computed alignments (in the form of a SAM/BAM file)" + echo "to the transcripts rather than the raw reads, or can be run in the mapping-based" + echo "mode. This component creates a salmon index for the transcriptome to use Salmon" + echo "in the mapping-based mode. It is generally recommend that you build a" + echo "decoy-aware transcriptome file. This is done using the entire genome of the" + echo "organism as the decoy sequence by concatenating the genome to the end of the" + echo "transcriptome to be indexed and populating the decoys.txt file with the" + echo "chromosome names." + echo "" + echo "Inputs:" + echo " --genome" + echo " type: file, file must exist" + echo " example: genome.fasta" + echo " Genome of the organism to prepare the set of decoy sequences. Required" + echo " to build decoy-aware transccriptome." + echo "" + echo " -t, --transcripts" + echo " type: file, required parameter, file must exist" + echo " example: transcriptome.fasta" + echo " Transcript fasta file." + echo "" + echo " -k, --kmer_len" + echo " type: integer" + echo " example: 31" + echo " The size of k-mers that should be used for the quasi index." + echo "" + echo " --gencode" + echo " type: boolean_true" + echo " This flag will expect the input transcript fasta to be in GENCODE" + echo " format, and will split the transcript name at the first '|' character." + echo " These reduced names will be used in the output and when looking for" + echo " these transcripts in a gene to transcript GTF." + echo "" + echo " --features" + echo " type: boolean_true" + echo " This flag will expect the input reference to be in the tsv file format," + echo " and will split the feature name at the first 'tab' character. These" + echo " reduced names will be used in the output and when looking for the" + echo " sequence of the features.GTF." + echo "" + echo " --keep_duplicates" + echo " type: boolean_true" + echo " This flag will disable the default indexing behavior of discarding" + echo " sequence-identical duplicate transcripts. If this flag is passed, then" + echo " duplicate transcripts that appear in the input will be retained and" + echo " quantified separately." + echo "" + echo " --keep_fixed_fasta" + echo " type: boolean_true" + echo " Retain the fixed fasta file (without short transcripts and duplicates," + echo " clipped, etc.) generated during indexing." + echo "" + echo " -f, --filter_size" + echo " type: integer" + echo " example: -1" + echo " The size of the Bloom filter that will be used by TwoPaCo during" + echo " indexing. The filter will be of size 2^{filter_size}. The default value" + echo " of -1 means that the filter size will be automatically set based on the" + echo " number of distinct k-mers in the input, as estimated by nthll." + echo "" + echo " --sparse" + echo " type: boolean_true" + echo " Build the index using a sparse sampling of k-mer positions This will" + echo " require less memory (especially during quantification), but will take" + echo " longer to construct and can slow down mapping / alignment." + echo "" + echo " -d, --decoys" + echo " type: file, file must exist" + echo " example: decoys.txt" + echo " Treat these sequences ids from the reference as the decoys that may have" + echo " sequence homologous to some known transcript. For example in case of the" + echo " genome, provide a list of chromosome names (one per line)." + echo "" + echo " --no_clip" + echo " type: boolean_true" + echo " Don't clip poly-A tails from the ends of target sequences." + echo "" + echo " -n, --type" + echo " type: string" + echo " example: puff" + echo " The type of index to build; the only option is \"puff\" in this version of" + echo " salmon." + echo "" + echo "Output:" + echo " -i, --index" + echo " type: file, required parameter, output, file must exist" + echo " example: Salmon_index" + echo " Salmon index" +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/salmon:1.10.2--hecfa306_0 +ENTRYPOINT [] +RUN salmon index -v 2>&1 | sed 's/salmon \([0-9.]*\)/salmon: \1/' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component salmon salmon_index" +LABEL org.opencontainers.image.created="2024-06-24T08:36:42Z" +LABEL org.opencontainers.image.source="https://github.com/COMBINE-lab/salmon" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "salmon_index main" + exit + ;; + --genome) + [ -n "$VIASH_PAR_GENOME" ] && ViashError Bad arguments for option \'--genome\': \'$VIASH_PAR_GENOME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOME="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genome. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genome=*) + [ -n "$VIASH_PAR_GENOME" ] && ViashError Bad arguments for option \'--genome=*\': \'$VIASH_PAR_GENOME\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOME=$(ViashRemoveFlags "$1") + shift 1 + ;; + --transcripts) + [ -n "$VIASH_PAR_TRANSCRIPTS" ] && ViashError Bad arguments for option \'--transcripts\': \'$VIASH_PAR_TRANSCRIPTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRANSCRIPTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --transcripts. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --transcripts=*) + [ -n "$VIASH_PAR_TRANSCRIPTS" ] && ViashError Bad arguments for option \'--transcripts=*\': \'$VIASH_PAR_TRANSCRIPTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRANSCRIPTS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -t) + [ -n "$VIASH_PAR_TRANSCRIPTS" ] && ViashError Bad arguments for option \'-t\': \'$VIASH_PAR_TRANSCRIPTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRANSCRIPTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -t. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --kmer_len) + [ -n "$VIASH_PAR_KMER_LEN" ] && ViashError Bad arguments for option \'--kmer_len\': \'$VIASH_PAR_KMER_LEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KMER_LEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --kmer_len. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --kmer_len=*) + [ -n "$VIASH_PAR_KMER_LEN" ] && ViashError Bad arguments for option \'--kmer_len=*\': \'$VIASH_PAR_KMER_LEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KMER_LEN=$(ViashRemoveFlags "$1") + shift 1 + ;; + -k) + [ -n "$VIASH_PAR_KMER_LEN" ] && ViashError Bad arguments for option \'-k\': \'$VIASH_PAR_KMER_LEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KMER_LEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -k. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --gencode) + [ -n "$VIASH_PAR_GENCODE" ] && ViashError Bad arguments for option \'--gencode\': \'$VIASH_PAR_GENCODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENCODE=true + shift 1 + ;; + --features) + [ -n "$VIASH_PAR_FEATURES" ] && ViashError Bad arguments for option \'--features\': \'$VIASH_PAR_FEATURES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FEATURES=true + shift 1 + ;; + --keep_duplicates) + [ -n "$VIASH_PAR_KEEP_DUPLICATES" ] && ViashError Bad arguments for option \'--keep_duplicates\': \'$VIASH_PAR_KEEP_DUPLICATES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KEEP_DUPLICATES=true + shift 1 + ;; + --keep_fixed_fasta) + [ -n "$VIASH_PAR_KEEP_FIXED_FASTA" ] && ViashError Bad arguments for option \'--keep_fixed_fasta\': \'$VIASH_PAR_KEEP_FIXED_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KEEP_FIXED_FASTA=true + shift 1 + ;; + --filter_size) + [ -n "$VIASH_PAR_FILTER_SIZE" ] && ViashError Bad arguments for option \'--filter_size\': \'$VIASH_PAR_FILTER_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_SIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --filter_size. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --filter_size=*) + [ -n "$VIASH_PAR_FILTER_SIZE" ] && ViashError Bad arguments for option \'--filter_size=*\': \'$VIASH_PAR_FILTER_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_SIZE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_FILTER_SIZE" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_FILTER_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_SIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -f. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sparse) + [ -n "$VIASH_PAR_SPARSE" ] && ViashError Bad arguments for option \'--sparse\': \'$VIASH_PAR_SPARSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPARSE=true + shift 1 + ;; + --decoys) + [ -n "$VIASH_PAR_DECOYS" ] && ViashError Bad arguments for option \'--decoys\': \'$VIASH_PAR_DECOYS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DECOYS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --decoys. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --decoys=*) + [ -n "$VIASH_PAR_DECOYS" ] && ViashError Bad arguments for option \'--decoys=*\': \'$VIASH_PAR_DECOYS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DECOYS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -d) + [ -n "$VIASH_PAR_DECOYS" ] && ViashError Bad arguments for option \'-d\': \'$VIASH_PAR_DECOYS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DECOYS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -d. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --no_clip) + [ -n "$VIASH_PAR_NO_CLIP" ] && ViashError Bad arguments for option \'--no_clip\': \'$VIASH_PAR_NO_CLIP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_CLIP=true + shift 1 + ;; + --type) + [ -n "$VIASH_PAR_TYPE" ] && ViashError Bad arguments for option \'--type\': \'$VIASH_PAR_TYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --type. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --type=*) + [ -n "$VIASH_PAR_TYPE" ] && ViashError Bad arguments for option \'--type=*\': \'$VIASH_PAR_TYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TYPE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -n) + [ -n "$VIASH_PAR_TYPE" ] && ViashError Bad arguments for option \'-n\': \'$VIASH_PAR_TYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -n. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --index) + [ -n "$VIASH_PAR_INDEX" ] && ViashError Bad arguments for option \'--index\': \'$VIASH_PAR_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --index. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --index=*) + [ -n "$VIASH_PAR_INDEX" ] && ViashError Bad arguments for option \'--index=*\': \'$VIASH_PAR_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX=$(ViashRemoveFlags "$1") + shift 1 + ;; + -i) + [ -n "$VIASH_PAR_INDEX" ] && ViashError Bad arguments for option \'-i\': \'$VIASH_PAR_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -i. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/salmon/salmon_index:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_TRANSCRIPTS+x} ]; then + ViashError '--transcripts' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_INDEX+x} ]; then + ViashError '--index' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_GENCODE+x} ]; then + VIASH_PAR_GENCODE="false" +fi +if [ -z ${VIASH_PAR_FEATURES+x} ]; then + VIASH_PAR_FEATURES="false" +fi +if [ -z ${VIASH_PAR_KEEP_DUPLICATES+x} ]; then + VIASH_PAR_KEEP_DUPLICATES="false" +fi +if [ -z ${VIASH_PAR_KEEP_FIXED_FASTA+x} ]; then + VIASH_PAR_KEEP_FIXED_FASTA="false" +fi +if [ -z ${VIASH_PAR_SPARSE+x} ]; then + VIASH_PAR_SPARSE="false" +fi +if [ -z ${VIASH_PAR_NO_CLIP+x} ]; then + VIASH_PAR_NO_CLIP="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_GENOME" ] && [ ! -e "$VIASH_PAR_GENOME" ]; then + ViashError "Input file '$VIASH_PAR_GENOME' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_TRANSCRIPTS" ] && [ ! -e "$VIASH_PAR_TRANSCRIPTS" ]; then + ViashError "Input file '$VIASH_PAR_TRANSCRIPTS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_DECOYS" ] && [ ! -e "$VIASH_PAR_DECOYS" ]; then + ViashError "Input file '$VIASH_PAR_DECOYS' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_KMER_LEN" ]]; then + if ! [[ "$VIASH_PAR_KMER_LEN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--kmer_len' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GENCODE" ]]; then + if ! [[ "$VIASH_PAR_GENCODE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--gencode' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FEATURES" ]]; then + if ! [[ "$VIASH_PAR_FEATURES" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--features' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_KEEP_DUPLICATES" ]]; then + if ! [[ "$VIASH_PAR_KEEP_DUPLICATES" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--keep_duplicates' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_KEEP_FIXED_FASTA" ]]; then + if ! [[ "$VIASH_PAR_KEEP_FIXED_FASTA" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--keep_fixed_fasta' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FILTER_SIZE" ]]; then + if ! [[ "$VIASH_PAR_FILTER_SIZE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--filter_size' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SPARSE" ]]; then + if ! [[ "$VIASH_PAR_SPARSE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--sparse' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_CLIP" ]]; then + if ! [[ "$VIASH_PAR_NO_CLIP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_clip' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_INDEX" ] && [ ! -d "$(dirname "$VIASH_PAR_INDEX")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_INDEX")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_GENOME" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_GENOME")" ) + VIASH_PAR_GENOME=$(ViashDockerAutodetectMount "$VIASH_PAR_GENOME") +fi +if [ ! -z "$VIASH_PAR_TRANSCRIPTS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_TRANSCRIPTS")" ) + VIASH_PAR_TRANSCRIPTS=$(ViashDockerAutodetectMount "$VIASH_PAR_TRANSCRIPTS") +fi +if [ ! -z "$VIASH_PAR_DECOYS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_DECOYS")" ) + VIASH_PAR_DECOYS=$(ViashDockerAutodetectMount "$VIASH_PAR_DECOYS") +fi +if [ ! -z "$VIASH_PAR_INDEX" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INDEX")" ) + VIASH_PAR_INDEX=$(ViashDockerAutodetectMount "$VIASH_PAR_INDEX") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_INDEX" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-salmon_index-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +set -e + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_GENOME+x} ]; then echo "${VIASH_PAR_GENOME}" | sed "s#'#'\"'\"'#g;s#.*#par_genome='&'#" ; else echo "# par_genome="; fi ) +$( if [ ! -z ${VIASH_PAR_TRANSCRIPTS+x} ]; then echo "${VIASH_PAR_TRANSCRIPTS}" | sed "s#'#'\"'\"'#g;s#.*#par_transcripts='&'#" ; else echo "# par_transcripts="; fi ) +$( if [ ! -z ${VIASH_PAR_KMER_LEN+x} ]; then echo "${VIASH_PAR_KMER_LEN}" | sed "s#'#'\"'\"'#g;s#.*#par_kmer_len='&'#" ; else echo "# par_kmer_len="; fi ) +$( if [ ! -z ${VIASH_PAR_GENCODE+x} ]; then echo "${VIASH_PAR_GENCODE}" | sed "s#'#'\"'\"'#g;s#.*#par_gencode='&'#" ; else echo "# par_gencode="; fi ) +$( if [ ! -z ${VIASH_PAR_FEATURES+x} ]; then echo "${VIASH_PAR_FEATURES}" | sed "s#'#'\"'\"'#g;s#.*#par_features='&'#" ; else echo "# par_features="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_DUPLICATES+x} ]; then echo "${VIASH_PAR_KEEP_DUPLICATES}" | sed "s#'#'\"'\"'#g;s#.*#par_keep_duplicates='&'#" ; else echo "# par_keep_duplicates="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_FIXED_FASTA+x} ]; then echo "${VIASH_PAR_KEEP_FIXED_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_keep_fixed_fasta='&'#" ; else echo "# par_keep_fixed_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTER_SIZE+x} ]; then echo "${VIASH_PAR_FILTER_SIZE}" | sed "s#'#'\"'\"'#g;s#.*#par_filter_size='&'#" ; else echo "# par_filter_size="; fi ) +$( if [ ! -z ${VIASH_PAR_SPARSE+x} ]; then echo "${VIASH_PAR_SPARSE}" | sed "s#'#'\"'\"'#g;s#.*#par_sparse='&'#" ; else echo "# par_sparse="; fi ) +$( if [ ! -z ${VIASH_PAR_DECOYS+x} ]; then echo "${VIASH_PAR_DECOYS}" | sed "s#'#'\"'\"'#g;s#.*#par_decoys='&'#" ; else echo "# par_decoys="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_CLIP+x} ]; then echo "${VIASH_PAR_NO_CLIP}" | sed "s#'#'\"'\"'#g;s#.*#par_no_clip='&'#" ; else echo "# par_no_clip="; fi ) +$( if [ ! -z ${VIASH_PAR_TYPE+x} ]; then echo "${VIASH_PAR_TYPE}" | sed "s#'#'\"'\"'#g;s#.*#par_type='&'#" ; else echo "# par_type="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX+x} ]; then echo "${VIASH_PAR_INDEX}" | sed "s#'#'\"'\"'#g;s#.*#par_index='&'#" ; else echo "# par_index="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +[[ "\$par_gencode" == "false" ]] && unset par_gencode +[[ "\$par_features" == "false" ]] && unset par_features +[[ "\$par_keep_duplicates" == "false" ]] && unset par_keep_duplicates +[[ "\$par_keep_fixed_fasta" == "false" ]] && unset par_keep_fixed_fasta +[[ "\$par_sparse" == "false" ]] && unset par_sparse +[[ "\$par_no_clip" == "false" ]] && unset par_no_clip + +tmp_dir=\$(mktemp -d -p "\$meta_temp_dir" "\${meta_functionality_name}_XXXXXX") +mkdir -p "\$tmp_dir/temp" + +if [[ -f "\$par_genome" ]] && [[ ! "\$par_decoys" ]]; then + filename="\$(basename -- \$par_genome)" + decoys="decoys.txt" + if [ \${filename##*.} == "gz" ]; then + grep '^>' <(gunzip -c \$par_genome) | cut -d ' ' -f 1 > \$decoys + gentrome="gentrome.fa.gz" + else + grep '^>' \$par_genome | cut -d ' ' -f 1 > \$decoys + gentrome="gentrome.fa" + fi + sed -i.bak -e 's/>//g' \$decoys + cat \$par_transcripts \$par_genome > \$gentrome +else + gentrome=\$par_transcripts + decoys=\$par_decoys +fi + +salmon index \\ + -t "\$gentrome" \\ + --tmpdir "\$tmp_dir/temp" \\ + \${meta_cpus:+--threads "\${meta_cpus}"} \\ + -i "\$par_index" \\ + \${par_kmer_len:+-k "\${par_kmer_len}"} \\ + \${par_gencode:+--gencode} \\ + \${par_features:+--features} \\ + \${par_keep_duplicates:+--keepDuplicates} \\ + \${par_keep_fixed_fasta:+--keepFixedFasta} \\ + \${par_filter_size:+-f "\${par_filter_size}"} \\ + \${par_sparse:+--sparse} \\ + \${decoys:+-d "\${decoys}"} \\ + \${par_no_clip:+--no-clip} \\ + \${par_type:+--type "\${par_type}"} +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_GENOME" ]; then + VIASH_PAR_GENOME=$(ViashDockerStripAutomount "$VIASH_PAR_GENOME") + fi + if [ ! -z "$VIASH_PAR_TRANSCRIPTS" ]; then + VIASH_PAR_TRANSCRIPTS=$(ViashDockerStripAutomount "$VIASH_PAR_TRANSCRIPTS") + fi + if [ ! -z "$VIASH_PAR_DECOYS" ]; then + VIASH_PAR_DECOYS=$(ViashDockerStripAutomount "$VIASH_PAR_DECOYS") + fi + if [ ! -z "$VIASH_PAR_INDEX" ]; then + VIASH_PAR_INDEX=$(ViashDockerStripAutomount "$VIASH_PAR_INDEX") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INDEX" ] && [ ! -e "$VIASH_PAR_INDEX" ]; then + ViashError "Output file '$VIASH_PAR_INDEX' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/salmon/salmon_quant/.config.vsh.yaml b/target/executable/salmon/salmon_quant/.config.vsh.yaml new file mode 100644 index 00000000..885dac66 --- /dev/null +++ b/target/executable/salmon/salmon_quant/.config.vsh.yaml @@ -0,0 +1,1185 @@ +name: "salmon_quant" +namespace: "salmon" +version: "main" +argument_groups: +- name: "Common input options" + arguments: + - type: "string" + name: "--lib_type" + alternatives: + - "-l" + description: "Format string describing the library.\nThe library type string consists\ + \ of three parts: \n1. Relative orientation of the reads: This part is only\ + \ provided if the library is paired-end, THe possible options are\n I = inward\n\ + \ O = outward\n M = matching\n2. Strandedness of the library: This part specifies\ + \ whether the protocol is stranded or unstranded. The options are:\n S = stranded\n\ + \ U = unstranded\n3. Directionality of the reads: If the library is stranded,\ + \ the final part of the library string is used to specify the strand from which\ + \ the read originates. The possible values are\n F = read 1 (or single-end\ + \ read) comes from the forward strand\n R = read 1 (or single-end read) comes\ + \ from the reverse strand\n" + info: null + default: + - "A" + required: false + choices: + - "A" + - "U" + - "SF" + - "SR" + - "IU" + - "IS" + - "ISF" + - "ISR" + - "OU" + - "OS" + - "OSF" + - "OSR" + - "MU" + - "MS" + - "MSF" + - "MSR" + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Mapping input options" + arguments: + - type: "file" + name: "--index" + alternatives: + - "-i" + description: "Salmon index.\n" + info: null + example: + - "transcriptome_index" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unmated_reads" + alternatives: + - "-r" + description: "List of files containing unmated reads of (e.g. single-end reads).\n" + info: null + example: + - "sample.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--mates1" + alternatives: + - "-m1" + description: "File containing the #1 mates.\n" + info: null + example: + - "sample_1.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--mates2" + alternatives: + - "-m2" + description: "File containing the #2 mates.\n" + info: null + example: + - "sample_2.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Alignment input options" + arguments: + - type: "boolean_true" + name: "--discard_orphans" + description: "Discard orphan alignments in the input [for alignment-based mode\ + \ only]. If this flag is passed, then only paired alignments will be considered\ + \ toward quantification estimates. The default behavior is to consider orphan\ + \ alignments if no valid paired mappings exist.\n" + info: null + direction: "input" + - type: "file" + name: "--alignments" + alternatives: + - "-a" + description: "Input alignment (BAM) file(s).\n" + info: null + example: + - "sample.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--eqclasses" + alternatives: + - "-e" + description: "input salmon weighted equivalence class file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--targets" + alternatives: + - "-t" + description: "FASTA format file containing target transcripts.\n" + info: null + example: + - "transcripts.fasta" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--ont" + description: "Use alignment model for Oxford Nanopore long reads\n" + info: null + direction: "input" +- name: "Output" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output quantification directory.\n" + info: null + example: + - "quant_output" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--quant_results" + description: "Salmon quantification file.\n" + info: null + example: + - "quant.sf" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Basic options" + arguments: + - type: "boolean_true" + name: "--seq_bias" + description: "Perform sequence-specific bias correction.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--gc_bias" + description: "Perform fragment GC bias correction [beta for single-end reads].\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--pos_bias" + description: "Perform positional bias correction.\n" + info: null + direction: "input" + - type: "double" + name: "--incompat_prior" + description: "Set the prior probability that an alignment that disagrees with\ + \ the specified library type (--lib_type) results from the true fragment origin.\ + \ Setting this to 0 specifies that alignments that disagree with the library\ + \ type should be \"impossible\", while setting it to 1 says that alignments\ + \ that disagree with the library type are no less likely than those that do.\n" + info: null + example: + - 0.0 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--gene_map" + alternatives: + - "-g" + description: "File containing a mapping of transcripts to genes. If this file\ + \ is provided salmon will output both quant.sf and quant.genes.sf files, where\ + \ the latter contains aggregated gene-level abundance estimates. The transcript\ + \ to gene mapping should be provided as either a GTF file, or a in a simple\ + \ tab-delimited format where each line contains the name of a transcript and\ + \ the gene to which it belongs separated by a tab. The extension of the file\ + \ is used to determine how the file should be parsed. Files ending in '.gtf',\ + \ '.gff' or '.gff3' are assumed to be in GTF format; files with any other extension\ + \ are assumed to be in the simple format. In GTF / GFF format, the \"transcript_id\"\ + \ is assumed to contain the transcript identifier and the \"gene_id\" is assumed\ + \ to contain the corresponding gene identifier.\n" + info: null + example: + - "gene_map.gtf" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--aux_target_file" + description: "A file containing a list of \"auxiliary\" targets. These are valid\ + \ targets (i.e., not decoys) to which fragments are allowed to map and be assigned,\ + \ and which will be quantified, but for which auxiliary models like sequence-specific\ + \ and fragment-GC bias correction should not be applied.\n" + info: null + example: + - "auxilary_targets.txt" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--meta" + description: "If you're using Salmon on a metagenomic dataset, consider setting\ + \ this flag to disable parts of the abundance estimation model that make less\ + \ sense for metagenomic data.\n" + info: null + direction: "input" + - type: "double" + name: "--score_exp" + description: "The factor by which sub-optimal alignment scores are downweighted\ + \ to produce a probability. If the best alignment score for the current read\ + \ is S, and the score for a particular alignment is w, then the probability\ + \ will be computed porportional to exp( - scoreExp * (S-w) ).\n" + info: null + example: + - 1.0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Options specific to mapping mode" + arguments: + - type: "boolean_true" + name: "--discard_orphans_quasi" + description: "[selective-alignment mode only] \nDiscard orphan mappings in selective-alignment\ + \ mode. If this flag is passed then only paired mappings will be considered\ + \ toward quantification estimates. The default behavior is to consider orphan\ + \ mappings if no valid paired mappings exist. This flag is independent of the\ + \ option to write the orphaned mappings to file (--writeOrphanLinks).\n" + info: null + direction: "input" + - type: "double" + name: "--consensus_slack" + description: "[selective-alignment mode only] \nThe amount of slack allowed in\ + \ the selective-alignment filtering mechanism. If this is set to a fraction,\ + \ X, greater than 0 (and in [0,1)), then uniMEM chains with scores below (100\ + \ * X)% of the best chain score for a read, and read pairs with a sum of chain\ + \ scores below (100 * X)% of the best chain score for a read pair will be discounted\ + \ as a mapping candidates. The default value of this option is 0.35.\n" + info: null + example: + - 0.35 + required: false + min: 0.0 + max: 0.999999999 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--pre_merge_chain_sub_thresh" + description: "[selective-alignment mode only] \nThe threshold of sub-optimal chains,\ + \ compared to the best chain on a given target, that will be retained and passed\ + \ to the next phase of mapping. Specifically, if the best chain for a read (or\ + \ read-end in paired-end mode) to target t has score X_t, then all chains for\ + \ this read with score >= X_t * preMergeChainSubThresh will be retained and\ + \ passed to subsequent mapping phases. This value must be in the range [0,\ + \ 1].\n" + info: null + example: + - 0.75 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--post_merge_chain_sub_thresh" + description: "[selective-alignment mode only] \nThe threshold of sub-optimal chains,\ + \ compared to the best chain on a given target, that will be retained and passed\ + \ to the next phase of mapping. This is different than post_merge_chain_sub_thresh,\ + \ because this is applied to pairs of chains (from the ends of paired-end reads)\ + \ after merging (i.e. after checking concordancy constraints etc.). Specifically,\ + \ if the best chain pair to target t has score X_t, then all chain pairs for\ + \ this read pair with score >= X_t * post_merge_chain_sub_thresh will be retained\ + \ and passed to subsequent mapping phases. This value must be in the range [0,\ + \ 1]. Note: This option is only meaningful for paired-end libraries, and is\ + \ ignored for single-end libraries.\n" + info: null + example: + - 0.9 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--orphan_chain_sub_thresh" + description: "[selective-alignment mode only]\nThis threshold sets a global sub-optimality\ + \ threshold for chains corresponding to orphan mappings. That is, if the merging\ + \ procedure results in no concordant mappings then only orphan mappings with\ + \ a chain score >= orphan_chain_sub_thresh * bestChainScore will be retained\ + \ and passed to subsequent mapping phases. This value must be in the range [0,\ + \ 1]. Note: This option is only meaningful for paired-end libraries, and is\ + \ ignored for single-end libraries.\n" + info: null + example: + - 0.95 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--min_score_fraction" + description: "[selective-alignment mode only]\nThe fraction of the optimal possible\ + \ alignment score that a mapping must achieve in order to be considered \"valid\"\ + \ --- should be in (0,1]. Default 0.65\n" + info: null + example: + - 0.65 + required: false + min: 1.0E-9 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--mismatch_seed_skip" + description: "[selective-alignment mode only]\nAfter a k-mer hit is extended to\ + \ a uni-MEM, the uni-MEM extension can terminate for one of 3 reasons; the end\ + \ of the read, the end of the unitig, or a mismatch. If the extension ends because\ + \ of a mismatch, this is likely the result of a sequencing error. To avoid looking\ + \ up many k-mers that will likely fail to be located in the index, the search\ + \ procedure skips by a factor of mismatch_seed_skip until it either (1) finds\ + \ another match or (2) is k-bases past the mismatch position. This value controls\ + \ that skip length. A smaller value can increase sensitivity, while a larger\ + \ value can speed up seeding.\n" + info: null + example: + - 3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--disable_chaining_heuristic" + description: "[selective-alignment mode only] \nBy default, the heuristic of (Li\ + \ 2018) is implemented, which terminates the chaining DP once a given number\ + \ of valid backpointers are found. This speeds up the seed (MEM) chaining step,\ + \ but may result in sub-optimal chains in complex situations (e.g. sequences\ + \ with many repeats and overlapping repeats). Passing this flag will disable\ + \ the chaining heuristic, and perform the full chaining dynamic program, guaranteeing\ + \ the optimal chain is found in this step.\n" + info: null + direction: "input" + - type: "double" + name: "--decoy_threshold" + description: "[selective-alignment mode only]\nFor an alignemnt to an annotated\ + \ transcript to be considered invalid, it must have an alignment score < (decoy_threshold\ + \ * bestDecoyScore). A value of 1.0 means that any alignment strictly worse\ + \ than the best decoy alignment will be discarded. A smaller value will allow\ + \ reads to be allocated to transcripts even if they strictly align better to\ + \ the decoy sequence.\n" + info: null + example: + - 1.0 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--ma" + description: "[selective-alignment mode only]\nThe value given to a match between\ + \ read and reference nucleotides in an alignment.\n" + info: null + example: + - 2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--mp" + description: "[selective-alignment mode only]\nThe value given to a mis-match\ + \ between read and reference nucleotides in an alignment.\n" + info: null + example: + - -4 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--go" + description: "[selective-alignment mode only]\nThe value given to a gap opening\ + \ in an alignment.\n" + info: null + example: + - 6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--ge" + description: "[selective-alignment mode only]\nThe value given to a gap extension\ + \ in an alignment.\n" + info: null + example: + - 2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bandwidth" + description: "[selective-alignment mode only]\nThe value used for the bandwidth\ + \ passed to ksw2. A smaller bandwidth can make the alignment verification run\ + \ more quickly, but could possibly miss valid alignments.\n" + info: null + example: + - 15 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--allow_dovetail" + description: "[selective-alignment mode only] \nAllow dovetailing mappings.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--recover_orphans" + description: "[selective-alignment mode only] \nAttempt to recover the mates of\ + \ orphaned reads. This uses edlib for orphan recovery, and so introduces some\ + \ computational overhead, but it can improve sensitivity.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--mimicBT2" + description: "[selective-alignment mode only] \nSet flags to mimic parameters\ + \ similar to Bowtie2 with --no-discordant and --no-mixed flags. This increases\ + \ disallows dovetailing reads, and discards orphans. Note, this does not impose\ + \ the very strict parameters assumed by RSEM+Bowtie2, like gapless alignments.\ + \ For that behavior, use the --mimic_strictBT2 flag below.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--mimic_strictBT2" + description: "[selective-alignment mode only] \nSet flags to mimic the very strict\ + \ parameters used by RSEM+Bowtie2. This increases --min_score_fraction to 0.8,\ + \ disallows dovetailing reads, discards orphans, and disallows gaps in alignments.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--softclip" + description: "[selective-alignment mode only] \nAllos soft-clipping of reads during\ + \ selective-alignment. If this option is provided, then regions at the beginning\ + \ or end of the read can be withheld from alignment without any effect on the\ + \ resulting score (i.e. neither adding nor removing from the score). This will\ + \ drastically reduce the penalty if there are mismatches at the beginning or\ + \ end of the read due to e.g. low-quality bases or adapters. NOTE: Even with\ + \ soft-clipping enabled, the read must still achieve a score of at least min_score_fraction\ + \ * maximum achievable score, where the maximum achievable score is computed\ + \ based on the full (un-clipped) read length.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--softclip_overhangs" + description: "[selective-alignment mode only] \nAllow soft-clipping of reads that\ + \ overhang the beginning or ends of the transcript. In this case, the overhaning\ + \ section of the read will simply be unaligned, and will not contribute or detract\ + \ from the alignment score. The default policy is to force an end-to-end alignment\ + \ of the entire read, so that overhanings will result in some deletion of nucleotides\ + \ from the read.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--full_length_alignment" + description: "[selective-alignment mode only] \nPerform selective alignment over\ + \ the full length of the read, beginning from the (approximate) initial mapping\ + \ location and using extension alignment. This is in contrast with the default\ + \ behavior which is to only perform alignment between the MEMs in the optimal\ + \ chain (and before the first and after the last MEM if applicable). The default\ + \ strategy forces the MEMs to belong to the alignment, but has the benefit that\ + \ it can discover indels prior to the first hit shared between the read and\ + \ reference. Except in very rare circumstances, the default mode should be more\ + \ accurate.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--hard_filter" + description: "[selective-alignment mode only] \nInstead of weighting mappings\ + \ by their alignment score, this flag will discard any mappings with sub-optimal\ + \ alignment score. The default option of soft-filtering (i.e. weighting mappings\ + \ by their alignment score) usually yields slightly more accurate abundance\ + \ estimates but this flag may be desirable if you want more accurate 'naive'\ + \ equivalence classes, rather than range factorized equivalence classes.\n" + info: null + direction: "input" + - type: "double" + name: "--min_aln_prob" + description: "The minimum number of fragments that must be assigned to the transcriptome\ + \ for quantification to proceed.\n" + info: null + example: + - 1.0E-5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--write_mappings" + alternatives: + - "-z" + description: "If this option is provided, then the selective-alignment results\ + \ will be written out in SAM-compatible format. By default, output will be directed\ + \ to stdout, but an alternative file name can be provided instead.\n" + info: null + direction: "input" + - type: "file" + name: "--mapping_sam" + description: "Path to file that should output the selective-alignment results\ + \ in SAM-compatible format. THis option must be provided while using --write_mappings" + info: null + example: + - "mappings.sam" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--write_qualities" + description: "This flag only has meaning if mappings are being written (with --write_mappings/-z).\ + \ If this flag is provided, then the output SAM file will contain quality strings\ + \ as well as read sequences. Note that this can greatly increase the size of\ + \ the output file.\n" + info: null + direction: "input" + - type: "string" + name: "--hit_filter_policy" + description: "[selective-alignment mode only]\nDetermines the policy by which\ + \ hits are filtered in selective alignment. Filtering hits after chaining (the\ + \ default) is more sensitive, but more computationally intensive, because it\ + \ performs the chaining dynamic program for all hits. Filtering before chaining\ + \ is faster, but some true hits may be missed. The options are BEFORE, AFTER,\ + \ BOTH and NONE.\n" + info: null + example: + - "AFTER" + required: false + choices: + - "BEFORE" + - "AFTER" + - "BOTH" + - "NONE" + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Advance options" + arguments: + - type: "boolean_true" + name: "--alternative_init_mode" + description: "Use an alternative strategy (rather than simple interpolation between)\ + \ the online and uniform abundance estimates to initialize the EM / VBEM algorithm.\n" + info: null + direction: "input" + - type: "file" + name: "--aux_dir" + description: "The sub-directory of the quantification directory where auxiliary\ + \ information e.g. bootstraps, bias parameters, etc. will be written.\n" + info: null + example: + - "aux_info" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--skip_quant" + description: "Skip performing the actual transcript quantification (including\ + \ any Gibbs sampling or bootstrapping).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--dump_eq" + description: "Dump the simple equivalence class counts that were computed during\ + \ mapping or alignment.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--dump_eq_weights" + alternatives: + - "-d" + description: "Dump conditional probabilities associated with transcripts when\ + \ equivalence class information is being dumped to file. Note, this will dump\ + \ the factorization that is actually used by salmon's offline phase for inference.\ + \ If you are using range-factorized equivalence classes (the default) then the\ + \ same transcript set may appear multiple times with different associated conditional\ + \ probabilities.\n" + info: null + direction: "input" + - type: "integer" + name: "--min_assigned_frags" + description: "The minimum number of fragments that must be assigned to the transcriptome\ + \ for quantification to proceed.\n" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--reduce_GC_memory" + description: "If this option is selected, a more memory efficient (but slightly\ + \ slower) representation is used to compute fragment GC content. Enabling this\ + \ will reduce memory usage, but can also reduce speed. However, the results\ + \ themselves will remain the same.\n" + info: null + direction: "input" + - type: "integer" + name: "--bias_speed_samp" + description: "The value at which the fragment length PMF is down-sampled when\ + \ evaluating sequence-specific & GC fragment bias. Larger values speed up effective\ + \ length correction, but may decrease the fidelity of bias modeling results.\n" + info: null + example: + - 5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--fld_max" + description: "The maximum fragment length to consider when building the empirical\ + \ distribution\n" + info: null + example: + - 1000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--fld_mean" + description: "The mean used in the fragment length distribution prior\n" + info: null + example: + - 250 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--fld_SD" + description: "The standard deviation used in the fragment length distribution\ + \ prior\n" + info: null + example: + - 25 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--forgetting_factor" + alternatives: + - "-f" + description: "The forgetting factor used in the online learning schedule. A smallervalue\ + \ results in quicker learning, but higher variance and may be unstable. A larger\ + \ value results in slower learning but may be more stable. Value should be\ + \ in the interval (0.5, 1.0].\n" + info: null + example: + - 0.65 + required: false + min: 0.500000001 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--init_uniform" + description: "Initialize the offline inference with uniform parameters, rather\ + \ than seeding with online parameters.\n" + info: null + direction: "input" + - type: "integer" + name: "--max_occs_per_hit" + description: "When collecting \"hits\" (MEMs), hits having more than max_occs_per_hit\ + \ occurrences won't be considered.\n" + info: null + example: + - 1000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_read_occ" + description: "Reads \"mapping\" to more than this many places won't be considered.\n" + info: null + example: + - 200 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_length_correction" + description: "Entirely disables length correction when estimating the abundance\ + \ of transcripts. This option can be used with protocols where one expects that\ + \ fragments derive from their underlying targets without regard to that target's\ + \ length (e.g. QuantSeq)\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_effective_length_correction" + description: "Disables effective length correction when computing the probability\ + \ that a fragment was generated from a transcript. If this flag is passed in,the\ + \ fragment length distribution is not taken into account when computing this\ + \ probability.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_single_frag_prob" + description: "Disables the estimation of an associated fragment length probability\ + \ for single-end reads or for orphaned mappings in paired-end libraries. The\ + \ default behavior is to consider the probability of all possible fragment\ + \ lengths associated with the retained mapping. Enabling this flag (i.e. turning\ + \ this default behavior off) will simply not attempt to estimate a fragment\ + \ length probability in such cases.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_frag_length_dist" + description: "Don't consider concordance with the learned fragment length distribution\ + \ when trying to determine the probability that a fragment has originated from\ + \ a specified location. Normally, Fragments with unlikely lengths will be assigned\ + \ a smaller relative probability than those with more likely lengths. When this\ + \ flag is passed in, the observed fragment length has no effect on that fragment's\ + \ a priori probability.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_bias_length_threshold" + description: "If this option is enabled, then no (lower) threshold will be set\ + \ on how short bias correction can make effective lengths. This can increase\ + \ the precision of bias correction, but harm robustness. The default correction\ + \ applies a threshold.\n" + info: null + direction: "input" + - type: "integer" + name: "--num_bias_samples" + description: "Number of fragment mappings to use when learning the sequence-specific\ + \ bias model.\n" + info: null + example: + - 2000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--num_aux_model_samples" + description: "The first are used to train the auxiliary\ + \ model parameters (e.g. fragment length distribution, bias, etc.). After ther\ + \ first observations the auxiliary model parameters\ + \ will be assumed to have converged and will be fixed.\n" + info: null + example: + - 5000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--num_pre_aux_model_samples" + description: "The first will have their assignment likelihoods\ + \ and contributions to the transcript abundances computed without applying any\ + \ auxiliary models. The purpose of ignoring the auxiliary models for the first\ + \ observations is to avoid applying these models\ + \ before their parameters have been learned sufficiently well.\n" + info: null + example: + - 5000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--useEM" + description: "Use the traditional EM algorithm for optimization in the batch passes.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--useVBOpt" + description: "Use the Variational Bayesian EM [default]\n" + info: null + direction: "input" + - type: "integer" + name: "--range_factorization_bins" + description: "Factorizes the likelihood used in quantification by adopting a new\ + \ notion of equivalence classes based on the conditional probabilities with\ + \ which fragments are generated from different transcripts. This is a more fine-grained\ + \ factorization than the normal rich equivalence classes. The default value\ + \ (4) corresponds to the default used in Zakeri et al. 2017 (doi: 10.1093/bioinformatics/btx262),\ + \ and larger values imply a more fine-grained factorization. If range factorization\ + \ is enabled, a common value to select for this parameter is 4. A value of 0\ + \ signifies the use of basic rich equivalence classes.\n" + info: null + example: + - 4 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--num_Gibbs_samples" + description: "Number of Gibbs sampling rounds to perform.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_Gamma_draw" + description: "This switch will disable drawing transcript fractions from a Gamma\ + \ distribution during Gibbs sampling. In this case the sampler does not account\ + \ for shot-noise, but only assignment ambiguity\n" + info: null + direction: "input" + - type: "integer" + name: "--num_bootstraps" + description: "Number of bootstrap samples to generate. Note: This is mutually\ + \ exclusive with Gibbs sampling.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--bootstrap_reproject" + description: "This switch will learn the parameter distribution from the bootstrapped\ + \ counts for each sample, but will reproject those parameters onto the original\ + \ equivalence class counts.\n" + info: null + direction: "input" + - type: "integer" + name: "--thinning_factor" + description: "Number of steps to discard for every sample kept from the Gibbs\ + \ chain. The larger this number, the less chance that subsequent samples are\ + \ auto-correlated, but the slower sampling becomes.\n" + info: null + example: + - 16 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--quiet" + alternatives: + - "-q" + description: "Be quiet while doing quantification (don't write informative output\ + \ to the console unless something goes wrong).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--per_transcript_prior" + description: "The prior (either the default or the argument provided via --vb_prior)\ + \ will be interpreted as a transcript-level prior (i.e. each transcript will\ + \ be given a prior read count of this value)\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--per_nucleotide_prior" + description: "The prior (either the default or the argument provided via --vb_prior)\ + \ will be interpreted as a nucleotide-level prior (i.e. each nucleotide will\ + \ be given a prior read count of this value)\n" + info: null + direction: "input" + - type: "integer" + name: "--sig_digits" + description: "The number of significant digits to write when outputting the EffectiveLength\ + \ and NumReads columns\n" + info: null + example: + - 3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--vb_prior" + description: "The prior that will be used in the VBEM algorithm. This is interpreted\ + \ as a per-transcript prior, unless the --per_nucleotide_prior flag is also\ + \ given. If the --per_nucleotide_prior flag is given, this is used as a nucleotide-level\ + \ prior. If the default is used, it will be divided by 1000 before being used\ + \ as a nucleotide-level prior, i.e. the default per-nucleotide prior will be\ + \ 1e-5.\n" + info: null + example: + - 0.01 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--write_orphan_links" + description: "Write the transcripts that are linked by orphaned reads.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--write_unmapped_names" + description: "Write the names of un-mapped reads to the file unmapped_names.txt\ + \ in the auxiliary directory.\n" + info: null + direction: "input" +- name: "Alignment-specific options" + arguments: + - type: "boolean_true" + name: "--no_error_model" + description: "Turn off the alignment error model, which takes into account the\ + \ the observed frequency of different types of mismatches / indels when computing\ + \ the likelihood of a given alignment. Turning this off can speed up alignment-based\ + \ salmon, but can harm quantification accuracy.\n" + info: null + direction: "input" + - type: "integer" + name: "--num_error_bins" + description: "The number of bins into which to divide each read when learning\ + \ and applying the error model. For example, a value of 10 would mean that\ + \ effectively, a separate error model is leared and applied to each 10th of\ + \ the read, while a value of 3 would mean that a separate error model is applied\ + \ to the read beginning (first third), middle (second third) and end (final\ + \ third).\n" + info: null + example: + - 6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--sample_out" + alternatives: + - "-s" + description: "Write a \"postSample.bam\" file in the output directory that will\ + \ sample the input alignments according to the estimated transcript abundances.\ + \ If you're going to perform downstream analysis of the alignments with tools\ + \ which don't, themselves, take fragment assignment ambiguity into account,\ + \ you should use this output.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--sample_unaligned" + alternatives: + - "-u" + description: "In addition to sampling the aligned reads, also write the un-aligned\ + \ reads to \"postSample.bam\".\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--gencode" + description: "This flag will expect the input transcript fasta to be in GENCODE\ + \ format, and will split the transcript name at the first '|' character. These\ + \ reduced names will be used in the output and when looking for these transcripts\ + \ in a gene to transcript GTF.\n" + info: null + direction: "input" + - type: "integer" + name: "--mapping_cache_memory_limit" + description: "If the file contained fewer than this many mapped reads, then just\ + \ keep the data in memory for subsequent rounds of inference. Obviously, this\ + \ value should not be too large if you wish to keep a low memory usage, but\ + \ setting it large enough to accommodate all of the mapped read can substantially\ + \ speed up inference on \"small\" files that contain only a few million reads.\n" + info: null + example: + - 2000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Salmon is a tool for wicked-fast transcript quantification from RNA-seq\ + \ data. It can either make use of pre-computed alignments (in the form of a SAM/BAM\ + \ file) to the transcripts rather than the raw reads, or can be run in the mapping-based\ + \ mode. \n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "Transcriptome" +- "Quantification" +license: "GPL-3.0" +references: + doi: + - "10.1038/nmeth.4197" +links: + repository: "https://github.com/COMBINE-lab/salmon" + homepage: "https://salmon.readthedocs.io/en/latest/salmon.html" + documentation: "https://salmon.readthedocs.io/en/latest/salmon.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/salmon:1.10.2--hecfa306_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "salmon index -v 2>&1 | sed 's/salmon \\([0-9.]*\\)/salmon: \\1/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/salmon/salmon_quant/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/salmon/salmon_quant" + executable: "target/executable/salmon/salmon_quant/salmon_quant" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/salmon/salmon_quant/salmon_quant b/target/executable/salmon/salmon_quant/salmon_quant new file mode 100755 index 00000000..b13a56cc --- /dev/null +++ b/target/executable/salmon/salmon_quant/salmon_quant @@ -0,0 +1,3887 @@ +#!/usr/bin/env bash + +# salmon_quant main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="salmon_quant" +VIASH_META_FUNCTIONALITY_NAME="salmon_quant" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "salmon_quant main" + echo "" + echo "Salmon is a tool for wicked-fast transcript quantification from RNA-seq data. It" + echo "can either make use of pre-computed alignments (in the form of a SAM/BAM file)" + echo "to the transcripts rather than the raw reads, or can be run in the mapping-based" + echo "mode." + echo "" + echo "Common input options:" + echo " -l, --lib_type" + echo " type: string" + echo " default: A" + echo " choices: [ A, U, SF, SR, IU, IS, ISF, ISR, OU, OS, OSF, OSR, MU, MS," + echo "MSF, MSR ]" + echo " Format string describing the library." + echo " The library type string consists of three parts:" + echo " 1. Relative orientation of the reads: This part is only provided if the" + echo " library is paired-end, THe possible options are" + echo " I = inward" + echo " O = outward" + echo " M = matching" + echo " 2. Strandedness of the library: This part specifies whether the protocol" + echo " is stranded or unstranded. The options are:" + echo " S = stranded" + echo " U = unstranded" + echo " 3. Directionality of the reads: If the library is stranded, the final" + echo " part of the library string is used to specify the strand from which the" + echo " read originates. The possible values are" + echo " F = read 1 (or single-end read) comes from the forward strand" + echo " R = read 1 (or single-end read) comes from the reverse strand" + echo "" + echo "Mapping input options:" + echo " -i, --index" + echo " type: file, file must exist" + echo " example: transcriptome_index" + echo " Salmon index." + echo "" + echo " -r, --unmated_reads" + echo " type: file, multiple values allowed, file must exist" + echo " example: sample.fq.gz" + echo " List of files containing unmated reads of (e.g. single-end reads)." + echo "" + echo " -m1, --mates1" + echo " type: file, multiple values allowed, file must exist" + echo " example: sample_1.fq.gz" + echo " File containing the #1 mates." + echo "" + echo " -m2, --mates2" + echo " type: file, multiple values allowed, file must exist" + echo " example: sample_2.fq.gz" + echo " File containing the #2 mates." + echo "" + echo "Alignment input options:" + echo " --discard_orphans" + echo " type: boolean_true" + echo " Discard orphan alignments in the input [for alignment-based mode only]." + echo " If this flag is passed, then only paired alignments will be considered" + echo " toward quantification estimates. The default behavior is to consider" + echo " orphan alignments if no valid paired mappings exist." + echo "" + echo " -a, --alignments" + echo " type: file, multiple values allowed, file must exist" + echo " example: sample.fq.gz" + echo " Input alignment (BAM) file(s)." + echo "" + echo " -e, --eqclasses" + echo " type: file, file must exist" + echo " input salmon weighted equivalence class file." + echo "" + echo " -t, --targets" + echo " type: file, file must exist" + echo " example: transcripts.fasta" + echo " FASTA format file containing target transcripts." + echo "" + echo " --ont" + echo " type: boolean_true" + echo " Use alignment model for Oxford Nanopore long reads" + echo "" + echo "Output:" + echo " -o, --output" + echo " type: file, required parameter, output, file must exist" + echo " example: quant_output" + echo " Output quantification directory." + echo "" + echo " --quant_results" + echo " type: file, required parameter, output, file must exist" + echo " example: quant.sf" + echo " Salmon quantification file." + echo "" + echo "Basic options:" + echo " --seq_bias" + echo " type: boolean_true" + echo " Perform sequence-specific bias correction." + echo "" + echo " --gc_bias" + echo " type: boolean_true" + echo " Perform fragment GC bias correction [beta for single-end reads]." + echo "" + echo " --pos_bias" + echo " type: boolean_true" + echo " Perform positional bias correction." + echo "" + echo " --incompat_prior" + echo " type: double" + echo " example: 0.0" + echo " min: 0.0" + echo " max: 1.0" + echo " Set the prior probability that an alignment that disagrees with the" + echo " specified library type (--lib_type) results from the true fragment" + echo " origin. Setting this to 0 specifies that alignments that disagree with" + echo " the library type should be \"impossible\", while setting it to 1 says that" + echo " alignments that disagree with the library type are no less likely than" + echo " those that do." + echo "" + echo " -g, --gene_map" + echo " type: file, file must exist" + echo " example: gene_map.gtf" + echo " File containing a mapping of transcripts to genes. If this file is" + echo " provided salmon will output both quant.sf and quant.genes.sf files," + echo " where the latter contains aggregated gene-level abundance estimates." + echo " The transcript to gene mapping should be provided as either a GTF file," + echo " or a in a simple tab-delimited format where each line contains the name" + echo " of a transcript and the gene to which it belongs separated by a tab. The" + echo " extension of the file is used to determine how the file should be" + echo " parsed. Files ending in '.gtf', '.gff' or '.gff3' are assumed to be in" + echo " GTF format; files with any other extension are assumed to be in the" + echo " simple format. In GTF / GFF format, the \"transcript_id\" is assumed to" + echo " contain the transcript identifier and the \"gene_id\" is assumed to" + echo " contain the corresponding gene identifier." + echo "" + echo " --aux_target_file" + echo " type: file, file must exist" + echo " example: auxilary_targets.txt" + echo " A file containing a list of \"auxiliary\" targets. These are valid targets" + echo " (i.e., not decoys) to which fragments are allowed to map and be" + echo " assigned, and which will be quantified, but for which auxiliary models" + echo " like sequence-specific and fragment-GC bias correction should not be" + echo " applied." + echo "" + echo " --meta" + echo " type: boolean_true" + echo " If you're using Salmon on a metagenomic dataset, consider setting this" + echo " flag to disable parts of the abundance estimation model that make less" + echo " sense for metagenomic data." + echo "" + echo " --score_exp" + echo " type: double" + echo " example: 1.0" + echo " The factor by which sub-optimal alignment scores are downweighted to" + echo " produce a probability. If the best alignment score for the current read" + echo " is S, and the score for a particular alignment is w, then the" + echo " probability will be computed porportional to exp( - scoreExp * (S-w) )." + echo "" + echo "Options specific to mapping mode:" + echo " --discard_orphans_quasi" + echo " type: boolean_true" + echo " [selective-alignment mode only]" + echo " Discard orphan mappings in selective-alignment mode. If this flag is" + echo " passed then only paired mappings will be considered toward" + echo " quantification estimates. The default behavior is to consider orphan" + echo " mappings if no valid paired mappings exist. This flag is independent of" + echo " the option to write the orphaned mappings to file (--writeOrphanLinks)." + echo "" + echo " --consensus_slack" + echo " type: double" + echo " example: 0.35" + echo " min: 0.0" + echo " max: 0.999999999" + echo " [selective-alignment mode only]" + echo " The amount of slack allowed in the selective-alignment filtering" + echo " mechanism. If this is set to a fraction, X, greater than 0 (and in" + echo " [0,1)), then uniMEM chains with scores below (100 * X)% of the best" + echo " chain score for a read, and read pairs with a sum of chain scores below" + echo " (100 * X)% of the best chain score for a read pair will be discounted as" + echo " a mapping candidates. The default value of this option is 0.35." + echo "" + echo " --pre_merge_chain_sub_thresh" + echo " type: double" + echo " example: 0.75" + echo " min: 0.0" + echo " max: 1.0" + echo " [selective-alignment mode only]" + echo " The threshold of sub-optimal chains, compared to the best chain on a" + echo " given target, that will be retained and passed to the next phase of" + echo " mapping. Specifically, if the best chain for a read (or read-end in" + echo " paired-end mode) to target t has score X_t, then all chains for this" + echo " read with score >= X_t * preMergeChainSubThresh will be retained and" + echo " passed to subsequent mapping phases. This value must be in the range" + echo " [0, 1]." + echo "" + echo " --post_merge_chain_sub_thresh" + echo " type: double" + echo " example: 0.9" + echo " min: 0.0" + echo " max: 1.0" + echo " [selective-alignment mode only]" + echo " The threshold of sub-optimal chains, compared to the best chain on a" + echo " given target, that will be retained and passed to the next phase of" + echo " mapping. This is different than post_merge_chain_sub_thresh, because" + echo " this is applied to pairs of chains (from the ends of paired-end reads)" + echo " after merging (i.e. after checking concordancy constraints etc.)." + echo " Specifically, if the best chain pair to target t has score X_t, then all" + echo " chain pairs for this read pair with score >= X_t *" + echo " post_merge_chain_sub_thresh will be retained and passed to subsequent" + echo " mapping phases. This value must be in the range [0, 1]. Note: This" + echo " option is only meaningful for paired-end libraries, and is ignored for" + echo " single-end libraries." + echo "" + echo " --orphan_chain_sub_thresh" + echo " type: double" + echo " example: 0.95" + echo " min: 0.0" + echo " max: 1.0" + echo " [selective-alignment mode only]" + echo " This threshold sets a global sub-optimality threshold for chains" + echo " corresponding to orphan mappings. That is, if the merging procedure" + echo " results in no concordant mappings then only orphan mappings with a chain" + echo " score >= orphan_chain_sub_thresh * bestChainScore will be retained and" + echo " passed to subsequent mapping phases. This value must be in the range [0," + echo " 1]. Note: This option is only meaningful for paired-end libraries, and" + echo " is ignored for single-end libraries." + echo "" + echo " --min_score_fraction" + echo " type: double" + echo " example: 0.65" + echo " min: 1.0E-9" + echo " max: 1.0" + echo " [selective-alignment mode only]" + echo " The fraction of the optimal possible alignment score that a mapping must" + echo " achieve in order to be considered \"valid\" --- should be in (0,1]." + echo " Default 0.65" + echo "" + echo " --mismatch_seed_skip" + echo " type: integer" + echo " example: 3" + echo " [selective-alignment mode only]" + echo " After a k-mer hit is extended to a uni-MEM, the uni-MEM extension can" + echo " terminate for one of 3 reasons; the end of the read, the end of the" + echo " unitig, or a mismatch. If the extension ends because of a mismatch, this" + echo " is likely the result of a sequencing error. To avoid looking up many" + echo " k-mers that will likely fail to be located in the index, the search" + echo " procedure skips by a factor of mismatch_seed_skip until it either (1)" + echo " finds another match or (2) is k-bases past the mismatch position. This" + echo " value controls that skip length. A smaller value can increase" + echo " sensitivity, while a larger value can speed up seeding." + echo "" + echo " --disable_chaining_heuristic" + echo " type: boolean_true" + echo " [selective-alignment mode only]" + echo " By default, the heuristic of (Li 2018) is implemented, which terminates" + echo " the chaining DP once a given number of valid backpointers are found." + echo " This speeds up the seed (MEM) chaining step, but may result in" + echo " sub-optimal chains in complex situations (e.g. sequences with many" + echo " repeats and overlapping repeats). Passing this flag will disable the" + echo " chaining heuristic, and perform the full chaining dynamic program," + echo " guaranteeing the optimal chain is found in this step." + echo "" + echo " --decoy_threshold" + echo " type: double" + echo " example: 1.0" + echo " min: 0.0" + echo " max: 1.0" + echo " [selective-alignment mode only]" + echo " For an alignemnt to an annotated transcript to be considered invalid, it" + echo " must have an alignment score < (decoy_threshold * bestDecoyScore). A" + echo " value of 1.0 means that any alignment strictly worse than the best decoy" + echo " alignment will be discarded. A smaller value will allow reads to be" + echo " allocated to transcripts even if they strictly align better to the decoy" + echo " sequence." + echo "" + echo " --ma" + echo " type: integer" + echo " example: 2" + echo " [selective-alignment mode only]" + echo " The value given to a match between read and reference nucleotides in an" + echo " alignment." + echo "" + echo " --mp" + echo " type: integer" + echo " example: -4" + echo " [selective-alignment mode only]" + echo " The value given to a mis-match between read and reference nucleotides in" + echo " an alignment." + echo "" + echo " --go" + echo " type: integer" + echo " example: 6" + echo " [selective-alignment mode only]" + echo " The value given to a gap opening in an alignment." + echo "" + echo " --ge" + echo " type: integer" + echo " example: 2" + echo " [selective-alignment mode only]" + echo " The value given to a gap extension in an alignment." + echo "" + echo " --bandwidth" + echo " type: integer" + echo " example: 15" + echo " [selective-alignment mode only]" + echo " The value used for the bandwidth passed to ksw2. A smaller bandwidth can" + echo " make the alignment verification run more quickly, but could possibly" + echo " miss valid alignments." + echo "" + echo " --allow_dovetail" + echo " type: boolean_true" + echo " [selective-alignment mode only]" + echo " Allow dovetailing mappings." + echo "" + echo " --recover_orphans" + echo " type: boolean_true" + echo " [selective-alignment mode only]" + echo " Attempt to recover the mates of orphaned reads. This uses edlib for" + echo " orphan recovery, and so introduces some computational overhead, but it" + echo " can improve sensitivity." + echo "" + echo " --mimicBT2" + echo " type: boolean_true" + echo " [selective-alignment mode only]" + echo " Set flags to mimic parameters similar to Bowtie2 with --no-discordant" + echo " and --no-mixed flags. This increases disallows dovetailing reads, and" + echo " discards orphans. Note, this does not impose the very strict parameters" + echo " assumed by RSEM+Bowtie2, like gapless alignments. For that behavior, use" + echo " the --mimic_strictBT2 flag below." + echo "" + echo " --mimic_strictBT2" + echo " type: boolean_true" + echo " [selective-alignment mode only]" + echo " Set flags to mimic the very strict parameters used by RSEM+Bowtie2. This" + echo " increases --min_score_fraction to 0.8, disallows dovetailing reads," + echo " discards orphans, and disallows gaps in alignments." + echo "" + echo " --softclip" + echo " type: boolean_true" + echo " [selective-alignment mode only]" + echo " Allos soft-clipping of reads during selective-alignment. If this option" + echo " is provided, then regions at the beginning or end of the read can be" + echo " withheld from alignment without any effect on the resulting score (i.e." + echo " neither adding nor removing from the score). This will drastically" + echo " reduce the penalty if there are mismatches at the beginning or end of" + echo " the read due to e.g. low-quality bases or adapters. NOTE: Even with" + echo " soft-clipping enabled, the read must still achieve a score of at least" + echo " min_score_fraction * maximum achievable score, where the maximum" + echo " achievable score is computed based on the full (un-clipped) read length." + echo "" + echo " --softclip_overhangs" + echo " type: boolean_true" + echo " [selective-alignment mode only]" + echo " Allow soft-clipping of reads that overhang the beginning or ends of the" + echo " transcript. In this case, the overhaning section of the read will simply" + echo " be unaligned, and will not contribute or detract from the alignment" + echo " score. The default policy is to force an end-to-end alignment of the" + echo " entire read, so that overhanings will result in some deletion of" + echo " nucleotides from the read." + echo "" + echo " --full_length_alignment" + echo " type: boolean_true" + echo " [selective-alignment mode only]" + echo " Perform selective alignment over the full length of the read, beginning" + echo " from the (approximate) initial mapping location and using extension" + echo " alignment. This is in contrast with the default behavior which is to" + echo " only perform alignment between the MEMs in the optimal chain (and before" + echo " the first and after the last MEM if applicable). The default strategy" + echo " forces the MEMs to belong to the alignment, but has the benefit that it" + echo " can discover indels prior to the first hit shared between the read and" + echo " reference. Except in very rare circumstances, the default mode should be" + echo " more accurate." + echo "" + echo " --hard_filter" + echo " type: boolean_true" + echo " [selective-alignment mode only]" + echo " Instead of weighting mappings by their alignment score, this flag will" + echo " discard any mappings with sub-optimal alignment score. The default" + echo " option of soft-filtering (i.e. weighting mappings by their alignment" + echo " score) usually yields slightly more accurate abundance estimates but" + echo " this flag may be desirable if you want more accurate 'naive' equivalence" + echo " classes, rather than range factorized equivalence classes." + echo "" + echo " --min_aln_prob" + echo " type: double" + echo " example: 1.0E-5" + echo " The minimum number of fragments that must be assigned to the" + echo " transcriptome for quantification to proceed." + echo "" + echo " -z, --write_mappings" + echo " type: boolean_true" + echo " If this option is provided, then the selective-alignment results will be" + echo " written out in SAM-compatible format. By default, output will be" + echo " directed to stdout, but an alternative file name can be provided" + echo " instead." + echo "" + echo " --mapping_sam" + echo " type: file, output, file must exist" + echo " example: mappings.sam" + echo " Path to file that should output the selective-alignment results in" + echo " SAM-compatible format. THis option must be provided while using" + echo " --write_mappings" + echo "" + echo " --write_qualities" + echo " type: boolean_true" + echo " This flag only has meaning if mappings are being written (with" + echo " --write_mappings/-z). If this flag is provided, then the output SAM file" + echo " will contain quality strings as well as read sequences. Note that this" + echo " can greatly increase the size of the output file." + echo "" + echo " --hit_filter_policy" + echo " type: string" + echo " example: AFTER" + echo " choices: [ BEFORE, AFTER, BOTH, NONE ]" + echo " [selective-alignment mode only]" + echo " Determines the policy by which hits are filtered in selective alignment." + echo " Filtering hits after chaining (the default) is more sensitive, but more" + echo " computationally intensive, because it performs the chaining dynamic" + echo " program for all hits. Filtering before chaining is faster, but some true" + echo " hits may be missed. The options are BEFORE, AFTER, BOTH and NONE." + echo "" + echo "Advance options:" + echo " --alternative_init_mode" + echo " type: boolean_true" + echo " Use an alternative strategy (rather than simple interpolation between)" + echo " the online and uniform abundance estimates to initialize the EM / VBEM" + echo " algorithm." + echo "" + echo " --aux_dir" + echo " type: file, output, file must exist" + echo " example: aux_info" + echo " The sub-directory of the quantification directory where auxiliary" + echo " information e.g. bootstraps, bias parameters, etc. will be written." + echo "" + echo " --skip_quant" + echo " type: boolean_true" + echo " Skip performing the actual transcript quantification (including any" + echo " Gibbs sampling or bootstrapping)." + echo "" + echo " --dump_eq" + echo " type: boolean_true" + echo " Dump the simple equivalence class counts that were computed during" + echo " mapping or alignment." + echo "" + echo " -d, --dump_eq_weights" + echo " type: boolean_true" + echo " Dump conditional probabilities associated with transcripts when" + echo " equivalence class information is being dumped to file. Note, this will" + echo " dump the factorization that is actually used by salmon's offline phase" + echo " for inference. If you are using range-factorized equivalence classes" + echo " (the default) then the same transcript set may appear multiple times" + echo " with different associated conditional probabilities." + echo "" + echo " --min_assigned_frags" + echo " type: integer" + echo " example: 10" + echo " The minimum number of fragments that must be assigned to the" + echo " transcriptome for quantification to proceed." + echo "" + echo " --reduce_GC_memory" + echo " type: boolean_true" + echo " If this option is selected, a more memory efficient (but slightly" + echo " slower) representation is used to compute fragment GC content. Enabling" + echo " this will reduce memory usage, but can also reduce speed. However, the" + echo " results themselves will remain the same." + echo "" + echo " --bias_speed_samp" + echo " type: integer" + echo " example: 5" + echo " The value at which the fragment length PMF is down-sampled when" + echo " evaluating sequence-specific & GC fragment bias. Larger values speed up" + echo " effective length correction, but may decrease the fidelity of bias" + echo " modeling results." + echo "" + echo " --fld_max" + echo " type: integer" + echo " example: 1000" + echo " The maximum fragment length to consider when building the empirical" + echo " distribution" + echo "" + echo " --fld_mean" + echo " type: integer" + echo " example: 250" + echo " The mean used in the fragment length distribution prior" + echo "" + echo " --fld_SD" + echo " type: integer" + echo " example: 25" + echo " The standard deviation used in the fragment length distribution prior" + echo "" + echo " -f, --forgetting_factor" + echo " type: double" + echo " example: 0.65" + echo " min: 0.500000001" + echo " max: 1.0" + echo " The forgetting factor used in the online learning schedule. A" + echo " smallervalue results in quicker learning, but higher variance and may be" + echo " unstable. A larger value results in slower learning but may be more" + echo " stable. Value should be in the interval (0.5, 1.0]." + echo "" + echo " --init_uniform" + echo " type: boolean_true" + echo " Initialize the offline inference with uniform parameters, rather than" + echo " seeding with online parameters." + echo "" + echo " --max_occs_per_hit" + echo " type: integer" + echo " example: 1000" + echo " When collecting \"hits\" (MEMs), hits having more than max_occs_per_hit" + echo " occurrences won't be considered." + echo "" + echo " --max_read_occ" + echo " type: integer" + echo " example: 200" + echo " Reads \"mapping\" to more than this many places won't be considered." + echo "" + echo " --no_length_correction" + echo " type: boolean_true" + echo " Entirely disables length correction when estimating the abundance of" + echo " transcripts. This option can be used with protocols where one expects" + echo " that fragments derive from their underlying targets without regard to" + echo " that target's length (e.g. QuantSeq)" + echo "" + echo " --no_effective_length_correction" + echo " type: boolean_true" + echo " Disables effective length correction when computing the probability that" + echo " a fragment was generated from a transcript. If this flag is passed" + echo " in,the fragment length distribution is not taken into account when" + echo " computing this probability." + echo "" + echo " --no_single_frag_prob" + echo " type: boolean_true" + echo " Disables the estimation of an associated fragment length probability for" + echo " single-end reads or for orphaned mappings in paired-end libraries. The" + echo " default behavior is to consider the probability of all possible" + echo " fragment lengths associated with the retained mapping. Enabling this" + echo " flag (i.e. turning this default behavior off) will simply not attempt to" + echo " estimate a fragment length probability in such cases." + echo "" + echo " --no_frag_length_dist" + echo " type: boolean_true" + echo " Don't consider concordance with the learned fragment length distribution" + echo " when trying to determine the probability that a fragment has originated" + echo " from a specified location. Normally, Fragments with unlikely lengths" + echo " will be assigned a smaller relative probability than those with more" + echo " likely lengths. When this flag is passed in, the observed fragment" + echo " length has no effect on that fragment's a priori probability." + echo "" + echo " --no_bias_length_threshold" + echo " type: boolean_true" + echo " If this option is enabled, then no (lower) threshold will be set on how" + echo " short bias correction can make effective lengths. This can increase the" + echo " precision of bias correction, but harm robustness. The default" + echo " correction applies a threshold." + echo "" + echo " --num_bias_samples" + echo " type: integer" + echo " example: 2000000" + echo " Number of fragment mappings to use when learning the sequence-specific" + echo " bias model." + echo "" + echo " --num_aux_model_samples" + echo " type: integer" + echo " example: 5000000" + echo " The first are used to train the auxiliary model" + echo " parameters (e.g. fragment length distribution, bias, etc.). After ther" + echo " first observations the auxiliary model" + echo " parameters will be assumed to have converged and will be fixed." + echo "" + echo " --num_pre_aux_model_samples" + echo " type: integer" + echo " example: 5000" + echo " The first will have their assignment likelihoods" + echo " and contributions to the transcript abundances computed without applying" + echo " any auxiliary models. The purpose of ignoring the auxiliary models for" + echo " the first observations is to avoid applying" + echo " these models before their parameters have been learned sufficiently" + echo " well." + echo "" + echo " --useEM" + echo " type: boolean_true" + echo " Use the traditional EM algorithm for optimization in the batch passes." + echo "" + echo " --useVBOpt" + echo " type: boolean_true" + echo " Use the Variational Bayesian EM [default]" + echo "" + echo " --range_factorization_bins" + echo " type: integer" + echo " example: 4" + echo " Factorizes the likelihood used in quantification by adopting a new" + echo " notion of equivalence classes based on the conditional probabilities" + echo " with which fragments are generated from different transcripts. This is a" + echo " more fine-grained factorization than the normal rich equivalence" + echo " classes. The default value (4) corresponds to the default used in Zakeri" + echo " et al. 2017 (doi: 10.1093/bioinformatics/btx262), and larger values" + echo " imply a more fine-grained factorization. If range factorization is" + echo " enabled, a common value to select for this parameter is 4. A value of 0" + echo " signifies the use of basic rich equivalence classes." + echo "" + echo " --num_Gibbs_samples" + echo " type: integer" + echo " example: 0" + echo " Number of Gibbs sampling rounds to perform." + echo "" + echo " --no_Gamma_draw" + echo " type: boolean_true" + echo " This switch will disable drawing transcript fractions from a Gamma" + echo " distribution during Gibbs sampling. In this case the sampler does not" + echo " account for shot-noise, but only assignment ambiguity" + echo "" + echo " --num_bootstraps" + echo " type: integer" + echo " example: 0" + echo " Number of bootstrap samples to generate. Note: This is mutually" + echo " exclusive with Gibbs sampling." + echo "" + echo " --bootstrap_reproject" + echo " type: boolean_true" + echo " This switch will learn the parameter distribution from the bootstrapped" + echo " counts for each sample, but will reproject those parameters onto the" + echo " original equivalence class counts." + echo "" + echo " --thinning_factor" + echo " type: integer" + echo " example: 16" + echo " Number of steps to discard for every sample kept from the Gibbs chain." + echo " The larger this number, the less chance that subsequent samples are" + echo " auto-correlated, but the slower sampling becomes." + echo "" + echo " -q, --quiet" + echo " type: boolean_true" + echo " Be quiet while doing quantification (don't write informative output to" + echo " the console unless something goes wrong)." + echo "" + echo " --per_transcript_prior" + echo " type: boolean_true" + echo " The prior (either the default or the argument provided via --vb_prior)" + echo " will be interpreted as a transcript-level prior (i.e. each transcript" + echo " will be given a prior read count of this value)" + echo "" + echo " --per_nucleotide_prior" + echo " type: boolean_true" + echo " The prior (either the default or the argument provided via --vb_prior)" + echo " will be interpreted as a nucleotide-level prior (i.e. each nucleotide" + echo " will be given a prior read count of this value)" + echo "" + echo " --sig_digits" + echo " type: integer" + echo " example: 3" + echo " The number of significant digits to write when outputting the" + echo " EffectiveLength and NumReads columns" + echo "" + echo " --vb_prior" + echo " type: double" + echo " example: 0.01" + echo " The prior that will be used in the VBEM algorithm. This is interpreted" + echo " as a per-transcript prior, unless the --per_nucleotide_prior flag is" + echo " also given. If the --per_nucleotide_prior flag is given, this is used as" + echo " a nucleotide-level prior. If the default is used, it will be divided by" + echo " 1000 before being used as a nucleotide-level prior, i.e. the default" + echo " per-nucleotide prior will be 1e-5." + echo "" + echo " --write_orphan_links" + echo " type: boolean_true" + echo " Write the transcripts that are linked by orphaned reads." + echo "" + echo " --write_unmapped_names" + echo " type: boolean_true" + echo " Write the names of un-mapped reads to the file unmapped_names.txt in the" + echo " auxiliary directory." + echo "" + echo "Alignment-specific options:" + echo " --no_error_model" + echo " type: boolean_true" + echo " Turn off the alignment error model, which takes into account the the" + echo " observed frequency of different types of mismatches / indels when" + echo " computing the likelihood of a given alignment. Turning this off can" + echo " speed up alignment-based salmon, but can harm quantification accuracy." + echo "" + echo " --num_error_bins" + echo " type: integer" + echo " example: 6" + echo " The number of bins into which to divide each read when learning and" + echo " applying the error model. For example, a value of 10 would mean that" + echo " effectively, a separate error model is leared and applied to each 10th" + echo " of the read, while a value of 3 would mean that a separate error model" + echo " is applied to the read beginning (first third), middle (second third)" + echo " and end (final third)." + echo "" + echo " -s, --sample_out" + echo " type: boolean_true" + echo " Write a \"postSample.bam\" file in the output directory that will sample" + echo " the input alignments according to the estimated transcript abundances." + echo " If you're going to perform downstream analysis of the alignments with" + echo " tools which don't, themselves, take fragment assignment ambiguity into" + echo " account, you should use this output." + echo "" + echo " -u, --sample_unaligned" + echo " type: boolean_true" + echo " In addition to sampling the aligned reads, also write the un-aligned" + echo " reads to \"postSample.bam\"." + echo "" + echo " --gencode" + echo " type: boolean_true" + echo " This flag will expect the input transcript fasta to be in GENCODE" + echo " format, and will split the transcript name at the first '|' character." + echo " These reduced names will be used in the output and when looking for" + echo " these transcripts in a gene to transcript GTF." + echo "" + echo " --mapping_cache_memory_limit" + echo " type: integer" + echo " example: 2000000" + echo " If the file contained fewer than this many mapped reads, then just keep" + echo " the data in memory for subsequent rounds of inference. Obviously, this" + echo " value should not be too large if you wish to keep a low memory usage," + echo " but setting it large enough to accommodate all of the mapped read can" + echo " substantially speed up inference on \"small\" files that contain only a" + echo " few million reads." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/salmon:1.10.2--hecfa306_0 +ENTRYPOINT [] +RUN salmon index -v 2>&1 | sed 's/salmon \([0-9.]*\)/salmon: \1/' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component salmon salmon_quant" +LABEL org.opencontainers.image.created="2024-06-24T08:36:42Z" +LABEL org.opencontainers.image.source="https://github.com/COMBINE-lab/salmon" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "salmon_quant main" + exit + ;; + --lib_type) + [ -n "$VIASH_PAR_LIB_TYPE" ] && ViashError Bad arguments for option \'--lib_type\': \'$VIASH_PAR_LIB_TYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIB_TYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --lib_type. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --lib_type=*) + [ -n "$VIASH_PAR_LIB_TYPE" ] && ViashError Bad arguments for option \'--lib_type=*\': \'$VIASH_PAR_LIB_TYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIB_TYPE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -l) + [ -n "$VIASH_PAR_LIB_TYPE" ] && ViashError Bad arguments for option \'-l\': \'$VIASH_PAR_LIB_TYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIB_TYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -l. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --index) + [ -n "$VIASH_PAR_INDEX" ] && ViashError Bad arguments for option \'--index\': \'$VIASH_PAR_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --index. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --index=*) + [ -n "$VIASH_PAR_INDEX" ] && ViashError Bad arguments for option \'--index=*\': \'$VIASH_PAR_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX=$(ViashRemoveFlags "$1") + shift 1 + ;; + -i) + [ -n "$VIASH_PAR_INDEX" ] && ViashError Bad arguments for option \'-i\': \'$VIASH_PAR_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -i. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unmated_reads) + if [ -z "$VIASH_PAR_UNMATED_READS" ]; then + VIASH_PAR_UNMATED_READS="$2" + else + VIASH_PAR_UNMATED_READS="$VIASH_PAR_UNMATED_READS;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --unmated_reads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unmated_reads=*) + if [ -z "$VIASH_PAR_UNMATED_READS" ]; then + VIASH_PAR_UNMATED_READS=$(ViashRemoveFlags "$1") + else + VIASH_PAR_UNMATED_READS="$VIASH_PAR_UNMATED_READS;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -r) + if [ -z "$VIASH_PAR_UNMATED_READS" ]; then + VIASH_PAR_UNMATED_READS="$2" + else + VIASH_PAR_UNMATED_READS="$VIASH_PAR_UNMATED_READS;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -r. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mates1) + if [ -z "$VIASH_PAR_MATES1" ]; then + VIASH_PAR_MATES1="$2" + else + VIASH_PAR_MATES1="$VIASH_PAR_MATES1;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --mates1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mates1=*) + if [ -z "$VIASH_PAR_MATES1" ]; then + VIASH_PAR_MATES1=$(ViashRemoveFlags "$1") + else + VIASH_PAR_MATES1="$VIASH_PAR_MATES1;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -m1) + if [ -z "$VIASH_PAR_MATES1" ]; then + VIASH_PAR_MATES1="$2" + else + VIASH_PAR_MATES1="$VIASH_PAR_MATES1;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -m1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mates2) + if [ -z "$VIASH_PAR_MATES2" ]; then + VIASH_PAR_MATES2="$2" + else + VIASH_PAR_MATES2="$VIASH_PAR_MATES2;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --mates2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mates2=*) + if [ -z "$VIASH_PAR_MATES2" ]; then + VIASH_PAR_MATES2=$(ViashRemoveFlags "$1") + else + VIASH_PAR_MATES2="$VIASH_PAR_MATES2;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -m2) + if [ -z "$VIASH_PAR_MATES2" ]; then + VIASH_PAR_MATES2="$2" + else + VIASH_PAR_MATES2="$VIASH_PAR_MATES2;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -m2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --discard_orphans) + [ -n "$VIASH_PAR_DISCARD_ORPHANS" ] && ViashError Bad arguments for option \'--discard_orphans\': \'$VIASH_PAR_DISCARD_ORPHANS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISCARD_ORPHANS=true + shift 1 + ;; + --alignments) + if [ -z "$VIASH_PAR_ALIGNMENTS" ]; then + VIASH_PAR_ALIGNMENTS="$2" + else + VIASH_PAR_ALIGNMENTS="$VIASH_PAR_ALIGNMENTS;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignments. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignments=*) + if [ -z "$VIASH_PAR_ALIGNMENTS" ]; then + VIASH_PAR_ALIGNMENTS=$(ViashRemoveFlags "$1") + else + VIASH_PAR_ALIGNMENTS="$VIASH_PAR_ALIGNMENTS;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -a) + if [ -z "$VIASH_PAR_ALIGNMENTS" ]; then + VIASH_PAR_ALIGNMENTS="$2" + else + VIASH_PAR_ALIGNMENTS="$VIASH_PAR_ALIGNMENTS;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -a. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --eqclasses) + [ -n "$VIASH_PAR_EQCLASSES" ] && ViashError Bad arguments for option \'--eqclasses\': \'$VIASH_PAR_EQCLASSES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EQCLASSES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --eqclasses. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --eqclasses=*) + [ -n "$VIASH_PAR_EQCLASSES" ] && ViashError Bad arguments for option \'--eqclasses=*\': \'$VIASH_PAR_EQCLASSES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EQCLASSES=$(ViashRemoveFlags "$1") + shift 1 + ;; + -e) + [ -n "$VIASH_PAR_EQCLASSES" ] && ViashError Bad arguments for option \'-e\': \'$VIASH_PAR_EQCLASSES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EQCLASSES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -e. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --targets) + [ -n "$VIASH_PAR_TARGETS" ] && ViashError Bad arguments for option \'--targets\': \'$VIASH_PAR_TARGETS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TARGETS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --targets. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --targets=*) + [ -n "$VIASH_PAR_TARGETS" ] && ViashError Bad arguments for option \'--targets=*\': \'$VIASH_PAR_TARGETS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TARGETS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -t) + [ -n "$VIASH_PAR_TARGETS" ] && ViashError Bad arguments for option \'-t\': \'$VIASH_PAR_TARGETS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TARGETS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -t. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ont) + [ -n "$VIASH_PAR_ONT" ] && ViashError Bad arguments for option \'--ont\': \'$VIASH_PAR_ONT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ONT=true + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quant_results) + [ -n "$VIASH_PAR_QUANT_RESULTS" ] && ViashError Bad arguments for option \'--quant_results\': \'$VIASH_PAR_QUANT_RESULTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUANT_RESULTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --quant_results. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quant_results=*) + [ -n "$VIASH_PAR_QUANT_RESULTS" ] && ViashError Bad arguments for option \'--quant_results=*\': \'$VIASH_PAR_QUANT_RESULTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUANT_RESULTS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --seq_bias) + [ -n "$VIASH_PAR_SEQ_BIAS" ] && ViashError Bad arguments for option \'--seq_bias\': \'$VIASH_PAR_SEQ_BIAS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEQ_BIAS=true + shift 1 + ;; + --gc_bias) + [ -n "$VIASH_PAR_GC_BIAS" ] && ViashError Bad arguments for option \'--gc_bias\': \'$VIASH_PAR_GC_BIAS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GC_BIAS=true + shift 1 + ;; + --pos_bias) + [ -n "$VIASH_PAR_POS_BIAS" ] && ViashError Bad arguments for option \'--pos_bias\': \'$VIASH_PAR_POS_BIAS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_POS_BIAS=true + shift 1 + ;; + --incompat_prior) + [ -n "$VIASH_PAR_INCOMPAT_PRIOR" ] && ViashError Bad arguments for option \'--incompat_prior\': \'$VIASH_PAR_INCOMPAT_PRIOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INCOMPAT_PRIOR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --incompat_prior. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --incompat_prior=*) + [ -n "$VIASH_PAR_INCOMPAT_PRIOR" ] && ViashError Bad arguments for option \'--incompat_prior=*\': \'$VIASH_PAR_INCOMPAT_PRIOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INCOMPAT_PRIOR=$(ViashRemoveFlags "$1") + shift 1 + ;; + --gene_map) + [ -n "$VIASH_PAR_GENE_MAP" ] && ViashError Bad arguments for option \'--gene_map\': \'$VIASH_PAR_GENE_MAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENE_MAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --gene_map. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --gene_map=*) + [ -n "$VIASH_PAR_GENE_MAP" ] && ViashError Bad arguments for option \'--gene_map=*\': \'$VIASH_PAR_GENE_MAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENE_MAP=$(ViashRemoveFlags "$1") + shift 1 + ;; + -g) + [ -n "$VIASH_PAR_GENE_MAP" ] && ViashError Bad arguments for option \'-g\': \'$VIASH_PAR_GENE_MAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENE_MAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -g. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --aux_target_file) + [ -n "$VIASH_PAR_AUX_TARGET_FILE" ] && ViashError Bad arguments for option \'--aux_target_file\': \'$VIASH_PAR_AUX_TARGET_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUX_TARGET_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --aux_target_file. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --aux_target_file=*) + [ -n "$VIASH_PAR_AUX_TARGET_FILE" ] && ViashError Bad arguments for option \'--aux_target_file=*\': \'$VIASH_PAR_AUX_TARGET_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUX_TARGET_FILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --meta) + [ -n "$VIASH_PAR_META" ] && ViashError Bad arguments for option \'--meta\': \'$VIASH_PAR_META\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_META=true + shift 1 + ;; + --score_exp) + [ -n "$VIASH_PAR_SCORE_EXP" ] && ViashError Bad arguments for option \'--score_exp\': \'$VIASH_PAR_SCORE_EXP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCORE_EXP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --score_exp. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --score_exp=*) + [ -n "$VIASH_PAR_SCORE_EXP" ] && ViashError Bad arguments for option \'--score_exp=*\': \'$VIASH_PAR_SCORE_EXP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCORE_EXP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --discard_orphans_quasi) + [ -n "$VIASH_PAR_DISCARD_ORPHANS_QUASI" ] && ViashError Bad arguments for option \'--discard_orphans_quasi\': \'$VIASH_PAR_DISCARD_ORPHANS_QUASI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISCARD_ORPHANS_QUASI=true + shift 1 + ;; + --consensus_slack) + [ -n "$VIASH_PAR_CONSENSUS_SLACK" ] && ViashError Bad arguments for option \'--consensus_slack\': \'$VIASH_PAR_CONSENSUS_SLACK\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CONSENSUS_SLACK="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --consensus_slack. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --consensus_slack=*) + [ -n "$VIASH_PAR_CONSENSUS_SLACK" ] && ViashError Bad arguments for option \'--consensus_slack=*\': \'$VIASH_PAR_CONSENSUS_SLACK\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CONSENSUS_SLACK=$(ViashRemoveFlags "$1") + shift 1 + ;; + --pre_merge_chain_sub_thresh) + [ -n "$VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH" ] && ViashError Bad arguments for option \'--pre_merge_chain_sub_thresh\': \'$VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --pre_merge_chain_sub_thresh. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --pre_merge_chain_sub_thresh=*) + [ -n "$VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH" ] && ViashError Bad arguments for option \'--pre_merge_chain_sub_thresh=*\': \'$VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH=$(ViashRemoveFlags "$1") + shift 1 + ;; + --post_merge_chain_sub_thresh) + [ -n "$VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH" ] && ViashError Bad arguments for option \'--post_merge_chain_sub_thresh\': \'$VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --post_merge_chain_sub_thresh. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --post_merge_chain_sub_thresh=*) + [ -n "$VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH" ] && ViashError Bad arguments for option \'--post_merge_chain_sub_thresh=*\': \'$VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH=$(ViashRemoveFlags "$1") + shift 1 + ;; + --orphan_chain_sub_thresh) + [ -n "$VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH" ] && ViashError Bad arguments for option \'--orphan_chain_sub_thresh\': \'$VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --orphan_chain_sub_thresh. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --orphan_chain_sub_thresh=*) + [ -n "$VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH" ] && ViashError Bad arguments for option \'--orphan_chain_sub_thresh=*\': \'$VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH=$(ViashRemoveFlags "$1") + shift 1 + ;; + --min_score_fraction) + [ -n "$VIASH_PAR_MIN_SCORE_FRACTION" ] && ViashError Bad arguments for option \'--min_score_fraction\': \'$VIASH_PAR_MIN_SCORE_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_SCORE_FRACTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_score_fraction. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_score_fraction=*) + [ -n "$VIASH_PAR_MIN_SCORE_FRACTION" ] && ViashError Bad arguments for option \'--min_score_fraction=*\': \'$VIASH_PAR_MIN_SCORE_FRACTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_SCORE_FRACTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --mismatch_seed_skip) + [ -n "$VIASH_PAR_MISMATCH_SEED_SKIP" ] && ViashError Bad arguments for option \'--mismatch_seed_skip\': \'$VIASH_PAR_MISMATCH_SEED_SKIP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MISMATCH_SEED_SKIP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --mismatch_seed_skip. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mismatch_seed_skip=*) + [ -n "$VIASH_PAR_MISMATCH_SEED_SKIP" ] && ViashError Bad arguments for option \'--mismatch_seed_skip=*\': \'$VIASH_PAR_MISMATCH_SEED_SKIP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MISMATCH_SEED_SKIP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --disable_chaining_heuristic) + [ -n "$VIASH_PAR_DISABLE_CHAINING_HEURISTIC" ] && ViashError Bad arguments for option \'--disable_chaining_heuristic\': \'$VIASH_PAR_DISABLE_CHAINING_HEURISTIC\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DISABLE_CHAINING_HEURISTIC=true + shift 1 + ;; + --decoy_threshold) + [ -n "$VIASH_PAR_DECOY_THRESHOLD" ] && ViashError Bad arguments for option \'--decoy_threshold\': \'$VIASH_PAR_DECOY_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DECOY_THRESHOLD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --decoy_threshold. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --decoy_threshold=*) + [ -n "$VIASH_PAR_DECOY_THRESHOLD" ] && ViashError Bad arguments for option \'--decoy_threshold=*\': \'$VIASH_PAR_DECOY_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DECOY_THRESHOLD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --ma) + [ -n "$VIASH_PAR_MA" ] && ViashError Bad arguments for option \'--ma\': \'$VIASH_PAR_MA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --ma. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ma=*) + [ -n "$VIASH_PAR_MA" ] && ViashError Bad arguments for option \'--ma=*\': \'$VIASH_PAR_MA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MA=$(ViashRemoveFlags "$1") + shift 1 + ;; + --mp) + [ -n "$VIASH_PAR_MP" ] && ViashError Bad arguments for option \'--mp\': \'$VIASH_PAR_MP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --mp. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mp=*) + [ -n "$VIASH_PAR_MP" ] && ViashError Bad arguments for option \'--mp=*\': \'$VIASH_PAR_MP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --go) + [ -n "$VIASH_PAR_GO" ] && ViashError Bad arguments for option \'--go\': \'$VIASH_PAR_GO\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GO="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --go. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --go=*) + [ -n "$VIASH_PAR_GO" ] && ViashError Bad arguments for option \'--go=*\': \'$VIASH_PAR_GO\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GO=$(ViashRemoveFlags "$1") + shift 1 + ;; + --ge) + [ -n "$VIASH_PAR_GE" ] && ViashError Bad arguments for option \'--ge\': \'$VIASH_PAR_GE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --ge. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ge=*) + [ -n "$VIASH_PAR_GE" ] && ViashError Bad arguments for option \'--ge=*\': \'$VIASH_PAR_GE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bandwidth) + [ -n "$VIASH_PAR_BANDWIDTH" ] && ViashError Bad arguments for option \'--bandwidth\': \'$VIASH_PAR_BANDWIDTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BANDWIDTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bandwidth. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bandwidth=*) + [ -n "$VIASH_PAR_BANDWIDTH" ] && ViashError Bad arguments for option \'--bandwidth=*\': \'$VIASH_PAR_BANDWIDTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BANDWIDTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + --allow_dovetail) + [ -n "$VIASH_PAR_ALLOW_DOVETAIL" ] && ViashError Bad arguments for option \'--allow_dovetail\': \'$VIASH_PAR_ALLOW_DOVETAIL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALLOW_DOVETAIL=true + shift 1 + ;; + --recover_orphans) + [ -n "$VIASH_PAR_RECOVER_ORPHANS" ] && ViashError Bad arguments for option \'--recover_orphans\': \'$VIASH_PAR_RECOVER_ORPHANS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RECOVER_ORPHANS=true + shift 1 + ;; + --mimicBT2) + [ -n "$VIASH_PAR_MIMICBT2" ] && ViashError Bad arguments for option \'--mimicBT2\': \'$VIASH_PAR_MIMICBT2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIMICBT2=true + shift 1 + ;; + --mimic_strictBT2) + [ -n "$VIASH_PAR_MIMIC_STRICTBT2" ] && ViashError Bad arguments for option \'--mimic_strictBT2\': \'$VIASH_PAR_MIMIC_STRICTBT2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIMIC_STRICTBT2=true + shift 1 + ;; + --softclip) + [ -n "$VIASH_PAR_SOFTCLIP" ] && ViashError Bad arguments for option \'--softclip\': \'$VIASH_PAR_SOFTCLIP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SOFTCLIP=true + shift 1 + ;; + --softclip_overhangs) + [ -n "$VIASH_PAR_SOFTCLIP_OVERHANGS" ] && ViashError Bad arguments for option \'--softclip_overhangs\': \'$VIASH_PAR_SOFTCLIP_OVERHANGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SOFTCLIP_OVERHANGS=true + shift 1 + ;; + --full_length_alignment) + [ -n "$VIASH_PAR_FULL_LENGTH_ALIGNMENT" ] && ViashError Bad arguments for option \'--full_length_alignment\': \'$VIASH_PAR_FULL_LENGTH_ALIGNMENT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FULL_LENGTH_ALIGNMENT=true + shift 1 + ;; + --hard_filter) + [ -n "$VIASH_PAR_HARD_FILTER" ] && ViashError Bad arguments for option \'--hard_filter\': \'$VIASH_PAR_HARD_FILTER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HARD_FILTER=true + shift 1 + ;; + --min_aln_prob) + [ -n "$VIASH_PAR_MIN_ALN_PROB" ] && ViashError Bad arguments for option \'--min_aln_prob\': \'$VIASH_PAR_MIN_ALN_PROB\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ALN_PROB="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_aln_prob. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_aln_prob=*) + [ -n "$VIASH_PAR_MIN_ALN_PROB" ] && ViashError Bad arguments for option \'--min_aln_prob=*\': \'$VIASH_PAR_MIN_ALN_PROB\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ALN_PROB=$(ViashRemoveFlags "$1") + shift 1 + ;; + --write_mappings) + [ -n "$VIASH_PAR_WRITE_MAPPINGS" ] && ViashError Bad arguments for option \'--write_mappings\': \'$VIASH_PAR_WRITE_MAPPINGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WRITE_MAPPINGS=true + shift 1 + ;; + -z) + [ -n "$VIASH_PAR_WRITE_MAPPINGS" ] && ViashError Bad arguments for option \'-z\': \'$VIASH_PAR_WRITE_MAPPINGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WRITE_MAPPINGS=true + shift 1 + ;; + --mapping_sam) + [ -n "$VIASH_PAR_MAPPING_SAM" ] && ViashError Bad arguments for option \'--mapping_sam\': \'$VIASH_PAR_MAPPING_SAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAPPING_SAM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --mapping_sam. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mapping_sam=*) + [ -n "$VIASH_PAR_MAPPING_SAM" ] && ViashError Bad arguments for option \'--mapping_sam=*\': \'$VIASH_PAR_MAPPING_SAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAPPING_SAM=$(ViashRemoveFlags "$1") + shift 1 + ;; + --write_qualities) + [ -n "$VIASH_PAR_WRITE_QUALITIES" ] && ViashError Bad arguments for option \'--write_qualities\': \'$VIASH_PAR_WRITE_QUALITIES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WRITE_QUALITIES=true + shift 1 + ;; + --hit_filter_policy) + [ -n "$VIASH_PAR_HIT_FILTER_POLICY" ] && ViashError Bad arguments for option \'--hit_filter_policy\': \'$VIASH_PAR_HIT_FILTER_POLICY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HIT_FILTER_POLICY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --hit_filter_policy. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --hit_filter_policy=*) + [ -n "$VIASH_PAR_HIT_FILTER_POLICY" ] && ViashError Bad arguments for option \'--hit_filter_policy=*\': \'$VIASH_PAR_HIT_FILTER_POLICY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HIT_FILTER_POLICY=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alternative_init_mode) + [ -n "$VIASH_PAR_ALTERNATIVE_INIT_MODE" ] && ViashError Bad arguments for option \'--alternative_init_mode\': \'$VIASH_PAR_ALTERNATIVE_INIT_MODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALTERNATIVE_INIT_MODE=true + shift 1 + ;; + --aux_dir) + [ -n "$VIASH_PAR_AUX_DIR" ] && ViashError Bad arguments for option \'--aux_dir\': \'$VIASH_PAR_AUX_DIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUX_DIR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --aux_dir. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --aux_dir=*) + [ -n "$VIASH_PAR_AUX_DIR" ] && ViashError Bad arguments for option \'--aux_dir=*\': \'$VIASH_PAR_AUX_DIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUX_DIR=$(ViashRemoveFlags "$1") + shift 1 + ;; + --skip_quant) + [ -n "$VIASH_PAR_SKIP_QUANT" ] && ViashError Bad arguments for option \'--skip_quant\': \'$VIASH_PAR_SKIP_QUANT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SKIP_QUANT=true + shift 1 + ;; + --dump_eq) + [ -n "$VIASH_PAR_DUMP_EQ" ] && ViashError Bad arguments for option \'--dump_eq\': \'$VIASH_PAR_DUMP_EQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DUMP_EQ=true + shift 1 + ;; + --dump_eq_weights) + [ -n "$VIASH_PAR_DUMP_EQ_WEIGHTS" ] && ViashError Bad arguments for option \'--dump_eq_weights\': \'$VIASH_PAR_DUMP_EQ_WEIGHTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DUMP_EQ_WEIGHTS=true + shift 1 + ;; + -d) + [ -n "$VIASH_PAR_DUMP_EQ_WEIGHTS" ] && ViashError Bad arguments for option \'-d\': \'$VIASH_PAR_DUMP_EQ_WEIGHTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_DUMP_EQ_WEIGHTS=true + shift 1 + ;; + --min_assigned_frags) + [ -n "$VIASH_PAR_MIN_ASSIGNED_FRAGS" ] && ViashError Bad arguments for option \'--min_assigned_frags\': \'$VIASH_PAR_MIN_ASSIGNED_FRAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ASSIGNED_FRAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_assigned_frags. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_assigned_frags=*) + [ -n "$VIASH_PAR_MIN_ASSIGNED_FRAGS" ] && ViashError Bad arguments for option \'--min_assigned_frags=*\': \'$VIASH_PAR_MIN_ASSIGNED_FRAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_ASSIGNED_FRAGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --reduce_GC_memory) + [ -n "$VIASH_PAR_REDUCE_GC_MEMORY" ] && ViashError Bad arguments for option \'--reduce_GC_memory\': \'$VIASH_PAR_REDUCE_GC_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REDUCE_GC_MEMORY=true + shift 1 + ;; + --bias_speed_samp) + [ -n "$VIASH_PAR_BIAS_SPEED_SAMP" ] && ViashError Bad arguments for option \'--bias_speed_samp\': \'$VIASH_PAR_BIAS_SPEED_SAMP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BIAS_SPEED_SAMP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bias_speed_samp. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bias_speed_samp=*) + [ -n "$VIASH_PAR_BIAS_SPEED_SAMP" ] && ViashError Bad arguments for option \'--bias_speed_samp=*\': \'$VIASH_PAR_BIAS_SPEED_SAMP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BIAS_SPEED_SAMP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --fld_max) + [ -n "$VIASH_PAR_FLD_MAX" ] && ViashError Bad arguments for option \'--fld_max\': \'$VIASH_PAR_FLD_MAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FLD_MAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --fld_max. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fld_max=*) + [ -n "$VIASH_PAR_FLD_MAX" ] && ViashError Bad arguments for option \'--fld_max=*\': \'$VIASH_PAR_FLD_MAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FLD_MAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --fld_mean) + [ -n "$VIASH_PAR_FLD_MEAN" ] && ViashError Bad arguments for option \'--fld_mean\': \'$VIASH_PAR_FLD_MEAN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FLD_MEAN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --fld_mean. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fld_mean=*) + [ -n "$VIASH_PAR_FLD_MEAN" ] && ViashError Bad arguments for option \'--fld_mean=*\': \'$VIASH_PAR_FLD_MEAN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FLD_MEAN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --fld_SD) + [ -n "$VIASH_PAR_FLD_SD" ] && ViashError Bad arguments for option \'--fld_SD\': \'$VIASH_PAR_FLD_SD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FLD_SD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --fld_SD. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fld_SD=*) + [ -n "$VIASH_PAR_FLD_SD" ] && ViashError Bad arguments for option \'--fld_SD=*\': \'$VIASH_PAR_FLD_SD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FLD_SD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --forgetting_factor) + [ -n "$VIASH_PAR_FORGETTING_FACTOR" ] && ViashError Bad arguments for option \'--forgetting_factor\': \'$VIASH_PAR_FORGETTING_FACTOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORGETTING_FACTOR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --forgetting_factor. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --forgetting_factor=*) + [ -n "$VIASH_PAR_FORGETTING_FACTOR" ] && ViashError Bad arguments for option \'--forgetting_factor=*\': \'$VIASH_PAR_FORGETTING_FACTOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORGETTING_FACTOR=$(ViashRemoveFlags "$1") + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_FORGETTING_FACTOR" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_FORGETTING_FACTOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FORGETTING_FACTOR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -f. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --init_uniform) + [ -n "$VIASH_PAR_INIT_UNIFORM" ] && ViashError Bad arguments for option \'--init_uniform\': \'$VIASH_PAR_INIT_UNIFORM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INIT_UNIFORM=true + shift 1 + ;; + --max_occs_per_hit) + [ -n "$VIASH_PAR_MAX_OCCS_PER_HIT" ] && ViashError Bad arguments for option \'--max_occs_per_hit\': \'$VIASH_PAR_MAX_OCCS_PER_HIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_OCCS_PER_HIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_occs_per_hit. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_occs_per_hit=*) + [ -n "$VIASH_PAR_MAX_OCCS_PER_HIT" ] && ViashError Bad arguments for option \'--max_occs_per_hit=*\': \'$VIASH_PAR_MAX_OCCS_PER_HIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_OCCS_PER_HIT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --max_read_occ) + [ -n "$VIASH_PAR_MAX_READ_OCC" ] && ViashError Bad arguments for option \'--max_read_occ\': \'$VIASH_PAR_MAX_READ_OCC\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_READ_OCC="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --max_read_occ. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --max_read_occ=*) + [ -n "$VIASH_PAR_MAX_READ_OCC" ] && ViashError Bad arguments for option \'--max_read_occ=*\': \'$VIASH_PAR_MAX_READ_OCC\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAX_READ_OCC=$(ViashRemoveFlags "$1") + shift 1 + ;; + --no_length_correction) + [ -n "$VIASH_PAR_NO_LENGTH_CORRECTION" ] && ViashError Bad arguments for option \'--no_length_correction\': \'$VIASH_PAR_NO_LENGTH_CORRECTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_LENGTH_CORRECTION=true + shift 1 + ;; + --no_effective_length_correction) + [ -n "$VIASH_PAR_NO_EFFECTIVE_LENGTH_CORRECTION" ] && ViashError Bad arguments for option \'--no_effective_length_correction\': \'$VIASH_PAR_NO_EFFECTIVE_LENGTH_CORRECTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_EFFECTIVE_LENGTH_CORRECTION=true + shift 1 + ;; + --no_single_frag_prob) + [ -n "$VIASH_PAR_NO_SINGLE_FRAG_PROB" ] && ViashError Bad arguments for option \'--no_single_frag_prob\': \'$VIASH_PAR_NO_SINGLE_FRAG_PROB\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_SINGLE_FRAG_PROB=true + shift 1 + ;; + --no_frag_length_dist) + [ -n "$VIASH_PAR_NO_FRAG_LENGTH_DIST" ] && ViashError Bad arguments for option \'--no_frag_length_dist\': \'$VIASH_PAR_NO_FRAG_LENGTH_DIST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_FRAG_LENGTH_DIST=true + shift 1 + ;; + --no_bias_length_threshold) + [ -n "$VIASH_PAR_NO_BIAS_LENGTH_THRESHOLD" ] && ViashError Bad arguments for option \'--no_bias_length_threshold\': \'$VIASH_PAR_NO_BIAS_LENGTH_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_BIAS_LENGTH_THRESHOLD=true + shift 1 + ;; + --num_bias_samples) + [ -n "$VIASH_PAR_NUM_BIAS_SAMPLES" ] && ViashError Bad arguments for option \'--num_bias_samples\': \'$VIASH_PAR_NUM_BIAS_SAMPLES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_BIAS_SAMPLES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --num_bias_samples. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --num_bias_samples=*) + [ -n "$VIASH_PAR_NUM_BIAS_SAMPLES" ] && ViashError Bad arguments for option \'--num_bias_samples=*\': \'$VIASH_PAR_NUM_BIAS_SAMPLES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_BIAS_SAMPLES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --num_aux_model_samples) + [ -n "$VIASH_PAR_NUM_AUX_MODEL_SAMPLES" ] && ViashError Bad arguments for option \'--num_aux_model_samples\': \'$VIASH_PAR_NUM_AUX_MODEL_SAMPLES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_AUX_MODEL_SAMPLES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --num_aux_model_samples. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --num_aux_model_samples=*) + [ -n "$VIASH_PAR_NUM_AUX_MODEL_SAMPLES" ] && ViashError Bad arguments for option \'--num_aux_model_samples=*\': \'$VIASH_PAR_NUM_AUX_MODEL_SAMPLES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_AUX_MODEL_SAMPLES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --num_pre_aux_model_samples) + [ -n "$VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES" ] && ViashError Bad arguments for option \'--num_pre_aux_model_samples\': \'$VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --num_pre_aux_model_samples. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --num_pre_aux_model_samples=*) + [ -n "$VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES" ] && ViashError Bad arguments for option \'--num_pre_aux_model_samples=*\': \'$VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --useEM) + [ -n "$VIASH_PAR_USEEM" ] && ViashError Bad arguments for option \'--useEM\': \'$VIASH_PAR_USEEM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_USEEM=true + shift 1 + ;; + --useVBOpt) + [ -n "$VIASH_PAR_USEVBOPT" ] && ViashError Bad arguments for option \'--useVBOpt\': \'$VIASH_PAR_USEVBOPT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_USEVBOPT=true + shift 1 + ;; + --range_factorization_bins) + [ -n "$VIASH_PAR_RANGE_FACTORIZATION_BINS" ] && ViashError Bad arguments for option \'--range_factorization_bins\': \'$VIASH_PAR_RANGE_FACTORIZATION_BINS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RANGE_FACTORIZATION_BINS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --range_factorization_bins. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --range_factorization_bins=*) + [ -n "$VIASH_PAR_RANGE_FACTORIZATION_BINS" ] && ViashError Bad arguments for option \'--range_factorization_bins=*\': \'$VIASH_PAR_RANGE_FACTORIZATION_BINS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RANGE_FACTORIZATION_BINS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --num_Gibbs_samples) + [ -n "$VIASH_PAR_NUM_GIBBS_SAMPLES" ] && ViashError Bad arguments for option \'--num_Gibbs_samples\': \'$VIASH_PAR_NUM_GIBBS_SAMPLES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_GIBBS_SAMPLES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --num_Gibbs_samples. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --num_Gibbs_samples=*) + [ -n "$VIASH_PAR_NUM_GIBBS_SAMPLES" ] && ViashError Bad arguments for option \'--num_Gibbs_samples=*\': \'$VIASH_PAR_NUM_GIBBS_SAMPLES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_GIBBS_SAMPLES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --no_Gamma_draw) + [ -n "$VIASH_PAR_NO_GAMMA_DRAW" ] && ViashError Bad arguments for option \'--no_Gamma_draw\': \'$VIASH_PAR_NO_GAMMA_DRAW\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_GAMMA_DRAW=true + shift 1 + ;; + --num_bootstraps) + [ -n "$VIASH_PAR_NUM_BOOTSTRAPS" ] && ViashError Bad arguments for option \'--num_bootstraps\': \'$VIASH_PAR_NUM_BOOTSTRAPS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_BOOTSTRAPS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --num_bootstraps. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --num_bootstraps=*) + [ -n "$VIASH_PAR_NUM_BOOTSTRAPS" ] && ViashError Bad arguments for option \'--num_bootstraps=*\': \'$VIASH_PAR_NUM_BOOTSTRAPS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_BOOTSTRAPS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bootstrap_reproject) + [ -n "$VIASH_PAR_BOOTSTRAP_REPROJECT" ] && ViashError Bad arguments for option \'--bootstrap_reproject\': \'$VIASH_PAR_BOOTSTRAP_REPROJECT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BOOTSTRAP_REPROJECT=true + shift 1 + ;; + --thinning_factor) + [ -n "$VIASH_PAR_THINNING_FACTOR" ] && ViashError Bad arguments for option \'--thinning_factor\': \'$VIASH_PAR_THINNING_FACTOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_THINNING_FACTOR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --thinning_factor. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --thinning_factor=*) + [ -n "$VIASH_PAR_THINNING_FACTOR" ] && ViashError Bad arguments for option \'--thinning_factor=*\': \'$VIASH_PAR_THINNING_FACTOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_THINNING_FACTOR=$(ViashRemoveFlags "$1") + shift 1 + ;; + --quiet) + [ -n "$VIASH_PAR_QUIET" ] && ViashError Bad arguments for option \'--quiet\': \'$VIASH_PAR_QUIET\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUIET=true + shift 1 + ;; + -q) + [ -n "$VIASH_PAR_QUIET" ] && ViashError Bad arguments for option \'-q\': \'$VIASH_PAR_QUIET\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUIET=true + shift 1 + ;; + --per_transcript_prior) + [ -n "$VIASH_PAR_PER_TRANSCRIPT_PRIOR" ] && ViashError Bad arguments for option \'--per_transcript_prior\': \'$VIASH_PAR_PER_TRANSCRIPT_PRIOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PER_TRANSCRIPT_PRIOR=true + shift 1 + ;; + --per_nucleotide_prior) + [ -n "$VIASH_PAR_PER_NUCLEOTIDE_PRIOR" ] && ViashError Bad arguments for option \'--per_nucleotide_prior\': \'$VIASH_PAR_PER_NUCLEOTIDE_PRIOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PER_NUCLEOTIDE_PRIOR=true + shift 1 + ;; + --sig_digits) + [ -n "$VIASH_PAR_SIG_DIGITS" ] && ViashError Bad arguments for option \'--sig_digits\': \'$VIASH_PAR_SIG_DIGITS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SIG_DIGITS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sig_digits. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sig_digits=*) + [ -n "$VIASH_PAR_SIG_DIGITS" ] && ViashError Bad arguments for option \'--sig_digits=*\': \'$VIASH_PAR_SIG_DIGITS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SIG_DIGITS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --vb_prior) + [ -n "$VIASH_PAR_VB_PRIOR" ] && ViashError Bad arguments for option \'--vb_prior\': \'$VIASH_PAR_VB_PRIOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_VB_PRIOR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --vb_prior. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --vb_prior=*) + [ -n "$VIASH_PAR_VB_PRIOR" ] && ViashError Bad arguments for option \'--vb_prior=*\': \'$VIASH_PAR_VB_PRIOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_VB_PRIOR=$(ViashRemoveFlags "$1") + shift 1 + ;; + --write_orphan_links) + [ -n "$VIASH_PAR_WRITE_ORPHAN_LINKS" ] && ViashError Bad arguments for option \'--write_orphan_links\': \'$VIASH_PAR_WRITE_ORPHAN_LINKS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WRITE_ORPHAN_LINKS=true + shift 1 + ;; + --write_unmapped_names) + [ -n "$VIASH_PAR_WRITE_UNMAPPED_NAMES" ] && ViashError Bad arguments for option \'--write_unmapped_names\': \'$VIASH_PAR_WRITE_UNMAPPED_NAMES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WRITE_UNMAPPED_NAMES=true + shift 1 + ;; + --no_error_model) + [ -n "$VIASH_PAR_NO_ERROR_MODEL" ] && ViashError Bad arguments for option \'--no_error_model\': \'$VIASH_PAR_NO_ERROR_MODEL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_ERROR_MODEL=true + shift 1 + ;; + --num_error_bins) + [ -n "$VIASH_PAR_NUM_ERROR_BINS" ] && ViashError Bad arguments for option \'--num_error_bins\': \'$VIASH_PAR_NUM_ERROR_BINS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_ERROR_BINS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --num_error_bins. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --num_error_bins=*) + [ -n "$VIASH_PAR_NUM_ERROR_BINS" ] && ViashError Bad arguments for option \'--num_error_bins=*\': \'$VIASH_PAR_NUM_ERROR_BINS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NUM_ERROR_BINS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sample_out) + [ -n "$VIASH_PAR_SAMPLE_OUT" ] && ViashError Bad arguments for option \'--sample_out\': \'$VIASH_PAR_SAMPLE_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_OUT=true + shift 1 + ;; + -s) + [ -n "$VIASH_PAR_SAMPLE_OUT" ] && ViashError Bad arguments for option \'-s\': \'$VIASH_PAR_SAMPLE_OUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_OUT=true + shift 1 + ;; + --sample_unaligned) + [ -n "$VIASH_PAR_SAMPLE_UNALIGNED" ] && ViashError Bad arguments for option \'--sample_unaligned\': \'$VIASH_PAR_SAMPLE_UNALIGNED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_UNALIGNED=true + shift 1 + ;; + -u) + [ -n "$VIASH_PAR_SAMPLE_UNALIGNED" ] && ViashError Bad arguments for option \'-u\': \'$VIASH_PAR_SAMPLE_UNALIGNED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SAMPLE_UNALIGNED=true + shift 1 + ;; + --gencode) + [ -n "$VIASH_PAR_GENCODE" ] && ViashError Bad arguments for option \'--gencode\': \'$VIASH_PAR_GENCODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENCODE=true + shift 1 + ;; + --mapping_cache_memory_limit) + [ -n "$VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT" ] && ViashError Bad arguments for option \'--mapping_cache_memory_limit\': \'$VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --mapping_cache_memory_limit. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mapping_cache_memory_limit=*) + [ -n "$VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT" ] && ViashError Bad arguments for option \'--mapping_cache_memory_limit=*\': \'$VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/salmon/salmon_quant:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_QUANT_RESULTS+x} ]; then + ViashError '--quant_results' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_LIB_TYPE+x} ]; then + VIASH_PAR_LIB_TYPE="A" +fi +if [ -z ${VIASH_PAR_DISCARD_ORPHANS+x} ]; then + VIASH_PAR_DISCARD_ORPHANS="false" +fi +if [ -z ${VIASH_PAR_ONT+x} ]; then + VIASH_PAR_ONT="false" +fi +if [ -z ${VIASH_PAR_SEQ_BIAS+x} ]; then + VIASH_PAR_SEQ_BIAS="false" +fi +if [ -z ${VIASH_PAR_GC_BIAS+x} ]; then + VIASH_PAR_GC_BIAS="false" +fi +if [ -z ${VIASH_PAR_POS_BIAS+x} ]; then + VIASH_PAR_POS_BIAS="false" +fi +if [ -z ${VIASH_PAR_META+x} ]; then + VIASH_PAR_META="false" +fi +if [ -z ${VIASH_PAR_DISCARD_ORPHANS_QUASI+x} ]; then + VIASH_PAR_DISCARD_ORPHANS_QUASI="false" +fi +if [ -z ${VIASH_PAR_DISABLE_CHAINING_HEURISTIC+x} ]; then + VIASH_PAR_DISABLE_CHAINING_HEURISTIC="false" +fi +if [ -z ${VIASH_PAR_ALLOW_DOVETAIL+x} ]; then + VIASH_PAR_ALLOW_DOVETAIL="false" +fi +if [ -z ${VIASH_PAR_RECOVER_ORPHANS+x} ]; then + VIASH_PAR_RECOVER_ORPHANS="false" +fi +if [ -z ${VIASH_PAR_MIMICBT2+x} ]; then + VIASH_PAR_MIMICBT2="false" +fi +if [ -z ${VIASH_PAR_MIMIC_STRICTBT2+x} ]; then + VIASH_PAR_MIMIC_STRICTBT2="false" +fi +if [ -z ${VIASH_PAR_SOFTCLIP+x} ]; then + VIASH_PAR_SOFTCLIP="false" +fi +if [ -z ${VIASH_PAR_SOFTCLIP_OVERHANGS+x} ]; then + VIASH_PAR_SOFTCLIP_OVERHANGS="false" +fi +if [ -z ${VIASH_PAR_FULL_LENGTH_ALIGNMENT+x} ]; then + VIASH_PAR_FULL_LENGTH_ALIGNMENT="false" +fi +if [ -z ${VIASH_PAR_HARD_FILTER+x} ]; then + VIASH_PAR_HARD_FILTER="false" +fi +if [ -z ${VIASH_PAR_WRITE_MAPPINGS+x} ]; then + VIASH_PAR_WRITE_MAPPINGS="false" +fi +if [ -z ${VIASH_PAR_WRITE_QUALITIES+x} ]; then + VIASH_PAR_WRITE_QUALITIES="false" +fi +if [ -z ${VIASH_PAR_ALTERNATIVE_INIT_MODE+x} ]; then + VIASH_PAR_ALTERNATIVE_INIT_MODE="false" +fi +if [ -z ${VIASH_PAR_SKIP_QUANT+x} ]; then + VIASH_PAR_SKIP_QUANT="false" +fi +if [ -z ${VIASH_PAR_DUMP_EQ+x} ]; then + VIASH_PAR_DUMP_EQ="false" +fi +if [ -z ${VIASH_PAR_DUMP_EQ_WEIGHTS+x} ]; then + VIASH_PAR_DUMP_EQ_WEIGHTS="false" +fi +if [ -z ${VIASH_PAR_REDUCE_GC_MEMORY+x} ]; then + VIASH_PAR_REDUCE_GC_MEMORY="false" +fi +if [ -z ${VIASH_PAR_INIT_UNIFORM+x} ]; then + VIASH_PAR_INIT_UNIFORM="false" +fi +if [ -z ${VIASH_PAR_NO_LENGTH_CORRECTION+x} ]; then + VIASH_PAR_NO_LENGTH_CORRECTION="false" +fi +if [ -z ${VIASH_PAR_NO_EFFECTIVE_LENGTH_CORRECTION+x} ]; then + VIASH_PAR_NO_EFFECTIVE_LENGTH_CORRECTION="false" +fi +if [ -z ${VIASH_PAR_NO_SINGLE_FRAG_PROB+x} ]; then + VIASH_PAR_NO_SINGLE_FRAG_PROB="false" +fi +if [ -z ${VIASH_PAR_NO_FRAG_LENGTH_DIST+x} ]; then + VIASH_PAR_NO_FRAG_LENGTH_DIST="false" +fi +if [ -z ${VIASH_PAR_NO_BIAS_LENGTH_THRESHOLD+x} ]; then + VIASH_PAR_NO_BIAS_LENGTH_THRESHOLD="false" +fi +if [ -z ${VIASH_PAR_USEEM+x} ]; then + VIASH_PAR_USEEM="false" +fi +if [ -z ${VIASH_PAR_USEVBOPT+x} ]; then + VIASH_PAR_USEVBOPT="false" +fi +if [ -z ${VIASH_PAR_NO_GAMMA_DRAW+x} ]; then + VIASH_PAR_NO_GAMMA_DRAW="false" +fi +if [ -z ${VIASH_PAR_BOOTSTRAP_REPROJECT+x} ]; then + VIASH_PAR_BOOTSTRAP_REPROJECT="false" +fi +if [ -z ${VIASH_PAR_QUIET+x} ]; then + VIASH_PAR_QUIET="false" +fi +if [ -z ${VIASH_PAR_PER_TRANSCRIPT_PRIOR+x} ]; then + VIASH_PAR_PER_TRANSCRIPT_PRIOR="false" +fi +if [ -z ${VIASH_PAR_PER_NUCLEOTIDE_PRIOR+x} ]; then + VIASH_PAR_PER_NUCLEOTIDE_PRIOR="false" +fi +if [ -z ${VIASH_PAR_WRITE_ORPHAN_LINKS+x} ]; then + VIASH_PAR_WRITE_ORPHAN_LINKS="false" +fi +if [ -z ${VIASH_PAR_WRITE_UNMAPPED_NAMES+x} ]; then + VIASH_PAR_WRITE_UNMAPPED_NAMES="false" +fi +if [ -z ${VIASH_PAR_NO_ERROR_MODEL+x} ]; then + VIASH_PAR_NO_ERROR_MODEL="false" +fi +if [ -z ${VIASH_PAR_SAMPLE_OUT+x} ]; then + VIASH_PAR_SAMPLE_OUT="false" +fi +if [ -z ${VIASH_PAR_SAMPLE_UNALIGNED+x} ]; then + VIASH_PAR_SAMPLE_UNALIGNED="false" +fi +if [ -z ${VIASH_PAR_GENCODE+x} ]; then + VIASH_PAR_GENCODE="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INDEX" ] && [ ! -e "$VIASH_PAR_INDEX" ]; then + ViashError "Input file '$VIASH_PAR_INDEX' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_UNMATED_READS" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_UNMATED_READS; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi +if [ ! -z "$VIASH_PAR_MATES1" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_MATES1; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi +if [ ! -z "$VIASH_PAR_MATES2" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_MATES2; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi +if [ ! -z "$VIASH_PAR_ALIGNMENTS" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_ALIGNMENTS; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi +if [ ! -z "$VIASH_PAR_EQCLASSES" ] && [ ! -e "$VIASH_PAR_EQCLASSES" ]; then + ViashError "Input file '$VIASH_PAR_EQCLASSES' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_TARGETS" ] && [ ! -e "$VIASH_PAR_TARGETS" ]; then + ViashError "Input file '$VIASH_PAR_TARGETS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_GENE_MAP" ] && [ ! -e "$VIASH_PAR_GENE_MAP" ]; then + ViashError "Input file '$VIASH_PAR_GENE_MAP' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_AUX_TARGET_FILE" ] && [ ! -e "$VIASH_PAR_AUX_TARGET_FILE" ]; then + ViashError "Input file '$VIASH_PAR_AUX_TARGET_FILE' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_DISCARD_ORPHANS" ]]; then + if ! [[ "$VIASH_PAR_DISCARD_ORPHANS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--discard_orphans' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ONT" ]]; then + if ! [[ "$VIASH_PAR_ONT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--ont' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SEQ_BIAS" ]]; then + if ! [[ "$VIASH_PAR_SEQ_BIAS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--seq_bias' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GC_BIAS" ]]; then + if ! [[ "$VIASH_PAR_GC_BIAS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--gc_bias' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_POS_BIAS" ]]; then + if ! [[ "$VIASH_PAR_POS_BIAS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--pos_bias' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_INCOMPAT_PRIOR" ]]; then + if ! [[ "$VIASH_PAR_INCOMPAT_PRIOR" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--incompat_prior' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_INCOMPAT_PRIOR '>=' 0.0 | bc` -eq 1 ]]; then + ViashError '--incompat_prior' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_INCOMPAT_PRIOR -v n2=0.0 'BEGIN { print (n1 >= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--incompat_prior' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--incompat_prior' specifies a minimum value but the value was not verified as neither \'bc\' or \`awk\` are present on the system. + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_INCOMPAT_PRIOR '<=' 1.0 | bc` -eq 1 ]]; then + ViashError '--incompat_prior' has to be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_INCOMPAT_PRIOR -v n2=1.0 'BEGIN { print (n1 <= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--incompat_prior' has be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--incompat_prior' specifies a maximum value but the value was not verified as neither \'bc\' or \'awk\' are present on the system. + fi +fi +if [[ -n "$VIASH_PAR_META" ]]; then + if ! [[ "$VIASH_PAR_META" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--meta' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCORE_EXP" ]]; then + if ! [[ "$VIASH_PAR_SCORE_EXP" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--score_exp' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DISCARD_ORPHANS_QUASI" ]]; then + if ! [[ "$VIASH_PAR_DISCARD_ORPHANS_QUASI" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--discard_orphans_quasi' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CONSENSUS_SLACK" ]]; then + if ! [[ "$VIASH_PAR_CONSENSUS_SLACK" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--consensus_slack' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_CONSENSUS_SLACK '>=' 0.0 | bc` -eq 1 ]]; then + ViashError '--consensus_slack' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_CONSENSUS_SLACK -v n2=0.0 'BEGIN { print (n1 >= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--consensus_slack' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--consensus_slack' specifies a minimum value but the value was not verified as neither \'bc\' or \`awk\` are present on the system. + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_CONSENSUS_SLACK '<=' 0.999999999 | bc` -eq 1 ]]; then + ViashError '--consensus_slack' has to be less than or equal to 0.999999999. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_CONSENSUS_SLACK -v n2=0.999999999 'BEGIN { print (n1 <= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--consensus_slack' has be less than or equal to 0.999999999. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--consensus_slack' specifies a maximum value but the value was not verified as neither \'bc\' or \'awk\' are present on the system. + fi +fi +if [[ -n "$VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH" ]]; then + if ! [[ "$VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--pre_merge_chain_sub_thresh' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH '>=' 0.0 | bc` -eq 1 ]]; then + ViashError '--pre_merge_chain_sub_thresh' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH -v n2=0.0 'BEGIN { print (n1 >= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--pre_merge_chain_sub_thresh' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--pre_merge_chain_sub_thresh' specifies a minimum value but the value was not verified as neither \'bc\' or \`awk\` are present on the system. + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH '<=' 1.0 | bc` -eq 1 ]]; then + ViashError '--pre_merge_chain_sub_thresh' has to be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH -v n2=1.0 'BEGIN { print (n1 <= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--pre_merge_chain_sub_thresh' has be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--pre_merge_chain_sub_thresh' specifies a maximum value but the value was not verified as neither \'bc\' or \'awk\' are present on the system. + fi +fi +if [[ -n "$VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH" ]]; then + if ! [[ "$VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--post_merge_chain_sub_thresh' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH '>=' 0.0 | bc` -eq 1 ]]; then + ViashError '--post_merge_chain_sub_thresh' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH -v n2=0.0 'BEGIN { print (n1 >= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--post_merge_chain_sub_thresh' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--post_merge_chain_sub_thresh' specifies a minimum value but the value was not verified as neither \'bc\' or \`awk\` are present on the system. + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH '<=' 1.0 | bc` -eq 1 ]]; then + ViashError '--post_merge_chain_sub_thresh' has to be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH -v n2=1.0 'BEGIN { print (n1 <= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--post_merge_chain_sub_thresh' has be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--post_merge_chain_sub_thresh' specifies a maximum value but the value was not verified as neither \'bc\' or \'awk\' are present on the system. + fi +fi +if [[ -n "$VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH" ]]; then + if ! [[ "$VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--orphan_chain_sub_thresh' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH '>=' 0.0 | bc` -eq 1 ]]; then + ViashError '--orphan_chain_sub_thresh' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH -v n2=0.0 'BEGIN { print (n1 >= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--orphan_chain_sub_thresh' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--orphan_chain_sub_thresh' specifies a minimum value but the value was not verified as neither \'bc\' or \`awk\` are present on the system. + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH '<=' 1.0 | bc` -eq 1 ]]; then + ViashError '--orphan_chain_sub_thresh' has to be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH -v n2=1.0 'BEGIN { print (n1 <= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--orphan_chain_sub_thresh' has be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--orphan_chain_sub_thresh' specifies a maximum value but the value was not verified as neither \'bc\' or \'awk\' are present on the system. + fi +fi +if [[ -n "$VIASH_PAR_MIN_SCORE_FRACTION" ]]; then + if ! [[ "$VIASH_PAR_MIN_SCORE_FRACTION" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--min_score_fraction' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_MIN_SCORE_FRACTION '>=' 1.0E-9 | bc` -eq 1 ]]; then + ViashError '--min_score_fraction' has be more than or equal to 1.0E-9. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_MIN_SCORE_FRACTION -v n2=1.0E-9 'BEGIN { print (n1 >= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--min_score_fraction' has be more than or equal to 1.0E-9. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--min_score_fraction' specifies a minimum value but the value was not verified as neither \'bc\' or \`awk\` are present on the system. + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_MIN_SCORE_FRACTION '<=' 1.0 | bc` -eq 1 ]]; then + ViashError '--min_score_fraction' has to be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_MIN_SCORE_FRACTION -v n2=1.0 'BEGIN { print (n1 <= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--min_score_fraction' has be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--min_score_fraction' specifies a maximum value but the value was not verified as neither \'bc\' or \'awk\' are present on the system. + fi +fi +if [[ -n "$VIASH_PAR_MISMATCH_SEED_SKIP" ]]; then + if ! [[ "$VIASH_PAR_MISMATCH_SEED_SKIP" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--mismatch_seed_skip' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DISABLE_CHAINING_HEURISTIC" ]]; then + if ! [[ "$VIASH_PAR_DISABLE_CHAINING_HEURISTIC" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--disable_chaining_heuristic' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DECOY_THRESHOLD" ]]; then + if ! [[ "$VIASH_PAR_DECOY_THRESHOLD" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--decoy_threshold' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_DECOY_THRESHOLD '>=' 0.0 | bc` -eq 1 ]]; then + ViashError '--decoy_threshold' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_DECOY_THRESHOLD -v n2=0.0 'BEGIN { print (n1 >= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--decoy_threshold' has be more than or equal to 0.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--decoy_threshold' specifies a minimum value but the value was not verified as neither \'bc\' or \`awk\` are present on the system. + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_DECOY_THRESHOLD '<=' 1.0 | bc` -eq 1 ]]; then + ViashError '--decoy_threshold' has to be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_DECOY_THRESHOLD -v n2=1.0 'BEGIN { print (n1 <= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--decoy_threshold' has be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--decoy_threshold' specifies a maximum value but the value was not verified as neither \'bc\' or \'awk\' are present on the system. + fi +fi +if [[ -n "$VIASH_PAR_MA" ]]; then + if ! [[ "$VIASH_PAR_MA" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--ma' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MP" ]]; then + if ! [[ "$VIASH_PAR_MP" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--mp' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GO" ]]; then + if ! [[ "$VIASH_PAR_GO" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--go' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GE" ]]; then + if ! [[ "$VIASH_PAR_GE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--ge' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BANDWIDTH" ]]; then + if ! [[ "$VIASH_PAR_BANDWIDTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--bandwidth' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ALLOW_DOVETAIL" ]]; then + if ! [[ "$VIASH_PAR_ALLOW_DOVETAIL" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--allow_dovetail' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_RECOVER_ORPHANS" ]]; then + if ! [[ "$VIASH_PAR_RECOVER_ORPHANS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--recover_orphans' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIMICBT2" ]]; then + if ! [[ "$VIASH_PAR_MIMICBT2" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--mimicBT2' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIMIC_STRICTBT2" ]]; then + if ! [[ "$VIASH_PAR_MIMIC_STRICTBT2" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--mimic_strictBT2' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SOFTCLIP" ]]; then + if ! [[ "$VIASH_PAR_SOFTCLIP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--softclip' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SOFTCLIP_OVERHANGS" ]]; then + if ! [[ "$VIASH_PAR_SOFTCLIP_OVERHANGS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--softclip_overhangs' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FULL_LENGTH_ALIGNMENT" ]]; then + if ! [[ "$VIASH_PAR_FULL_LENGTH_ALIGNMENT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--full_length_alignment' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_HARD_FILTER" ]]; then + if ! [[ "$VIASH_PAR_HARD_FILTER" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--hard_filter' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_ALN_PROB" ]]; then + if ! [[ "$VIASH_PAR_MIN_ALN_PROB" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--min_aln_prob' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WRITE_MAPPINGS" ]]; then + if ! [[ "$VIASH_PAR_WRITE_MAPPINGS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--write_mappings' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WRITE_QUALITIES" ]]; then + if ! [[ "$VIASH_PAR_WRITE_QUALITIES" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--write_qualities' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ALTERNATIVE_INIT_MODE" ]]; then + if ! [[ "$VIASH_PAR_ALTERNATIVE_INIT_MODE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--alternative_init_mode' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SKIP_QUANT" ]]; then + if ! [[ "$VIASH_PAR_SKIP_QUANT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--skip_quant' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DUMP_EQ" ]]; then + if ! [[ "$VIASH_PAR_DUMP_EQ" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--dump_eq' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_DUMP_EQ_WEIGHTS" ]]; then + if ! [[ "$VIASH_PAR_DUMP_EQ_WEIGHTS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--dump_eq_weights' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_ASSIGNED_FRAGS" ]]; then + if ! [[ "$VIASH_PAR_MIN_ASSIGNED_FRAGS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_assigned_frags' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_REDUCE_GC_MEMORY" ]]; then + if ! [[ "$VIASH_PAR_REDUCE_GC_MEMORY" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--reduce_GC_memory' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BIAS_SPEED_SAMP" ]]; then + if ! [[ "$VIASH_PAR_BIAS_SPEED_SAMP" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--bias_speed_samp' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FLD_MAX" ]]; then + if ! [[ "$VIASH_PAR_FLD_MAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--fld_max' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FLD_MEAN" ]]; then + if ! [[ "$VIASH_PAR_FLD_MEAN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--fld_mean' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FLD_SD" ]]; then + if ! [[ "$VIASH_PAR_FLD_SD" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--fld_SD' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FORGETTING_FACTOR" ]]; then + if ! [[ "$VIASH_PAR_FORGETTING_FACTOR" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--forgetting_factor' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_FORGETTING_FACTOR '>=' 0.500000001 | bc` -eq 1 ]]; then + ViashError '--forgetting_factor' has be more than or equal to 0.500000001. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_FORGETTING_FACTOR -v n2=0.500000001 'BEGIN { print (n1 >= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--forgetting_factor' has be more than or equal to 0.500000001. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--forgetting_factor' specifies a minimum value but the value was not verified as neither \'bc\' or \`awk\` are present on the system. + fi + if command -v bc &> /dev/null; then + if ! [[ `echo $VIASH_PAR_FORGETTING_FACTOR '<=' 1.0 | bc` -eq 1 ]]; then + ViashError '--forgetting_factor' has to be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + elif command -v awk &> /dev/null; then + if ! [[ `awk -v n1=$VIASH_PAR_FORGETTING_FACTOR -v n2=1.0 'BEGIN { print (n1 <= n2) ? "1" : "0" }'` -eq 1 ]]; then + ViashError '--forgetting_factor' has be less than or equal to 1.0. Use "--help" to get more information on the parameters. + exit 1 + fi + else + ViashWarning '--forgetting_factor' specifies a maximum value but the value was not verified as neither \'bc\' or \'awk\' are present on the system. + fi +fi +if [[ -n "$VIASH_PAR_INIT_UNIFORM" ]]; then + if ! [[ "$VIASH_PAR_INIT_UNIFORM" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--init_uniform' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_OCCS_PER_HIT" ]]; then + if ! [[ "$VIASH_PAR_MAX_OCCS_PER_HIT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_occs_per_hit' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAX_READ_OCC" ]]; then + if ! [[ "$VIASH_PAR_MAX_READ_OCC" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--max_read_occ' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_LENGTH_CORRECTION" ]]; then + if ! [[ "$VIASH_PAR_NO_LENGTH_CORRECTION" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_length_correction' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_EFFECTIVE_LENGTH_CORRECTION" ]]; then + if ! [[ "$VIASH_PAR_NO_EFFECTIVE_LENGTH_CORRECTION" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_effective_length_correction' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_SINGLE_FRAG_PROB" ]]; then + if ! [[ "$VIASH_PAR_NO_SINGLE_FRAG_PROB" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_single_frag_prob' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_FRAG_LENGTH_DIST" ]]; then + if ! [[ "$VIASH_PAR_NO_FRAG_LENGTH_DIST" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_frag_length_dist' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_BIAS_LENGTH_THRESHOLD" ]]; then + if ! [[ "$VIASH_PAR_NO_BIAS_LENGTH_THRESHOLD" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_bias_length_threshold' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NUM_BIAS_SAMPLES" ]]; then + if ! [[ "$VIASH_PAR_NUM_BIAS_SAMPLES" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--num_bias_samples' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NUM_AUX_MODEL_SAMPLES" ]]; then + if ! [[ "$VIASH_PAR_NUM_AUX_MODEL_SAMPLES" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--num_aux_model_samples' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES" ]]; then + if ! [[ "$VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--num_pre_aux_model_samples' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_USEEM" ]]; then + if ! [[ "$VIASH_PAR_USEEM" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--useEM' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_USEVBOPT" ]]; then + if ! [[ "$VIASH_PAR_USEVBOPT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--useVBOpt' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_RANGE_FACTORIZATION_BINS" ]]; then + if ! [[ "$VIASH_PAR_RANGE_FACTORIZATION_BINS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--range_factorization_bins' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NUM_GIBBS_SAMPLES" ]]; then + if ! [[ "$VIASH_PAR_NUM_GIBBS_SAMPLES" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--num_Gibbs_samples' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_GAMMA_DRAW" ]]; then + if ! [[ "$VIASH_PAR_NO_GAMMA_DRAW" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_Gamma_draw' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NUM_BOOTSTRAPS" ]]; then + if ! [[ "$VIASH_PAR_NUM_BOOTSTRAPS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--num_bootstraps' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BOOTSTRAP_REPROJECT" ]]; then + if ! [[ "$VIASH_PAR_BOOTSTRAP_REPROJECT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--bootstrap_reproject' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_THINNING_FACTOR" ]]; then + if ! [[ "$VIASH_PAR_THINNING_FACTOR" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--thinning_factor' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_QUIET" ]]; then + if ! [[ "$VIASH_PAR_QUIET" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--quiet' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PER_TRANSCRIPT_PRIOR" ]]; then + if ! [[ "$VIASH_PAR_PER_TRANSCRIPT_PRIOR" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--per_transcript_prior' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PER_NUCLEOTIDE_PRIOR" ]]; then + if ! [[ "$VIASH_PAR_PER_NUCLEOTIDE_PRIOR" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--per_nucleotide_prior' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SIG_DIGITS" ]]; then + if ! [[ "$VIASH_PAR_SIG_DIGITS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--sig_digits' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_VB_PRIOR" ]]; then + if ! [[ "$VIASH_PAR_VB_PRIOR" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--vb_prior' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WRITE_ORPHAN_LINKS" ]]; then + if ! [[ "$VIASH_PAR_WRITE_ORPHAN_LINKS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--write_orphan_links' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WRITE_UNMAPPED_NAMES" ]]; then + if ! [[ "$VIASH_PAR_WRITE_UNMAPPED_NAMES" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--write_unmapped_names' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_ERROR_MODEL" ]]; then + if ! [[ "$VIASH_PAR_NO_ERROR_MODEL" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_error_model' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NUM_ERROR_BINS" ]]; then + if ! [[ "$VIASH_PAR_NUM_ERROR_BINS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--num_error_bins' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SAMPLE_OUT" ]]; then + if ! [[ "$VIASH_PAR_SAMPLE_OUT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--sample_out' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SAMPLE_UNALIGNED" ]]; then + if ! [[ "$VIASH_PAR_SAMPLE_UNALIGNED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--sample_unaligned' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GENCODE" ]]; then + if ! [[ "$VIASH_PAR_GENCODE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--gencode' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT" ]]; then + if ! [[ "$VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--mapping_cache_memory_limit' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# check whether value is belongs to a set of choices +if [ ! -z "$VIASH_PAR_LIB_TYPE" ]; then + VIASH_PAR_LIB_TYPE_CHOICES=("A;U;SF;SR;IU;IS;ISF;ISR;OU;OS;OSF;OSR;MU;MS;MSF;MSR") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_LIB_TYPE_CHOICES[*]};" =~ ";$VIASH_PAR_LIB_TYPE;" ]]; then + ViashError '--lib_type' specified value of \'$VIASH_PAR_LIB_TYPE\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +if [ ! -z "$VIASH_PAR_HIT_FILTER_POLICY" ]; then + VIASH_PAR_HIT_FILTER_POLICY_CHOICES=("BEFORE;AFTER;BOTH;NONE") + IFS=';' + set -f + if ! [[ ";${VIASH_PAR_HIT_FILTER_POLICY_CHOICES[*]};" =~ ";$VIASH_PAR_HIT_FILTER_POLICY;" ]]; then + ViashError '--hit_filter_policy' specified value of \'$VIASH_PAR_HIT_FILTER_POLICY\' is not in the list of allowed values. Use "--help" to get more information on the parameters. + exit 1 + fi + set +f + unset IFS +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi +if [ ! -z "$VIASH_PAR_QUANT_RESULTS" ] && [ ! -d "$(dirname "$VIASH_PAR_QUANT_RESULTS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_QUANT_RESULTS")" +fi +if [ ! -z "$VIASH_PAR_MAPPING_SAM" ] && [ ! -d "$(dirname "$VIASH_PAR_MAPPING_SAM")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_MAPPING_SAM")" +fi +if [ ! -z "$VIASH_PAR_AUX_DIR" ] && [ ! -d "$(dirname "$VIASH_PAR_AUX_DIR")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_AUX_DIR")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INDEX" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INDEX")" ) + VIASH_PAR_INDEX=$(ViashDockerAutodetectMount "$VIASH_PAR_INDEX") +fi +if [ ! -z "$VIASH_PAR_UNMATED_READS" ]; then + VIASH_TEST_UNMATED_READS=() + IFS=';' + for var in $VIASH_PAR_UNMATED_READS; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_UNMATED_READS+=( "$var" ) + done + VIASH_PAR_UNMATED_READS=$(IFS=';' ; echo "${VIASH_TEST_UNMATED_READS[*]}") +fi +if [ ! -z "$VIASH_PAR_MATES1" ]; then + VIASH_TEST_MATES1=() + IFS=';' + for var in $VIASH_PAR_MATES1; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_MATES1+=( "$var" ) + done + VIASH_PAR_MATES1=$(IFS=';' ; echo "${VIASH_TEST_MATES1[*]}") +fi +if [ ! -z "$VIASH_PAR_MATES2" ]; then + VIASH_TEST_MATES2=() + IFS=';' + for var in $VIASH_PAR_MATES2; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_MATES2+=( "$var" ) + done + VIASH_PAR_MATES2=$(IFS=';' ; echo "${VIASH_TEST_MATES2[*]}") +fi +if [ ! -z "$VIASH_PAR_ALIGNMENTS" ]; then + VIASH_TEST_ALIGNMENTS=() + IFS=';' + for var in $VIASH_PAR_ALIGNMENTS; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_ALIGNMENTS+=( "$var" ) + done + VIASH_PAR_ALIGNMENTS=$(IFS=';' ; echo "${VIASH_TEST_ALIGNMENTS[*]}") +fi +if [ ! -z "$VIASH_PAR_EQCLASSES" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_EQCLASSES")" ) + VIASH_PAR_EQCLASSES=$(ViashDockerAutodetectMount "$VIASH_PAR_EQCLASSES") +fi +if [ ! -z "$VIASH_PAR_TARGETS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_TARGETS")" ) + VIASH_PAR_TARGETS=$(ViashDockerAutodetectMount "$VIASH_PAR_TARGETS") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_PAR_QUANT_RESULTS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_QUANT_RESULTS")" ) + VIASH_PAR_QUANT_RESULTS=$(ViashDockerAutodetectMount "$VIASH_PAR_QUANT_RESULTS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_QUANT_RESULTS" ) +fi +if [ ! -z "$VIASH_PAR_GENE_MAP" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_GENE_MAP")" ) + VIASH_PAR_GENE_MAP=$(ViashDockerAutodetectMount "$VIASH_PAR_GENE_MAP") +fi +if [ ! -z "$VIASH_PAR_AUX_TARGET_FILE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_AUX_TARGET_FILE")" ) + VIASH_PAR_AUX_TARGET_FILE=$(ViashDockerAutodetectMount "$VIASH_PAR_AUX_TARGET_FILE") +fi +if [ ! -z "$VIASH_PAR_MAPPING_SAM" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_MAPPING_SAM")" ) + VIASH_PAR_MAPPING_SAM=$(ViashDockerAutodetectMount "$VIASH_PAR_MAPPING_SAM") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_MAPPING_SAM" ) +fi +if [ ! -z "$VIASH_PAR_AUX_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_AUX_DIR")" ) + VIASH_PAR_AUX_DIR=$(ViashDockerAutodetectMount "$VIASH_PAR_AUX_DIR") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_AUX_DIR" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-salmon_quant-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +set -e + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_LIB_TYPE+x} ]; then echo "${VIASH_PAR_LIB_TYPE}" | sed "s#'#'\"'\"'#g;s#.*#par_lib_type='&'#" ; else echo "# par_lib_type="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX+x} ]; then echo "${VIASH_PAR_INDEX}" | sed "s#'#'\"'\"'#g;s#.*#par_index='&'#" ; else echo "# par_index="; fi ) +$( if [ ! -z ${VIASH_PAR_UNMATED_READS+x} ]; then echo "${VIASH_PAR_UNMATED_READS}" | sed "s#'#'\"'\"'#g;s#.*#par_unmated_reads='&'#" ; else echo "# par_unmated_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_MATES1+x} ]; then echo "${VIASH_PAR_MATES1}" | sed "s#'#'\"'\"'#g;s#.*#par_mates1='&'#" ; else echo "# par_mates1="; fi ) +$( if [ ! -z ${VIASH_PAR_MATES2+x} ]; then echo "${VIASH_PAR_MATES2}" | sed "s#'#'\"'\"'#g;s#.*#par_mates2='&'#" ; else echo "# par_mates2="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARD_ORPHANS+x} ]; then echo "${VIASH_PAR_DISCARD_ORPHANS}" | sed "s#'#'\"'\"'#g;s#.*#par_discard_orphans='&'#" ; else echo "# par_discard_orphans="; fi ) +$( if [ ! -z ${VIASH_PAR_ALIGNMENTS+x} ]; then echo "${VIASH_PAR_ALIGNMENTS}" | sed "s#'#'\"'\"'#g;s#.*#par_alignments='&'#" ; else echo "# par_alignments="; fi ) +$( if [ ! -z ${VIASH_PAR_EQCLASSES+x} ]; then echo "${VIASH_PAR_EQCLASSES}" | sed "s#'#'\"'\"'#g;s#.*#par_eqclasses='&'#" ; else echo "# par_eqclasses="; fi ) +$( if [ ! -z ${VIASH_PAR_TARGETS+x} ]; then echo "${VIASH_PAR_TARGETS}" | sed "s#'#'\"'\"'#g;s#.*#par_targets='&'#" ; else echo "# par_targets="; fi ) +$( if [ ! -z ${VIASH_PAR_ONT+x} ]; then echo "${VIASH_PAR_ONT}" | sed "s#'#'\"'\"'#g;s#.*#par_ont='&'#" ; else echo "# par_ont="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_QUANT_RESULTS+x} ]; then echo "${VIASH_PAR_QUANT_RESULTS}" | sed "s#'#'\"'\"'#g;s#.*#par_quant_results='&'#" ; else echo "# par_quant_results="; fi ) +$( if [ ! -z ${VIASH_PAR_SEQ_BIAS+x} ]; then echo "${VIASH_PAR_SEQ_BIAS}" | sed "s#'#'\"'\"'#g;s#.*#par_seq_bias='&'#" ; else echo "# par_seq_bias="; fi ) +$( if [ ! -z ${VIASH_PAR_GC_BIAS+x} ]; then echo "${VIASH_PAR_GC_BIAS}" | sed "s#'#'\"'\"'#g;s#.*#par_gc_bias='&'#" ; else echo "# par_gc_bias="; fi ) +$( if [ ! -z ${VIASH_PAR_POS_BIAS+x} ]; then echo "${VIASH_PAR_POS_BIAS}" | sed "s#'#'\"'\"'#g;s#.*#par_pos_bias='&'#" ; else echo "# par_pos_bias="; fi ) +$( if [ ! -z ${VIASH_PAR_INCOMPAT_PRIOR+x} ]; then echo "${VIASH_PAR_INCOMPAT_PRIOR}" | sed "s#'#'\"'\"'#g;s#.*#par_incompat_prior='&'#" ; else echo "# par_incompat_prior="; fi ) +$( if [ ! -z ${VIASH_PAR_GENE_MAP+x} ]; then echo "${VIASH_PAR_GENE_MAP}" | sed "s#'#'\"'\"'#g;s#.*#par_gene_map='&'#" ; else echo "# par_gene_map="; fi ) +$( if [ ! -z ${VIASH_PAR_AUX_TARGET_FILE+x} ]; then echo "${VIASH_PAR_AUX_TARGET_FILE}" | sed "s#'#'\"'\"'#g;s#.*#par_aux_target_file='&'#" ; else echo "# par_aux_target_file="; fi ) +$( if [ ! -z ${VIASH_PAR_META+x} ]; then echo "${VIASH_PAR_META}" | sed "s#'#'\"'\"'#g;s#.*#par_meta='&'#" ; else echo "# par_meta="; fi ) +$( if [ ! -z ${VIASH_PAR_SCORE_EXP+x} ]; then echo "${VIASH_PAR_SCORE_EXP}" | sed "s#'#'\"'\"'#g;s#.*#par_score_exp='&'#" ; else echo "# par_score_exp="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARD_ORPHANS_QUASI+x} ]; then echo "${VIASH_PAR_DISCARD_ORPHANS_QUASI}" | sed "s#'#'\"'\"'#g;s#.*#par_discard_orphans_quasi='&'#" ; else echo "# par_discard_orphans_quasi="; fi ) +$( if [ ! -z ${VIASH_PAR_CONSENSUS_SLACK+x} ]; then echo "${VIASH_PAR_CONSENSUS_SLACK}" | sed "s#'#'\"'\"'#g;s#.*#par_consensus_slack='&'#" ; else echo "# par_consensus_slack="; fi ) +$( if [ ! -z ${VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH+x} ]; then echo "${VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH}" | sed "s#'#'\"'\"'#g;s#.*#par_pre_merge_chain_sub_thresh='&'#" ; else echo "# par_pre_merge_chain_sub_thresh="; fi ) +$( if [ ! -z ${VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH+x} ]; then echo "${VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH}" | sed "s#'#'\"'\"'#g;s#.*#par_post_merge_chain_sub_thresh='&'#" ; else echo "# par_post_merge_chain_sub_thresh="; fi ) +$( if [ ! -z ${VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH+x} ]; then echo "${VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH}" | sed "s#'#'\"'\"'#g;s#.*#par_orphan_chain_sub_thresh='&'#" ; else echo "# par_orphan_chain_sub_thresh="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_SCORE_FRACTION+x} ]; then echo "${VIASH_PAR_MIN_SCORE_FRACTION}" | sed "s#'#'\"'\"'#g;s#.*#par_min_score_fraction='&'#" ; else echo "# par_min_score_fraction="; fi ) +$( if [ ! -z ${VIASH_PAR_MISMATCH_SEED_SKIP+x} ]; then echo "${VIASH_PAR_MISMATCH_SEED_SKIP}" | sed "s#'#'\"'\"'#g;s#.*#par_mismatch_seed_skip='&'#" ; else echo "# par_mismatch_seed_skip="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_CHAINING_HEURISTIC+x} ]; then echo "${VIASH_PAR_DISABLE_CHAINING_HEURISTIC}" | sed "s#'#'\"'\"'#g;s#.*#par_disable_chaining_heuristic='&'#" ; else echo "# par_disable_chaining_heuristic="; fi ) +$( if [ ! -z ${VIASH_PAR_DECOY_THRESHOLD+x} ]; then echo "${VIASH_PAR_DECOY_THRESHOLD}" | sed "s#'#'\"'\"'#g;s#.*#par_decoy_threshold='&'#" ; else echo "# par_decoy_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_MA+x} ]; then echo "${VIASH_PAR_MA}" | sed "s#'#'\"'\"'#g;s#.*#par_ma='&'#" ; else echo "# par_ma="; fi ) +$( if [ ! -z ${VIASH_PAR_MP+x} ]; then echo "${VIASH_PAR_MP}" | sed "s#'#'\"'\"'#g;s#.*#par_mp='&'#" ; else echo "# par_mp="; fi ) +$( if [ ! -z ${VIASH_PAR_GO+x} ]; then echo "${VIASH_PAR_GO}" | sed "s#'#'\"'\"'#g;s#.*#par_go='&'#" ; else echo "# par_go="; fi ) +$( if [ ! -z ${VIASH_PAR_GE+x} ]; then echo "${VIASH_PAR_GE}" | sed "s#'#'\"'\"'#g;s#.*#par_ge='&'#" ; else echo "# par_ge="; fi ) +$( if [ ! -z ${VIASH_PAR_BANDWIDTH+x} ]; then echo "${VIASH_PAR_BANDWIDTH}" | sed "s#'#'\"'\"'#g;s#.*#par_bandwidth='&'#" ; else echo "# par_bandwidth="; fi ) +$( if [ ! -z ${VIASH_PAR_ALLOW_DOVETAIL+x} ]; then echo "${VIASH_PAR_ALLOW_DOVETAIL}" | sed "s#'#'\"'\"'#g;s#.*#par_allow_dovetail='&'#" ; else echo "# par_allow_dovetail="; fi ) +$( if [ ! -z ${VIASH_PAR_RECOVER_ORPHANS+x} ]; then echo "${VIASH_PAR_RECOVER_ORPHANS}" | sed "s#'#'\"'\"'#g;s#.*#par_recover_orphans='&'#" ; else echo "# par_recover_orphans="; fi ) +$( if [ ! -z ${VIASH_PAR_MIMICBT2+x} ]; then echo "${VIASH_PAR_MIMICBT2}" | sed "s#'#'\"'\"'#g;s#.*#par_mimicBT2='&'#" ; else echo "# par_mimicBT2="; fi ) +$( if [ ! -z ${VIASH_PAR_MIMIC_STRICTBT2+x} ]; then echo "${VIASH_PAR_MIMIC_STRICTBT2}" | sed "s#'#'\"'\"'#g;s#.*#par_mimic_strictBT2='&'#" ; else echo "# par_mimic_strictBT2="; fi ) +$( if [ ! -z ${VIASH_PAR_SOFTCLIP+x} ]; then echo "${VIASH_PAR_SOFTCLIP}" | sed "s#'#'\"'\"'#g;s#.*#par_softclip='&'#" ; else echo "# par_softclip="; fi ) +$( if [ ! -z ${VIASH_PAR_SOFTCLIP_OVERHANGS+x} ]; then echo "${VIASH_PAR_SOFTCLIP_OVERHANGS}" | sed "s#'#'\"'\"'#g;s#.*#par_softclip_overhangs='&'#" ; else echo "# par_softclip_overhangs="; fi ) +$( if [ ! -z ${VIASH_PAR_FULL_LENGTH_ALIGNMENT+x} ]; then echo "${VIASH_PAR_FULL_LENGTH_ALIGNMENT}" | sed "s#'#'\"'\"'#g;s#.*#par_full_length_alignment='&'#" ; else echo "# par_full_length_alignment="; fi ) +$( if [ ! -z ${VIASH_PAR_HARD_FILTER+x} ]; then echo "${VIASH_PAR_HARD_FILTER}" | sed "s#'#'\"'\"'#g;s#.*#par_hard_filter='&'#" ; else echo "# par_hard_filter="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ALN_PROB+x} ]; then echo "${VIASH_PAR_MIN_ALN_PROB}" | sed "s#'#'\"'\"'#g;s#.*#par_min_aln_prob='&'#" ; else echo "# par_min_aln_prob="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_MAPPINGS+x} ]; then echo "${VIASH_PAR_WRITE_MAPPINGS}" | sed "s#'#'\"'\"'#g;s#.*#par_write_mappings='&'#" ; else echo "# par_write_mappings="; fi ) +$( if [ ! -z ${VIASH_PAR_MAPPING_SAM+x} ]; then echo "${VIASH_PAR_MAPPING_SAM}" | sed "s#'#'\"'\"'#g;s#.*#par_mapping_sam='&'#" ; else echo "# par_mapping_sam="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_QUALITIES+x} ]; then echo "${VIASH_PAR_WRITE_QUALITIES}" | sed "s#'#'\"'\"'#g;s#.*#par_write_qualities='&'#" ; else echo "# par_write_qualities="; fi ) +$( if [ ! -z ${VIASH_PAR_HIT_FILTER_POLICY+x} ]; then echo "${VIASH_PAR_HIT_FILTER_POLICY}" | sed "s#'#'\"'\"'#g;s#.*#par_hit_filter_policy='&'#" ; else echo "# par_hit_filter_policy="; fi ) +$( if [ ! -z ${VIASH_PAR_ALTERNATIVE_INIT_MODE+x} ]; then echo "${VIASH_PAR_ALTERNATIVE_INIT_MODE}" | sed "s#'#'\"'\"'#g;s#.*#par_alternative_init_mode='&'#" ; else echo "# par_alternative_init_mode="; fi ) +$( if [ ! -z ${VIASH_PAR_AUX_DIR+x} ]; then echo "${VIASH_PAR_AUX_DIR}" | sed "s#'#'\"'\"'#g;s#.*#par_aux_dir='&'#" ; else echo "# par_aux_dir="; fi ) +$( if [ ! -z ${VIASH_PAR_SKIP_QUANT+x} ]; then echo "${VIASH_PAR_SKIP_QUANT}" | sed "s#'#'\"'\"'#g;s#.*#par_skip_quant='&'#" ; else echo "# par_skip_quant="; fi ) +$( if [ ! -z ${VIASH_PAR_DUMP_EQ+x} ]; then echo "${VIASH_PAR_DUMP_EQ}" | sed "s#'#'\"'\"'#g;s#.*#par_dump_eq='&'#" ; else echo "# par_dump_eq="; fi ) +$( if [ ! -z ${VIASH_PAR_DUMP_EQ_WEIGHTS+x} ]; then echo "${VIASH_PAR_DUMP_EQ_WEIGHTS}" | sed "s#'#'\"'\"'#g;s#.*#par_dump_eq_weights='&'#" ; else echo "# par_dump_eq_weights="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ASSIGNED_FRAGS+x} ]; then echo "${VIASH_PAR_MIN_ASSIGNED_FRAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_min_assigned_frags='&'#" ; else echo "# par_min_assigned_frags="; fi ) +$( if [ ! -z ${VIASH_PAR_REDUCE_GC_MEMORY+x} ]; then echo "${VIASH_PAR_REDUCE_GC_MEMORY}" | sed "s#'#'\"'\"'#g;s#.*#par_reduce_GC_memory='&'#" ; else echo "# par_reduce_GC_memory="; fi ) +$( if [ ! -z ${VIASH_PAR_BIAS_SPEED_SAMP+x} ]; then echo "${VIASH_PAR_BIAS_SPEED_SAMP}" | sed "s#'#'\"'\"'#g;s#.*#par_bias_speed_samp='&'#" ; else echo "# par_bias_speed_samp="; fi ) +$( if [ ! -z ${VIASH_PAR_FLD_MAX+x} ]; then echo "${VIASH_PAR_FLD_MAX}" | sed "s#'#'\"'\"'#g;s#.*#par_fld_max='&'#" ; else echo "# par_fld_max="; fi ) +$( if [ ! -z ${VIASH_PAR_FLD_MEAN+x} ]; then echo "${VIASH_PAR_FLD_MEAN}" | sed "s#'#'\"'\"'#g;s#.*#par_fld_mean='&'#" ; else echo "# par_fld_mean="; fi ) +$( if [ ! -z ${VIASH_PAR_FLD_SD+x} ]; then echo "${VIASH_PAR_FLD_SD}" | sed "s#'#'\"'\"'#g;s#.*#par_fld_SD='&'#" ; else echo "# par_fld_SD="; fi ) +$( if [ ! -z ${VIASH_PAR_FORGETTING_FACTOR+x} ]; then echo "${VIASH_PAR_FORGETTING_FACTOR}" | sed "s#'#'\"'\"'#g;s#.*#par_forgetting_factor='&'#" ; else echo "# par_forgetting_factor="; fi ) +$( if [ ! -z ${VIASH_PAR_INIT_UNIFORM+x} ]; then echo "${VIASH_PAR_INIT_UNIFORM}" | sed "s#'#'\"'\"'#g;s#.*#par_init_uniform='&'#" ; else echo "# par_init_uniform="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_OCCS_PER_HIT+x} ]; then echo "${VIASH_PAR_MAX_OCCS_PER_HIT}" | sed "s#'#'\"'\"'#g;s#.*#par_max_occs_per_hit='&'#" ; else echo "# par_max_occs_per_hit="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_READ_OCC+x} ]; then echo "${VIASH_PAR_MAX_READ_OCC}" | sed "s#'#'\"'\"'#g;s#.*#par_max_read_occ='&'#" ; else echo "# par_max_read_occ="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_LENGTH_CORRECTION+x} ]; then echo "${VIASH_PAR_NO_LENGTH_CORRECTION}" | sed "s#'#'\"'\"'#g;s#.*#par_no_length_correction='&'#" ; else echo "# par_no_length_correction="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_EFFECTIVE_LENGTH_CORRECTION+x} ]; then echo "${VIASH_PAR_NO_EFFECTIVE_LENGTH_CORRECTION}" | sed "s#'#'\"'\"'#g;s#.*#par_no_effective_length_correction='&'#" ; else echo "# par_no_effective_length_correction="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_SINGLE_FRAG_PROB+x} ]; then echo "${VIASH_PAR_NO_SINGLE_FRAG_PROB}" | sed "s#'#'\"'\"'#g;s#.*#par_no_single_frag_prob='&'#" ; else echo "# par_no_single_frag_prob="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_FRAG_LENGTH_DIST+x} ]; then echo "${VIASH_PAR_NO_FRAG_LENGTH_DIST}" | sed "s#'#'\"'\"'#g;s#.*#par_no_frag_length_dist='&'#" ; else echo "# par_no_frag_length_dist="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_BIAS_LENGTH_THRESHOLD+x} ]; then echo "${VIASH_PAR_NO_BIAS_LENGTH_THRESHOLD}" | sed "s#'#'\"'\"'#g;s#.*#par_no_bias_length_threshold='&'#" ; else echo "# par_no_bias_length_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_BIAS_SAMPLES+x} ]; then echo "${VIASH_PAR_NUM_BIAS_SAMPLES}" | sed "s#'#'\"'\"'#g;s#.*#par_num_bias_samples='&'#" ; else echo "# par_num_bias_samples="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_AUX_MODEL_SAMPLES+x} ]; then echo "${VIASH_PAR_NUM_AUX_MODEL_SAMPLES}" | sed "s#'#'\"'\"'#g;s#.*#par_num_aux_model_samples='&'#" ; else echo "# par_num_aux_model_samples="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES+x} ]; then echo "${VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES}" | sed "s#'#'\"'\"'#g;s#.*#par_num_pre_aux_model_samples='&'#" ; else echo "# par_num_pre_aux_model_samples="; fi ) +$( if [ ! -z ${VIASH_PAR_USEEM+x} ]; then echo "${VIASH_PAR_USEEM}" | sed "s#'#'\"'\"'#g;s#.*#par_useEM='&'#" ; else echo "# par_useEM="; fi ) +$( if [ ! -z ${VIASH_PAR_USEVBOPT+x} ]; then echo "${VIASH_PAR_USEVBOPT}" | sed "s#'#'\"'\"'#g;s#.*#par_useVBOpt='&'#" ; else echo "# par_useVBOpt="; fi ) +$( if [ ! -z ${VIASH_PAR_RANGE_FACTORIZATION_BINS+x} ]; then echo "${VIASH_PAR_RANGE_FACTORIZATION_BINS}" | sed "s#'#'\"'\"'#g;s#.*#par_range_factorization_bins='&'#" ; else echo "# par_range_factorization_bins="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_GIBBS_SAMPLES+x} ]; then echo "${VIASH_PAR_NUM_GIBBS_SAMPLES}" | sed "s#'#'\"'\"'#g;s#.*#par_num_Gibbs_samples='&'#" ; else echo "# par_num_Gibbs_samples="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_GAMMA_DRAW+x} ]; then echo "${VIASH_PAR_NO_GAMMA_DRAW}" | sed "s#'#'\"'\"'#g;s#.*#par_no_Gamma_draw='&'#" ; else echo "# par_no_Gamma_draw="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_BOOTSTRAPS+x} ]; then echo "${VIASH_PAR_NUM_BOOTSTRAPS}" | sed "s#'#'\"'\"'#g;s#.*#par_num_bootstraps='&'#" ; else echo "# par_num_bootstraps="; fi ) +$( if [ ! -z ${VIASH_PAR_BOOTSTRAP_REPROJECT+x} ]; then echo "${VIASH_PAR_BOOTSTRAP_REPROJECT}" | sed "s#'#'\"'\"'#g;s#.*#par_bootstrap_reproject='&'#" ; else echo "# par_bootstrap_reproject="; fi ) +$( if [ ! -z ${VIASH_PAR_THINNING_FACTOR+x} ]; then echo "${VIASH_PAR_THINNING_FACTOR}" | sed "s#'#'\"'\"'#g;s#.*#par_thinning_factor='&'#" ; else echo "# par_thinning_factor="; fi ) +$( if [ ! -z ${VIASH_PAR_QUIET+x} ]; then echo "${VIASH_PAR_QUIET}" | sed "s#'#'\"'\"'#g;s#.*#par_quiet='&'#" ; else echo "# par_quiet="; fi ) +$( if [ ! -z ${VIASH_PAR_PER_TRANSCRIPT_PRIOR+x} ]; then echo "${VIASH_PAR_PER_TRANSCRIPT_PRIOR}" | sed "s#'#'\"'\"'#g;s#.*#par_per_transcript_prior='&'#" ; else echo "# par_per_transcript_prior="; fi ) +$( if [ ! -z ${VIASH_PAR_PER_NUCLEOTIDE_PRIOR+x} ]; then echo "${VIASH_PAR_PER_NUCLEOTIDE_PRIOR}" | sed "s#'#'\"'\"'#g;s#.*#par_per_nucleotide_prior='&'#" ; else echo "# par_per_nucleotide_prior="; fi ) +$( if [ ! -z ${VIASH_PAR_SIG_DIGITS+x} ]; then echo "${VIASH_PAR_SIG_DIGITS}" | sed "s#'#'\"'\"'#g;s#.*#par_sig_digits='&'#" ; else echo "# par_sig_digits="; fi ) +$( if [ ! -z ${VIASH_PAR_VB_PRIOR+x} ]; then echo "${VIASH_PAR_VB_PRIOR}" | sed "s#'#'\"'\"'#g;s#.*#par_vb_prior='&'#" ; else echo "# par_vb_prior="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_ORPHAN_LINKS+x} ]; then echo "${VIASH_PAR_WRITE_ORPHAN_LINKS}" | sed "s#'#'\"'\"'#g;s#.*#par_write_orphan_links='&'#" ; else echo "# par_write_orphan_links="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_UNMAPPED_NAMES+x} ]; then echo "${VIASH_PAR_WRITE_UNMAPPED_NAMES}" | sed "s#'#'\"'\"'#g;s#.*#par_write_unmapped_names='&'#" ; else echo "# par_write_unmapped_names="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_ERROR_MODEL+x} ]; then echo "${VIASH_PAR_NO_ERROR_MODEL}" | sed "s#'#'\"'\"'#g;s#.*#par_no_error_model='&'#" ; else echo "# par_no_error_model="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_ERROR_BINS+x} ]; then echo "${VIASH_PAR_NUM_ERROR_BINS}" | sed "s#'#'\"'\"'#g;s#.*#par_num_error_bins='&'#" ; else echo "# par_num_error_bins="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_OUT+x} ]; then echo "${VIASH_PAR_SAMPLE_OUT}" | sed "s#'#'\"'\"'#g;s#.*#par_sample_out='&'#" ; else echo "# par_sample_out="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_UNALIGNED+x} ]; then echo "${VIASH_PAR_SAMPLE_UNALIGNED}" | sed "s#'#'\"'\"'#g;s#.*#par_sample_unaligned='&'#" ; else echo "# par_sample_unaligned="; fi ) +$( if [ ! -z ${VIASH_PAR_GENCODE+x} ]; then echo "${VIASH_PAR_GENCODE}" | sed "s#'#'\"'\"'#g;s#.*#par_gencode='&'#" ; else echo "# par_gencode="; fi ) +$( if [ ! -z ${VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT+x} ]; then echo "${VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT}" | sed "s#'#'\"'\"'#g;s#.*#par_mapping_cache_memory_limit='&'#" ; else echo "# par_mapping_cache_memory_limit="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +[[ "\$par_discard_orphans" == "false" ]] && unset par_discard_orphans +[[ "\$par_ont" == "false" ]] && unset par_ont +[[ "\$par_seq_bias" == "false" ]] && unset par_seq_bias +[[ "\$par_gc_bias" == "false" ]] && unset par_gc_bias +[[ "\$par_pos_bias" == "false" ]] && unset par_pos_bias +[[ "\$par_meta" == "false" ]] && unset par_meta +[[ "\$par_discard_orphans_quasi" == "false" ]] && unset par_discard_orphans_quasi +[[ "\$par_disable_chaining_heuristic" == "false" ]] && unset par_disable_chaining_heuristic +[[ "\$par_allow_dovetail" == "false" ]] && unset par_allow_dovetail +[[ "\$par_recover_orphans" == "false" ]] && unset par_recover_orphans +[[ "\$par_mimicBT2" == "false" ]] && unset par_mimicBT2 +[[ "\$par_mimic_strictBT2" == "false" ]] && unset par_mimic_strictBT2 +[[ "\$par_softclip" == "false" ]] && unset par_softclip +[[ "\$par_softclip_overhangs" == "false" ]] && unset par_softclip_overhangs +[[ "\$par_full_length_alignment" == "false" ]] && unset par_full_length_alignment +[[ "\$par_hard_filter" == "false" ]] && unset par_hard_filter +[[ "\$par_write_mappings" == "false" ]] && unset par_write_mappings +[[ "\$par_write_qualities" == "false" ]] && unset par_write_qualities +[[ "\$par_alternative_init_mode" == "false" ]] && unset par_alternative_init_mode +[[ "\$par_skip_quant" == "false" ]] && unset par_skip_quant +[[ "\$par_dump_eq" == "false" ]] && unset par_dump_eq +[[ "\$par_dump_eq_weights" == "false" ]] && unset par_dump_eq_weights +[[ "\$par_reduce_GC_memory" == "false" ]] && unset par_reduce_GC_memory +[[ "\$par_init_uniform" == "false" ]] && unset par_init_uniform +[[ "\$par_no_length_correction" == "false" ]] && unset par_no_length_correction +[[ "\$par_no_effective_length_correction" == "false" ]] && unset par_no_effective_length_correction +[[ "\$par_no_single_frag_prob" == "false" ]] && unset par_no_single_frag_prob +[[ "\$par_no_frag_length_dist" == "false" ]] && unset par_no_frag_length_dist +[[ "\$par_no_bias_length_threshold" == "false" ]] && unset par_no_bias_length_threshold +[[ "\$par_useEM" == "false" ]] && unset par_useEM +[[ "\$par_useVBOpt" == "false" ]] && unset par_useVBOpt +[[ "\$par_no_Gamma_draw" == "false" ]] && unset par_no_Gamma_draw +[[ "\$par_bootstrap_reproject" == "false" ]] && unset par_bootstrap_reproject +[[ "\$par_quiet" == "false" ]] && unset par_quiet +[[ "\$par_per_transcript_prior" == "false" ]] && unset par_per_transcript_prior +[[ "\$par_per_nucleotide_prior" == "false" ]] && unset par_per_nucleotide_prior +[[ "\$par_write_orphan_links" == "false" ]] && unset par_write_orphan_links +[[ "\$par_write_unmapped_names" == "false" ]] && unset par_write_unmapped_names +[[ "\$par_no_error_model" == "false" ]] && unset par_no_error_model +[[ "\$par_sample_out" == "false" ]] && unset par_sample_out +[[ "\$par_sample_unaligned" == "false" ]] && unset par_sample_unaligned +[[ "\$par_gencode" == "false" ]] && unset par_gencode + +IFS=";" read -ra unmated_reads <<< \$par_unmated_reads +IFS=";" read -ra mates1 <<< \$par_mates1 +IFS=";" read -ra mates2 <<< \$par_mates2 +IFS=";" read -ra alignment <<< \$par_alignments + +salmon quant \\ + \${par_lib_type:+-l "\${par_lib_type}"} \\ + \${par_index:+-i "\${par_index}"} \\ + \${par_unmated_reads:+-r \${unmated_reads[*]}} \\ + \${par_mates1:+-1 \${mates1[*]}} \\ + \${par_mates2:+-2 \${mates2[*]}} \\ + \${par_alignments:+-a \${alignment[*]}} \\ + \${par_discard_orphans:+--discardOrphans} \\ + \${par_eqclasses:+-e "\${par_eqclasses}"} \\ + \${par_targets:+-t "\${par_targets}"} \\ + \${par_ont:+--ont} \\ + \${par_output:+-o "\${par_output}"} \\ + \${par_seq_bias:+--seqBias} \\ + \${par_gc_bias:+--gcBias} \\ + \${par_pos_bias:+--posBias} \\ + \${meta_cpus:+-p "\${meta_cpus}"} \\ + \${par_incompat_prior:+--incompatPrior "\${par_incompat_prior}"} \\ + \${par_gene_map:+-g "\${par_gene_map}"} \\ + \${par_aux_target_file:+--auxTargetFile "\${par_aux_target_file}"} \\ + \${par_meta:+--meta} \\ + \${par_score_exp:+--scoreExp "\${par_score_exp}"} \\ + \${par_discard_orphans_quasi:+--discardOrphansQuasi} \\ + \${par_consensus_slack:+--consensusSlack "\${par_consensus_slack}"} \\ + \${par_pre_merge_chain_sub_thresh:+--preMergeChainSubThresh "\${par_pre_merge_chain_sub_thresh}"} \\ + \${par_post_merge_chain_sub_thresh:+--postMergeChainSubThresh "\${par_post_merge_chain_sub_thresh}"} \\ + \${par_orphan_chain_sub_thresh:+--orphanChainSubThresh "\${par_orphan_chain_sub_thresh}"} \\ + \${par_min_score_fraction:+--minScoreFraction "\${par_min_score_fraction}"} \\ + \${par_mismatch_seed_skip:+--mismatchSeedSkip "\${par_mismatch_seed_skip}"} \\ + \${par_disable_chaining_heuristic:+--disableChainingHeuristic} \\ + \${par_decoy_threshold:+--decoyThreshold "\${par_decoy_threshold}"} \\ + \${par_ma:+--ma "\${par_ma}"} \\ + \${par_mp:+--mp "\${par_mp}"} \\ + \${par_go:+--go "\${par_go}"} \\ + \${par_ge:+--ge "\${par_ge}"} \\ + \${par_bandwidth:+--bandwidth "\${par_bandwidth}"} \\ + \${par_allow_dovetail:+--allowDovetail} \\ + \${par_recover_orphans:+--recoverOrphans} \\ + \${par_mimicBT2:+--mimicBT2} \\ + \${par_mimic_strictBT2:+--mimicStrictBT2} \\ + \${par_softclip:+--softclip} \\ + \${par_softclip_overhangs:+--softclipOverhangs} \\ + \${par_full_length_alignment:+--fullLengthAlignment} \\ + \${par_hard_filter:+--hardFilter} \\ + \${par_min_aln_prob:+--minAlnProb "\${par_min_aln_prob}"} \\ + \${par_write_mappings:+--write_mappings="\${par_mappings_sam}"} \\ + \${par_write_qualities:+--writeQualities} \\ + \${par_hit_filter_policy:+--hitFilterPolicy "\${par_hit_filter_policy}"} \\ + \${par_alternative_init_mode:+--alternativeInitMode} \\ + \${par_aux_dir:+--auxDir "\${par_aux_dir}"} \\ + \${par_skip_quant:+--skipQuant} \\ + \${par_dump_eq:+--dumpEq} \\ + \${par_dump_eq_weights:+-d "\${par_dump_eq_weights}"} \\ + \${par_min_assigned_frags:+--minAssignedFrags "\${par_min_assigned_frags}"} \\ + \${par_reduce_GC_memory:+--reduceGCMemory} \\ + \${par_bias_speed_samp:+--biasSpeedSamp "\${par_bias_speed_samp}"} \\ + \${par_fld_max:+--fldMax "\${par_fld_max}"} \\ + \${par_fld_mean:+--fldMean "\${par_fld_mean}"} \\ + \${par_fld_SD:+--fldSD "\${par_fld_SD}"} \\ + \${par_forgetting_factor:+-f "\${par_forgetting_factor}"} \\ + \${par_init_uniform:+--initUniform} \\ + \${par_max_occs_per_hit:+--maxOccsPerHit "\${par_max_occs_per_hit}"} \\ + \${par_max_read_occ:+-w "\${par_max_read_occ}"} \\ + \${par_no_length_correction:+--noLengthCorrection} \\ + \${par_no_effective_length_correction:+--noEffectiveLengthCorrection} \\ + \${par_no_single_frag_prob:+--noSingleFragProb} \\ + \${par_no_frag_length_dist:+--noFragLengthDist} \\ + \${par_no_bias_length_threshold:+--noBiasLengthThreshold} \\ + \${par_num_bias_samples:+--numBiasSamples "\${par_num_bias_samples}"} \\ + \${par_num_aux_model_samples:+--numAuxModelSamples "\${par_num_aux_model_samples}"} \\ + \${par_num_pre_aux_model_samples:+--numPreAuxModelSamples "\${par_num_pre_aux_model_samples}"} \\ + \${par_useEM:+--useEM} \\ + \${par_useVBOpt:+--useVBOpt} \\ + \${par_range_factorization_bins:+--rangeFactorizationBins "\${par_range_factorization_bins}"} \\ + \${par_num_Gibbs_samples:+--numGibbsSamples "\${par_num_Gibbs_samples}"} \\ + \${par_no_Gamma_draw:+--noGammaDraw} \\ + \${par_num_bootstraps:+--numBootstraps "\${par_num_bootstraps}"} \\ + \${par_bootstrap_reproject:+--bootstrapReproject} \\ + \${par_thinning_factor:+--thinningFactor "\${par_thinning_factor}"} \\ + \${par_quiet:+--quiet} \\ + \${par_per_transcript_prior:+--perTranscriptPrior} \\ + \${par_per_nucleotide_prior:+--perNucleotidePrior} \\ + \${par_sig_digits:+--sigDigits "\${par_sig_digits}"} \\ + \${par_vb_prior:+--vbPrior "\${par_vb_prior}"} \\ + \${par_write_orphan_links:+--writeOrphanLinks} \\ + \${par_write_unmapped_names:+--writeUnmappedNames} \\ + \${par_no_error_model:+--noErrorModel} \\ + \${par_num_error_bins:+--numErrorBins "\${par_num_error_bins}"} \\ + \${par_sample_out:+--sampleOut} \\ + \${par_sample_unaligned:+--sampleUnaligned} \\ + \${par_gencode:+--gencode} \\ + \${par_mapping_cache_memory_limit:+--mappingCacheMemoryLimit "\${par_mapping_cache_memory_limit}"} + +if [ -f "\$par_output/quant.sf" ]; then + mv \$par_output/quant.sf \$par_quant_results +else + echo "Quantification file not generated!" +fi +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INDEX" ]; then + VIASH_PAR_INDEX=$(ViashDockerStripAutomount "$VIASH_PAR_INDEX") + fi + if [ ! -z "$VIASH_PAR_UNMATED_READS" ]; then + unset VIASH_TEST_UNMATED_READS + IFS=';' + for var in $VIASH_PAR_UNMATED_READS; do + unset IFS + if [ -z "$VIASH_TEST_UNMATED_READS" ]; then + VIASH_TEST_UNMATED_READS="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_UNMATED_READS="$VIASH_TEST_UNMATED_READS;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_UNMATED_READS="$VIASH_TEST_UNMATED_READS" + fi + if [ ! -z "$VIASH_PAR_MATES1" ]; then + unset VIASH_TEST_MATES1 + IFS=';' + for var in $VIASH_PAR_MATES1; do + unset IFS + if [ -z "$VIASH_TEST_MATES1" ]; then + VIASH_TEST_MATES1="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_MATES1="$VIASH_TEST_MATES1;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_MATES1="$VIASH_TEST_MATES1" + fi + if [ ! -z "$VIASH_PAR_MATES2" ]; then + unset VIASH_TEST_MATES2 + IFS=';' + for var in $VIASH_PAR_MATES2; do + unset IFS + if [ -z "$VIASH_TEST_MATES2" ]; then + VIASH_TEST_MATES2="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_MATES2="$VIASH_TEST_MATES2;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_MATES2="$VIASH_TEST_MATES2" + fi + if [ ! -z "$VIASH_PAR_ALIGNMENTS" ]; then + unset VIASH_TEST_ALIGNMENTS + IFS=';' + for var in $VIASH_PAR_ALIGNMENTS; do + unset IFS + if [ -z "$VIASH_TEST_ALIGNMENTS" ]; then + VIASH_TEST_ALIGNMENTS="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_ALIGNMENTS="$VIASH_TEST_ALIGNMENTS;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_ALIGNMENTS="$VIASH_TEST_ALIGNMENTS" + fi + if [ ! -z "$VIASH_PAR_EQCLASSES" ]; then + VIASH_PAR_EQCLASSES=$(ViashDockerStripAutomount "$VIASH_PAR_EQCLASSES") + fi + if [ ! -z "$VIASH_PAR_TARGETS" ]; then + VIASH_PAR_TARGETS=$(ViashDockerStripAutomount "$VIASH_PAR_TARGETS") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_PAR_QUANT_RESULTS" ]; then + VIASH_PAR_QUANT_RESULTS=$(ViashDockerStripAutomount "$VIASH_PAR_QUANT_RESULTS") + fi + if [ ! -z "$VIASH_PAR_GENE_MAP" ]; then + VIASH_PAR_GENE_MAP=$(ViashDockerStripAutomount "$VIASH_PAR_GENE_MAP") + fi + if [ ! -z "$VIASH_PAR_AUX_TARGET_FILE" ]; then + VIASH_PAR_AUX_TARGET_FILE=$(ViashDockerStripAutomount "$VIASH_PAR_AUX_TARGET_FILE") + fi + if [ ! -z "$VIASH_PAR_MAPPING_SAM" ]; then + VIASH_PAR_MAPPING_SAM=$(ViashDockerStripAutomount "$VIASH_PAR_MAPPING_SAM") + fi + if [ ! -z "$VIASH_PAR_AUX_DIR" ]; then + VIASH_PAR_AUX_DIR=$(ViashDockerStripAutomount "$VIASH_PAR_AUX_DIR") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_QUANT_RESULTS" ] && [ ! -e "$VIASH_PAR_QUANT_RESULTS" ]; then + ViashError "Output file '$VIASH_PAR_QUANT_RESULTS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_MAPPING_SAM" ] && [ ! -e "$VIASH_PAR_MAPPING_SAM" ]; then + ViashError "Output file '$VIASH_PAR_MAPPING_SAM' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_AUX_DIR" ] && [ ! -e "$VIASH_PAR_AUX_DIR" ]; then + ViashError "Output file '$VIASH_PAR_AUX_DIR' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/samtools/samtools_collate/.config.vsh.yaml b/target/executable/samtools/samtools_collate/.config.vsh.yaml new file mode 100644 index 00000000..84b9b48f --- /dev/null +++ b/target/executable/samtools/samtools_collate/.config.vsh.yaml @@ -0,0 +1,276 @@ +name: "samtools_collate" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "The input BAM file." + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reference" + description: "Reference sequence FASTA FILE." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "The output filename." + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Options" + arguments: + - type: "boolean_true" + name: "--uncompressed" + alternatives: + - "-u" + description: "Output uncompressed BAM." + info: null + direction: "input" + - type: "boolean_true" + name: "--fast" + alternatives: + - "-f" + description: "Fast mode, only primary alignments." + info: null + direction: "input" + - type: "integer" + name: "--working_reads" + alternatives: + - "-r" + description: "Working reads stored (for use with -f)." + info: null + default: + - 10000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--compression" + alternatives: + - "-l" + description: "Compression level." + info: null + default: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--nb_tmp_files" + alternatives: + - "-n" + description: "Number of temporary files." + info: null + default: + - 64 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--tmp_prefix" + alternatives: + - "-T" + description: "Write temporary files to PREFIX.nnnn.bam." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_pg" + description: "Do not add a PG line." + info: null + direction: "input" + - type: "string" + name: "--input_fmt_option" + description: "Specify a single input file format option in the form of OPTION\ + \ or OPTION=VALUE." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt" + description: "Specify output format (SAM, BAM, CRAM)." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt_option" + description: "Specify a single output file format option in the form of OPTION\ + \ or OPTION=VALUE." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Shuffles and groups reads in SAM/BAM/CRAM files together by their names." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "collate" +- "counts" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-icollate.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_collate/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/samtools/samtools_collate" + executable: "target/executable/samtools/samtools_collate/samtools_collate" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/samtools/samtools_collate/samtools_collate b/target/executable/samtools/samtools_collate/samtools_collate new file mode 100755 index 00000000..b116c8ca --- /dev/null +++ b/target/executable/samtools/samtools_collate/samtools_collate @@ -0,0 +1,1338 @@ +#!/usr/bin/env bash + +# samtools_collate main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="samtools_collate" +VIASH_META_FUNCTIONALITY_NAME="samtools_collate" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "samtools_collate main" + echo "" + echo "Shuffles and groups reads in SAM/BAM/CRAM files together by their names." + echo "" + echo "Inputs:" + echo " --input" + echo " type: file, required parameter, file must exist" + echo " The input BAM file." + echo "" + echo " --reference" + echo " type: file, file must exist" + echo " Reference sequence FASTA FILE." + echo "" + echo "Outputs:" + echo " -o, --output" + echo " type: file, required parameter, output, file must exist" + echo " The output filename." + echo "" + echo "Options:" + echo " -u, --uncompressed" + echo " type: boolean_true" + echo " Output uncompressed BAM." + echo "" + echo " -f, --fast" + echo " type: boolean_true" + echo " Fast mode, only primary alignments." + echo "" + echo " -r, --working_reads" + echo " type: integer" + echo " default: 10000" + echo " Working reads stored (for use with -f)." + echo "" + echo " -l, --compression" + echo " type: integer" + echo " default: 1" + echo " Compression level." + echo "" + echo " -n, --nb_tmp_files" + echo " type: integer" + echo " default: 64" + echo " Number of temporary files." + echo "" + echo " -T, --tmp_prefix" + echo " type: string" + echo " Write temporary files to PREFIX.nnnn.bam." + echo "" + echo " --no_pg" + echo " type: boolean_true" + echo " Do not add a PG line." + echo "" + echo " --input_fmt_option" + echo " type: string" + echo " Specify a single input file format option in the form of OPTION or" + echo " OPTION=VALUE." + echo "" + echo " --output_fmt" + echo " type: string" + echo " Specify output format (SAM, BAM, CRAM)." + echo "" + echo " --output_fmt_option" + echo " type: string" + echo " Specify a single output file format option in the form of OPTION or" + echo " OPTION=VALUE." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 +ENTRYPOINT [] +RUN samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ +sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component samtools samtools_collate" +LABEL org.opencontainers.image.created="2024-06-24T08:36:40Z" +LABEL org.opencontainers.image.source="https://github.com/samtools/samtools" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "samtools_collate main" + exit + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --reference) + [ -n "$VIASH_PAR_REFERENCE" ] && ViashError Bad arguments for option \'--reference\': \'$VIASH_PAR_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REFERENCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --reference. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --reference=*) + [ -n "$VIASH_PAR_REFERENCE" ] && ViashError Bad arguments for option \'--reference=*\': \'$VIASH_PAR_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REFERENCE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --uncompressed) + [ -n "$VIASH_PAR_UNCOMPRESSED" ] && ViashError Bad arguments for option \'--uncompressed\': \'$VIASH_PAR_UNCOMPRESSED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNCOMPRESSED=true + shift 1 + ;; + -u) + [ -n "$VIASH_PAR_UNCOMPRESSED" ] && ViashError Bad arguments for option \'-u\': \'$VIASH_PAR_UNCOMPRESSED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNCOMPRESSED=true + shift 1 + ;; + --fast) + [ -n "$VIASH_PAR_FAST" ] && ViashError Bad arguments for option \'--fast\': \'$VIASH_PAR_FAST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FAST=true + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_FAST" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_FAST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FAST=true + shift 1 + ;; + --working_reads) + [ -n "$VIASH_PAR_WORKING_READS" ] && ViashError Bad arguments for option \'--working_reads\': \'$VIASH_PAR_WORKING_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WORKING_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --working_reads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --working_reads=*) + [ -n "$VIASH_PAR_WORKING_READS" ] && ViashError Bad arguments for option \'--working_reads=*\': \'$VIASH_PAR_WORKING_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WORKING_READS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -r) + [ -n "$VIASH_PAR_WORKING_READS" ] && ViashError Bad arguments for option \'-r\': \'$VIASH_PAR_WORKING_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WORKING_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -r. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --compression) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'--compression\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --compression. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --compression=*) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'--compression=*\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION=$(ViashRemoveFlags "$1") + shift 1 + ;; + -l) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'-l\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -l. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --nb_tmp_files) + [ -n "$VIASH_PAR_NB_TMP_FILES" ] && ViashError Bad arguments for option \'--nb_tmp_files\': \'$VIASH_PAR_NB_TMP_FILES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NB_TMP_FILES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --nb_tmp_files. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --nb_tmp_files=*) + [ -n "$VIASH_PAR_NB_TMP_FILES" ] && ViashError Bad arguments for option \'--nb_tmp_files=*\': \'$VIASH_PAR_NB_TMP_FILES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NB_TMP_FILES=$(ViashRemoveFlags "$1") + shift 1 + ;; + -n) + [ -n "$VIASH_PAR_NB_TMP_FILES" ] && ViashError Bad arguments for option \'-n\': \'$VIASH_PAR_NB_TMP_FILES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NB_TMP_FILES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -n. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tmp_prefix) + [ -n "$VIASH_PAR_TMP_PREFIX" ] && ViashError Bad arguments for option \'--tmp_prefix\': \'$VIASH_PAR_TMP_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TMP_PREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --tmp_prefix. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tmp_prefix=*) + [ -n "$VIASH_PAR_TMP_PREFIX" ] && ViashError Bad arguments for option \'--tmp_prefix=*\': \'$VIASH_PAR_TMP_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TMP_PREFIX=$(ViashRemoveFlags "$1") + shift 1 + ;; + -T) + [ -n "$VIASH_PAR_TMP_PREFIX" ] && ViashError Bad arguments for option \'-T\': \'$VIASH_PAR_TMP_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TMP_PREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -T. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --no_pg) + [ -n "$VIASH_PAR_NO_PG" ] && ViashError Bad arguments for option \'--no_pg\': \'$VIASH_PAR_NO_PG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_PG=true + shift 1 + ;; + --input_fmt_option) + [ -n "$VIASH_PAR_INPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--input_fmt_option\': \'$VIASH_PAR_INPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_FMT_OPTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input_fmt_option. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input_fmt_option=*) + [ -n "$VIASH_PAR_INPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--input_fmt_option=*\': \'$VIASH_PAR_INPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_FMT_OPTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output_fmt) + [ -n "$VIASH_PAR_OUTPUT_FMT" ] && ViashError Bad arguments for option \'--output_fmt\': \'$VIASH_PAR_OUTPUT_FMT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_fmt. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_fmt=*) + [ -n "$VIASH_PAR_OUTPUT_FMT" ] && ViashError Bad arguments for option \'--output_fmt=*\': \'$VIASH_PAR_OUTPUT_FMT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output_fmt_option) + [ -n "$VIASH_PAR_OUTPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--output_fmt_option\': \'$VIASH_PAR_OUTPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT_OPTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_fmt_option. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_fmt_option=*) + [ -n "$VIASH_PAR_OUTPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--output_fmt_option=*\': \'$VIASH_PAR_OUTPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT_OPTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/samtools/samtools_collate:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_UNCOMPRESSED+x} ]; then + VIASH_PAR_UNCOMPRESSED="false" +fi +if [ -z ${VIASH_PAR_FAST+x} ]; then + VIASH_PAR_FAST="false" +fi +if [ -z ${VIASH_PAR_WORKING_READS+x} ]; then + VIASH_PAR_WORKING_READS="10000" +fi +if [ -z ${VIASH_PAR_COMPRESSION+x} ]; then + VIASH_PAR_COMPRESSION="1" +fi +if [ -z ${VIASH_PAR_NB_TMP_FILES+x} ]; then + VIASH_PAR_NB_TMP_FILES="64" +fi +if [ -z ${VIASH_PAR_NO_PG+x} ]; then + VIASH_PAR_NO_PG="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REFERENCE" ] && [ ! -e "$VIASH_PAR_REFERENCE" ]; then + ViashError "Input file '$VIASH_PAR_REFERENCE' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_UNCOMPRESSED" ]]; then + if ! [[ "$VIASH_PAR_UNCOMPRESSED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--uncompressed' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FAST" ]]; then + if ! [[ "$VIASH_PAR_FAST" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--fast' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WORKING_READS" ]]; then + if ! [[ "$VIASH_PAR_WORKING_READS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--working_reads' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_COMPRESSION" ]]; then + if ! [[ "$VIASH_PAR_COMPRESSION" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--compression' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NB_TMP_FILES" ]]; then + if ! [[ "$VIASH_PAR_NB_TMP_FILES" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--nb_tmp_files' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_PG" ]]; then + if ! [[ "$VIASH_PAR_NO_PG" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_pg' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_REFERENCE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REFERENCE")" ) + VIASH_PAR_REFERENCE=$(ViashDockerAutodetectMount "$VIASH_PAR_REFERENCE") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-samtools_collate-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_REFERENCE+x} ]; then echo "${VIASH_PAR_REFERENCE}" | sed "s#'#'\"'\"'#g;s#.*#par_reference='&'#" ; else echo "# par_reference="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_UNCOMPRESSED+x} ]; then echo "${VIASH_PAR_UNCOMPRESSED}" | sed "s#'#'\"'\"'#g;s#.*#par_uncompressed='&'#" ; else echo "# par_uncompressed="; fi ) +$( if [ ! -z ${VIASH_PAR_FAST+x} ]; then echo "${VIASH_PAR_FAST}" | sed "s#'#'\"'\"'#g;s#.*#par_fast='&'#" ; else echo "# par_fast="; fi ) +$( if [ ! -z ${VIASH_PAR_WORKING_READS+x} ]; then echo "${VIASH_PAR_WORKING_READS}" | sed "s#'#'\"'\"'#g;s#.*#par_working_reads='&'#" ; else echo "# par_working_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPRESSION+x} ]; then echo "${VIASH_PAR_COMPRESSION}" | sed "s#'#'\"'\"'#g;s#.*#par_compression='&'#" ; else echo "# par_compression="; fi ) +$( if [ ! -z ${VIASH_PAR_NB_TMP_FILES+x} ]; then echo "${VIASH_PAR_NB_TMP_FILES}" | sed "s#'#'\"'\"'#g;s#.*#par_nb_tmp_files='&'#" ; else echo "# par_nb_tmp_files="; fi ) +$( if [ ! -z ${VIASH_PAR_TMP_PREFIX+x} ]; then echo "${VIASH_PAR_TMP_PREFIX}" | sed "s#'#'\"'\"'#g;s#.*#par_tmp_prefix='&'#" ; else echo "# par_tmp_prefix="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_PG+x} ]; then echo "${VIASH_PAR_NO_PG}" | sed "s#'#'\"'\"'#g;s#.*#par_no_pg='&'#" ; else echo "# par_no_pg="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_INPUT_FMT_OPTION}" | sed "s#'#'\"'\"'#g;s#.*#par_input_fmt_option='&'#" ; else echo "# par_input_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT}" | sed "s#'#'\"'\"'#g;s#.*#par_output_fmt='&'#" ; else echo "# par_output_fmt="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT_OPTION}" | sed "s#'#'\"'\"'#g;s#.*#par_output_fmt_option='&'#" ; else echo "# par_output_fmt_option="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\$par_uncompressed" == "false" ]] && unset par_uncompressed +[[ "\$par_fast" == "false" ]] && unset par_fast +[[ "\$par_no_pg" == "false" ]] && unset par_no_pg + +samtools collate \\ + "\$par_input" \\ + \${par_output:+-o "\$par_output"} \\ + \${par_reference:+-T "\$par_reference"} \\ + \${par_uncompressed:+-u} \\ + \${par_fast:+-f} \\ + \${par_working_reads:+-r "\$par_working_reads"} \\ + \${par_compression:+-l "\$par_compression"} \\ + \${par_nb_tmp_files:+-n "\$par_nb_tmp_files"} \\ + \${par_tmp_prefix:+-T "\$par_tmp_prefix"} \\ + \${par_no_pg:+-P} \\ + \${par_input_fmt_option:+-O "\$par_input_fmt_option"} \\ + \${par_output_fmt:+-O "\$par_output_fmt"} \\ + \${par_output_fmt_option:+-O "\$par_output_fmt_option"} + +exit 0 +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_REFERENCE" ]; then + VIASH_PAR_REFERENCE=$(ViashDockerStripAutomount "$VIASH_PAR_REFERENCE") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/samtools/samtools_faidx/.config.vsh.yaml b/target/executable/samtools/samtools_faidx/.config.vsh.yaml new file mode 100644 index 00000000..9c03e06a --- /dev/null +++ b/target/executable/samtools/samtools_faidx/.config.vsh.yaml @@ -0,0 +1,255 @@ +name: "samtools_faidx" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "FASTA input file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--length" + alternatives: + - "-n" + description: "Length for FASTA sequence line wrapping. If zero, this means do\ + \ not\nline wrap. Defaults to the line length in the input file.\n" + info: null + default: + - 60 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--region_file" + alternatives: + - "-r" + description: "File of regions. Format is chr:from-to. One per line.\nMust be used\ + \ with --output to avoid sending output to stdout.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Options" + arguments: + - type: "boolean_true" + name: "--continue" + description: "Continue working if a non-existent region is requested.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--reverse_complement" + alternatives: + - "-i" + description: "Reverse complement sequences.\n" + info: null + direction: "input" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Write output to file.\n" + info: null + example: + - "output.fasta" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--mark_strand" + description: "Add strand indicator to sequence name. Options are:\n[ rc, no, sign,\ + \ custom,, ]\n" + info: null + default: + - "rc" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--fai_idx" + description: "Read/Write to specified index file (default file.fa.fai).\n" + info: null + example: + - "file.fa.fai" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--gzi_idx" + description: "Read/Write to specified compressed file index (used with .gz files,\ + \ default file.fa.gz.gzi).\n" + info: null + example: + - "file.fa.gz.gzi" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--fastq" + description: "Read FASTQ files and output extracted sequences in FASTQ format.\ + \ Same as using samtools fqidx.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Indexes FASTA files to enable random access to fasta and fastq files." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "idex" +- "fasta" +- "faidx" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-faidx.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_faidx/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/samtools/samtools_faidx" + executable: "target/executable/samtools/samtools_faidx/samtools_faidx" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/samtools/samtools_faidx/samtools_faidx b/target/executable/samtools/samtools_faidx/samtools_faidx new file mode 100755 index 00000000..8ed78227 --- /dev/null +++ b/target/executable/samtools/samtools_faidx/samtools_faidx @@ -0,0 +1,1286 @@ +#!/usr/bin/env bash + +# samtools_faidx main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="samtools_faidx" +VIASH_META_FUNCTIONALITY_NAME="samtools_faidx" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "samtools_faidx main" + echo "" + echo "Indexes FASTA files to enable random access to fasta and fastq files." + echo "" + echo "Inputs:" + echo " --input" + echo " type: file, file must exist" + echo " FASTA input file." + echo "" + echo " -n, --length" + echo " type: integer" + echo " default: 60" + echo " Length for FASTA sequence line wrapping. If zero, this means do not" + echo " line wrap. Defaults to the line length in the input file." + echo "" + echo " -r, --region_file" + echo " type: file, file must exist" + echo " File of regions. Format is chr:from-to. One per line." + echo " Must be used with --output to avoid sending output to stdout." + echo "" + echo "Options:" + echo " --continue" + echo " type: boolean_true" + echo " Continue working if a non-existent region is requested." + echo "" + echo " -i, --reverse_complement" + echo " type: boolean_true" + echo " Reverse complement sequences." + echo "" + echo "Outputs:" + echo " -o, --output" + echo " type: file, required parameter, output, file must exist" + echo " example: output.fasta" + echo " Write output to file." + echo "" + echo " --mark_strand" + echo " type: string" + echo " default: rc" + echo " Add strand indicator to sequence name. Options are:" + echo " [ rc, no, sign, custom,, ]" + echo "" + echo " --fai_idx" + echo " type: file, output, file must exist" + echo " example: file.fa.fai" + echo " Read/Write to specified index file (default file.fa.fai)." + echo "" + echo " --gzi_idx" + echo " type: file, output, file must exist" + echo " example: file.fa.gz.gzi" + echo " Read/Write to specified compressed file index (used with .gz files," + echo " default file.fa.gz.gzi)." + echo "" + echo " --fastq" + echo " type: boolean_true" + echo " Read FASTQ files and output extracted sequences in FASTQ format. Same as" + echo " using samtools fqidx." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 +ENTRYPOINT [] +RUN samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ +sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component samtools samtools_faidx" +LABEL org.opencontainers.image.created="2024-06-24T08:36:38Z" +LABEL org.opencontainers.image.source="https://github.com/samtools/samtools" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "samtools_faidx main" + exit + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --length) + [ -n "$VIASH_PAR_LENGTH" ] && ViashError Bad arguments for option \'--length\': \'$VIASH_PAR_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --length=*) + [ -n "$VIASH_PAR_LENGTH" ] && ViashError Bad arguments for option \'--length=*\': \'$VIASH_PAR_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -n) + [ -n "$VIASH_PAR_LENGTH" ] && ViashError Bad arguments for option \'-n\': \'$VIASH_PAR_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -n. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --region_file) + [ -n "$VIASH_PAR_REGION_FILE" ] && ViashError Bad arguments for option \'--region_file\': \'$VIASH_PAR_REGION_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REGION_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --region_file. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --region_file=*) + [ -n "$VIASH_PAR_REGION_FILE" ] && ViashError Bad arguments for option \'--region_file=*\': \'$VIASH_PAR_REGION_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REGION_FILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -r) + [ -n "$VIASH_PAR_REGION_FILE" ] && ViashError Bad arguments for option \'-r\': \'$VIASH_PAR_REGION_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REGION_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -r. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --continue) + [ -n "$VIASH_PAR_CONTINUE" ] && ViashError Bad arguments for option \'--continue\': \'$VIASH_PAR_CONTINUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CONTINUE=true + shift 1 + ;; + --reverse_complement) + [ -n "$VIASH_PAR_REVERSE_COMPLEMENT" ] && ViashError Bad arguments for option \'--reverse_complement\': \'$VIASH_PAR_REVERSE_COMPLEMENT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REVERSE_COMPLEMENT=true + shift 1 + ;; + -i) + [ -n "$VIASH_PAR_REVERSE_COMPLEMENT" ] && ViashError Bad arguments for option \'-i\': \'$VIASH_PAR_REVERSE_COMPLEMENT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REVERSE_COMPLEMENT=true + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mark_strand) + [ -n "$VIASH_PAR_MARK_STRAND" ] && ViashError Bad arguments for option \'--mark_strand\': \'$VIASH_PAR_MARK_STRAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MARK_STRAND="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --mark_strand. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --mark_strand=*) + [ -n "$VIASH_PAR_MARK_STRAND" ] && ViashError Bad arguments for option \'--mark_strand=*\': \'$VIASH_PAR_MARK_STRAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MARK_STRAND=$(ViashRemoveFlags "$1") + shift 1 + ;; + --fai_idx) + [ -n "$VIASH_PAR_FAI_IDX" ] && ViashError Bad arguments for option \'--fai_idx\': \'$VIASH_PAR_FAI_IDX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FAI_IDX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --fai_idx. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fai_idx=*) + [ -n "$VIASH_PAR_FAI_IDX" ] && ViashError Bad arguments for option \'--fai_idx=*\': \'$VIASH_PAR_FAI_IDX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FAI_IDX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --gzi_idx) + [ -n "$VIASH_PAR_GZI_IDX" ] && ViashError Bad arguments for option \'--gzi_idx\': \'$VIASH_PAR_GZI_IDX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GZI_IDX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --gzi_idx. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --gzi_idx=*) + [ -n "$VIASH_PAR_GZI_IDX" ] && ViashError Bad arguments for option \'--gzi_idx=*\': \'$VIASH_PAR_GZI_IDX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GZI_IDX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --fastq) + [ -n "$VIASH_PAR_FASTQ" ] && ViashError Bad arguments for option \'--fastq\': \'$VIASH_PAR_FASTQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FASTQ=true + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/samtools/samtools_faidx:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_LENGTH+x} ]; then + VIASH_PAR_LENGTH="60" +fi +if [ -z ${VIASH_PAR_CONTINUE+x} ]; then + VIASH_PAR_CONTINUE="false" +fi +if [ -z ${VIASH_PAR_REVERSE_COMPLEMENT+x} ]; then + VIASH_PAR_REVERSE_COMPLEMENT="false" +fi +if [ -z ${VIASH_PAR_MARK_STRAND+x} ]; then + VIASH_PAR_MARK_STRAND="rc" +fi +if [ -z ${VIASH_PAR_FASTQ+x} ]; then + VIASH_PAR_FASTQ="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REGION_FILE" ] && [ ! -e "$VIASH_PAR_REGION_FILE" ]; then + ViashError "Input file '$VIASH_PAR_REGION_FILE' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CONTINUE" ]]; then + if ! [[ "$VIASH_PAR_CONTINUE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--continue' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_REVERSE_COMPLEMENT" ]]; then + if ! [[ "$VIASH_PAR_REVERSE_COMPLEMENT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--reverse_complement' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FASTQ" ]]; then + if ! [[ "$VIASH_PAR_FASTQ" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--fastq' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi +if [ ! -z "$VIASH_PAR_FAI_IDX" ] && [ ! -d "$(dirname "$VIASH_PAR_FAI_IDX")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_FAI_IDX")" +fi +if [ ! -z "$VIASH_PAR_GZI_IDX" ] && [ ! -d "$(dirname "$VIASH_PAR_GZI_IDX")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_GZI_IDX")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_REGION_FILE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REGION_FILE")" ) + VIASH_PAR_REGION_FILE=$(ViashDockerAutodetectMount "$VIASH_PAR_REGION_FILE") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_PAR_FAI_IDX" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FAI_IDX")" ) + VIASH_PAR_FAI_IDX=$(ViashDockerAutodetectMount "$VIASH_PAR_FAI_IDX") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_FAI_IDX" ) +fi +if [ ! -z "$VIASH_PAR_GZI_IDX" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_GZI_IDX")" ) + VIASH_PAR_GZI_IDX=$(ViashDockerAutodetectMount "$VIASH_PAR_GZI_IDX") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_GZI_IDX" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-samtools_faidx-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_LENGTH+x} ]; then echo "${VIASH_PAR_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_length='&'#" ; else echo "# par_length="; fi ) +$( if [ ! -z ${VIASH_PAR_REGION_FILE+x} ]; then echo "${VIASH_PAR_REGION_FILE}" | sed "s#'#'\"'\"'#g;s#.*#par_region_file='&'#" ; else echo "# par_region_file="; fi ) +$( if [ ! -z ${VIASH_PAR_CONTINUE+x} ]; then echo "${VIASH_PAR_CONTINUE}" | sed "s#'#'\"'\"'#g;s#.*#par_continue='&'#" ; else echo "# par_continue="; fi ) +$( if [ ! -z ${VIASH_PAR_REVERSE_COMPLEMENT+x} ]; then echo "${VIASH_PAR_REVERSE_COMPLEMENT}" | sed "s#'#'\"'\"'#g;s#.*#par_reverse_complement='&'#" ; else echo "# par_reverse_complement="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_MARK_STRAND+x} ]; then echo "${VIASH_PAR_MARK_STRAND}" | sed "s#'#'\"'\"'#g;s#.*#par_mark_strand='&'#" ; else echo "# par_mark_strand="; fi ) +$( if [ ! -z ${VIASH_PAR_FAI_IDX+x} ]; then echo "${VIASH_PAR_FAI_IDX}" | sed "s#'#'\"'\"'#g;s#.*#par_fai_idx='&'#" ; else echo "# par_fai_idx="; fi ) +$( if [ ! -z ${VIASH_PAR_GZI_IDX+x} ]; then echo "${VIASH_PAR_GZI_IDX}" | sed "s#'#'\"'\"'#g;s#.*#par_gzi_idx='&'#" ; else echo "# par_gzi_idx="; fi ) +$( if [ ! -z ${VIASH_PAR_FASTQ+x} ]; then echo "${VIASH_PAR_FASTQ}" | sed "s#'#'\"'\"'#g;s#.*#par_fastq='&'#" ; else echo "# par_fastq="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\$par_continue" == "false" ]] && unset par_continue +[[ "\$par_reverse_complement" == "false" ]] && unset par_reverse_complement +[[ "\$par_fastq" == "false" ]] && unset par_fastq + +samtools faidx \\ + "\$par_input" \\ + \${par_output:+-o "\$par_output"} \\ + \${par_length:+-n "\$par_length"} \\ + \${par_continue:+-c} \\ + \${par_region_file:+-r "\$par_region_file"} \\ + \${par_reverse_complement:+-r} \\ + \${par_mark_strand:+--mark-strand "\$par_mark_strand"} \\ + \${par_fai_idx:+--fai-idx "\$par_fai_idx"} \\ + \${par_gzi_idx:+--gzi-idx "\$par_gzi_idx"} \\ + \${par_fastq:+-f} + +exit 0 +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_REGION_FILE" ]; then + VIASH_PAR_REGION_FILE=$(ViashDockerStripAutomount "$VIASH_PAR_REGION_FILE") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_PAR_FAI_IDX" ]; then + VIASH_PAR_FAI_IDX=$(ViashDockerStripAutomount "$VIASH_PAR_FAI_IDX") + fi + if [ ! -z "$VIASH_PAR_GZI_IDX" ]; then + VIASH_PAR_GZI_IDX=$(ViashDockerStripAutomount "$VIASH_PAR_GZI_IDX") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_FAI_IDX" ] && [ ! -e "$VIASH_PAR_FAI_IDX" ]; then + ViashError "Output file '$VIASH_PAR_FAI_IDX' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_GZI_IDX" ] && [ ! -e "$VIASH_PAR_GZI_IDX" ]; then + ViashError "Output file '$VIASH_PAR_GZI_IDX' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/samtools/samtools_fastq/.config.vsh.yaml b/target/executable/samtools/samtools_fastq/.config.vsh.yaml new file mode 100644 index 00000000..9dc8ec0a --- /dev/null +++ b/target/executable/samtools/samtools_fastq/.config.vsh.yaml @@ -0,0 +1,443 @@ +name: "samtools_fastq" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "input SAM/BAM/CRAM file" + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + description: "output FASTQ file" + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Options" + arguments: + - type: "boolean_true" + name: "--no_suffix" + alternatives: + - "-n" + description: "By default, either '/1' or '/2' is added to the end of read names\ + \ where the corresponding \nREAD1 or READ2 FLAG bit is set. Using -n causes\ + \ read names to be left as they are.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--suffix" + alternatives: + - "-N" + description: "Always add either '/1' or '/2' to the end of read names even when\ + \ put into different files.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--use_oq" + alternatives: + - "-O" + description: "Use quality values from OQ tags in preference to standard quality\ + \ string if available.\n" + info: null + direction: "input" + - type: "file" + name: "--singleton" + alternatives: + - "-s" + description: "write singleton reads to FILE." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--copy_tags" + alternatives: + - "-t" + description: "Copy RG, BC and QT tags to the FASTQ header line, if they exist.\n" + info: null + direction: "input" + - type: "string" + name: "--copy_tags_list" + alternatives: + - "-T" + description: "Specify a comma-separated list of tags to copy to the FASTQ header\ + \ line, if they exist. \nTAGLIST can be blank or * to indicate all tags should\ + \ be copied to the output. If using *, \nbe careful to quote it to avoid unwanted\ + \ shell expansion.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--read1" + alternatives: + - "-1" + description: "Write reads with the READ1 FLAG set (and READ2 not set) to FILE\ + \ instead of outputting them. \nIf the -s option is used, only paired reads\ + \ will be written to this file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--read2" + alternatives: + - "-2" + description: "Write reads with the READ2 FLAG set (and READ1 not set) to FILE\ + \ instead of outputting them. \nIf the -s option is used, only paired reads\ + \ will be written to this file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--output_reads" + alternatives: + - "-o" + description: "Write reads with either READ1 FLAG or READ2 flag set to FILE instead\ + \ of outputting them to stdout. \nThis is equivalent to -1 FILE -2 FILE.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--output_reads_both" + alternatives: + - "0" + description: "Write reads where the READ1 and READ2 FLAG bits set are either both\ + \ set or both unset to FILE \ninstead of outputting them.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--filter_flags" + alternatives: + - "-f" + description: "Only output alignments with all bits set in INT present in the FLAG\ + \ field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/)\ + \ or in octal by beginning with `0' \n(i.e. /^0[0-7]+/).\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--excl_flags" + alternatives: + - "-F" + description: "Do not output alignments with any bits set in INT present in the\ + \ FLAG field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/)\ + \ or in octal by beginning with `0' \n(i.e. /^0[0-7]+/). This defaults to 0x900\ + \ representing filtering of secondary and \nsupplementary alignments.\n" + info: null + default: + - "2304" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--incl_flags" + alternatives: + - "--rf" + description: "Only output alignments with any bits set in INT present in the FLAG\ + \ field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/),\ + \ in octal by beginning with `0' \n(i.e. /^0[0-7]+/), as a decimal number not\ + \ beginning with '0' or as a comma-separated list of \nflag names.\n" + info: null + default: + - "0" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--excl_flags_all" + alternatives: + - "-G" + description: "Only EXCLUDE reads with all of the bits set in INT present in the\ + \ FLAG field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/)\ + \ or in octal by beginning with `0' \n(i.e. /^0[0-7]+/).\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--aux_tag" + alternatives: + - "-d" + description: "Only output alignments containing an auxiliary tag matching both\ + \ TAG and VAL. If VAL is omitted \nthen any value is accepted. The tag types\ + \ supported are i, f, Z, A and H. \"B\" arrays are not \nsupported. This is\ + \ comparable to the method used in samtools view --tag. The option may be specified\ + \ \nmultiple times and is equivalent to using the --aux_tag_file option.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--aux_tag_file" + alternatives: + - "-D" + description: "Only output alignments containing an auxiliary tag matching TAG\ + \ and having a value listed in FILE. \nThe format of the file is one line per\ + \ value. This is equivalent to specifying --aux_tag multiple times.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--casava" + alternatives: + - "-i" + description: "add Illumina Casava 1.8 format entry to header (eg 1:N:0:ATCACG)" + info: null + direction: "input" + - type: "integer" + name: "--compression" + alternatives: + - "-c" + description: "set compression level when writing gz or bgzf fastq files." + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--index1" + alternatives: + - "--i1" + description: "write first index reads to FILE." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--index2" + alternatives: + - "--i2" + description: "write second index reads to FILE." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--barcode_tag" + description: "Auxiliary tag to find index reads in." + info: null + default: + - "BC" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--quality_tag" + description: "Auxiliary tag to find index quality in." + info: null + default: + - "QT" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--index_format" + description: "string to describe how to parse the barcode and quality tags. For\ + \ example:\n[i14i8]: the first 14 characters are index 1, the next 8 characters\ + \ are index 2.\n[n8i14]: ignore the first 8 characters, and use the next 14\ + \ characters for index 1.\nIf the tag contains a separator, then the numeric\ + \ part can be replaced with '*' to mean \n'read until the separator or end of\ + \ tag', for example: [n*i*].\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Converts a SAM, BAM or CRAM to FASTQ format." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "fastq" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-fastq.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_fastq/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/samtools/samtools_fastq" + executable: "target/executable/samtools/samtools_fastq/samtools_fastq" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/samtools/samtools_fastq/samtools_fastq b/target/executable/samtools/samtools_fastq/samtools_fastq new file mode 100755 index 00000000..ef47ef18 --- /dev/null +++ b/target/executable/samtools/samtools_fastq/samtools_fastq @@ -0,0 +1,1775 @@ +#!/usr/bin/env bash + +# samtools_fastq main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="samtools_fastq" +VIASH_META_FUNCTIONALITY_NAME="samtools_fastq" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "samtools_fastq main" + echo "" + echo "Converts a SAM, BAM or CRAM to FASTQ format." + echo "" + echo "Inputs:" + echo " --input" + echo " type: file, required parameter, file must exist" + echo " input SAM/BAM/CRAM file" + echo "" + echo "Outputs:" + echo " --output" + echo " type: file, required parameter, output, file must exist" + echo " output FASTQ file" + echo "" + echo "Options:" + echo " -n, --no_suffix" + echo " type: boolean_true" + echo " By default, either '/1' or '/2' is added to the end of read names where" + echo " the corresponding" + echo " READ1 or READ2 FLAG bit is set. Using -n causes read names to be left as" + echo " they are." + echo "" + echo " -N, --suffix" + echo " type: boolean_true" + echo " Always add either '/1' or '/2' to the end of read names even when put" + echo " into different files." + echo "" + echo " -O, --use_oq" + echo " type: boolean_true" + echo " Use quality values from OQ tags in preference to standard quality string" + echo " if available." + echo "" + echo " -s, --singleton" + echo " type: file, file must exist" + echo " write singleton reads to FILE." + echo "" + echo " -t, --copy_tags" + echo " type: boolean_true" + echo " Copy RG, BC and QT tags to the FASTQ header line, if they exist." + echo "" + echo " -T, --copy_tags_list" + echo " type: string" + echo " Specify a comma-separated list of tags to copy to the FASTQ header line," + echo " if they exist." + echo " TAGLIST can be blank or * to indicate all tags should be copied to the" + echo " output. If using *," + echo " be careful to quote it to avoid unwanted shell expansion." + echo "" + echo " -1, --read1" + echo " type: file, output, file must exist" + echo " Write reads with the READ1 FLAG set (and READ2 not set) to FILE instead" + echo " of outputting them." + echo " If the -s option is used, only paired reads will be written to this" + echo " file." + echo "" + echo " -2, --read2" + echo " type: file, output, file must exist" + echo " Write reads with the READ2 FLAG set (and READ1 not set) to FILE instead" + echo " of outputting them." + echo " If the -s option is used, only paired reads will be written to this" + echo " file." + echo "" + echo " -o, --output_reads" + echo " type: file, output, file must exist" + echo " Write reads with either READ1 FLAG or READ2 flag set to FILE instead of" + echo " outputting them to stdout." + echo " This is equivalent to -1 FILE -2 FILE." + echo "" + echo " 0, --output_reads_both" + echo " type: file, output, file must exist" + echo " Write reads where the READ1 and READ2 FLAG bits set are either both set" + echo " or both unset to FILE" + echo " instead of outputting them." + echo "" + echo " -f, --filter_flags" + echo " type: integer" + echo " default: 0" + echo " Only output alignments with all bits set in INT present in the FLAG" + echo " field. INT can be specified" + echo " in hex by beginning with \`0x' (i.e. /^0x[0-9A-F]+/) or in octal by" + echo " beginning with \`0'" + echo " (i.e. /^0[0-7]+/)." + echo "" + echo " -F, --excl_flags" + echo " type: string" + echo " default: 2304" + echo " Do not output alignments with any bits set in INT present in the FLAG" + echo " field. INT can be specified" + echo " in hex by beginning with \`0x' (i.e. /^0x[0-9A-F]+/) or in octal by" + echo " beginning with \`0'" + echo " (i.e. /^0[0-7]+/). This defaults to 0x900 representing filtering of" + echo " secondary and" + echo " supplementary alignments." + echo "" + echo " --rf, --incl_flags" + echo " type: string" + echo " default: 0" + echo " Only output alignments with any bits set in INT present in the FLAG" + echo " field. INT can be specified" + echo " in hex by beginning with \`0x' (i.e. /^0x[0-9A-F]+/), in octal by" + echo " beginning with \`0'" + echo " (i.e. /^0[0-7]+/), as a decimal number not beginning with '0' or as a" + echo " comma-separated list of" + echo " flag names." + echo "" + echo " -G, --excl_flags_all" + echo " type: integer" + echo " default: 0" + echo " Only EXCLUDE reads with all of the bits set in INT present in the FLAG" + echo " field. INT can be specified" + echo " in hex by beginning with \`0x' (i.e. /^0x[0-9A-F]+/) or in octal by" + echo " beginning with \`0'" + echo " (i.e. /^0[0-7]+/)." + echo "" + echo " -d, --aux_tag" + echo " type: string" + echo " Only output alignments containing an auxiliary tag matching both TAG and" + echo " VAL. If VAL is omitted" + echo " then any value is accepted. The tag types supported are i, f, Z, A and" + echo " H. \"B\" arrays are not" + echo " supported. This is comparable to the method used in samtools view --tag." + echo " The option may be specified" + echo " multiple times and is equivalent to using the --aux_tag_file option." + echo "" + echo " -D, --aux_tag_file" + echo " type: string" + echo " Only output alignments containing an auxiliary tag matching TAG and" + echo " having a value listed in FILE." + echo " The format of the file is one line per value. This is equivalent to" + echo " specifying --aux_tag multiple times." + echo "" + echo " -i, --casava" + echo " type: boolean_true" + echo " add Illumina Casava 1.8 format entry to header (eg 1:N:0:ATCACG)" + echo "" + echo " -c, --compression" + echo " type: integer" + echo " default: 0" + echo " set compression level when writing gz or bgzf fastq files." + echo "" + echo " --i1, --index1" + echo " type: file, file must exist" + echo " write first index reads to FILE." + echo "" + echo " --i2, --index2" + echo " type: file, file must exist" + echo " write second index reads to FILE." + echo "" + echo " --barcode_tag" + echo " type: string" + echo " default: BC" + echo " Auxiliary tag to find index reads in." + echo "" + echo " --quality_tag" + echo " type: string" + echo " default: QT" + echo " Auxiliary tag to find index quality in." + echo "" + echo " --index_format" + echo " type: string" + echo " string to describe how to parse the barcode and quality tags. For" + echo " example:" + echo " [i14i8]: the first 14 characters are index 1, the next 8 characters are" + echo " index 2." + echo " [n8i14]: ignore the first 8 characters, and use the next 14 characters" + echo " for index 1." + echo " If the tag contains a separator, then the numeric part can be replaced" + echo " with '*' to mean" + echo " 'read until the separator or end of tag', for example: [n*i*]." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 +ENTRYPOINT [] +RUN samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ +sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component samtools samtools_fastq" +LABEL org.opencontainers.image.created="2024-06-24T08:36:41Z" +LABEL org.opencontainers.image.source="https://github.com/samtools/samtools" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "samtools_fastq main" + exit + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --no_suffix) + [ -n "$VIASH_PAR_NO_SUFFIX" ] && ViashError Bad arguments for option \'--no_suffix\': \'$VIASH_PAR_NO_SUFFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_SUFFIX=true + shift 1 + ;; + -n) + [ -n "$VIASH_PAR_NO_SUFFIX" ] && ViashError Bad arguments for option \'-n\': \'$VIASH_PAR_NO_SUFFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_SUFFIX=true + shift 1 + ;; + --suffix) + [ -n "$VIASH_PAR_SUFFIX" ] && ViashError Bad arguments for option \'--suffix\': \'$VIASH_PAR_SUFFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUFFIX=true + shift 1 + ;; + -N) + [ -n "$VIASH_PAR_SUFFIX" ] && ViashError Bad arguments for option \'-N\': \'$VIASH_PAR_SUFFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUFFIX=true + shift 1 + ;; + --use_oq) + [ -n "$VIASH_PAR_USE_OQ" ] && ViashError Bad arguments for option \'--use_oq\': \'$VIASH_PAR_USE_OQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_USE_OQ=true + shift 1 + ;; + -O) + [ -n "$VIASH_PAR_USE_OQ" ] && ViashError Bad arguments for option \'-O\': \'$VIASH_PAR_USE_OQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_USE_OQ=true + shift 1 + ;; + --singleton) + [ -n "$VIASH_PAR_SINGLETON" ] && ViashError Bad arguments for option \'--singleton\': \'$VIASH_PAR_SINGLETON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SINGLETON="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --singleton. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --singleton=*) + [ -n "$VIASH_PAR_SINGLETON" ] && ViashError Bad arguments for option \'--singleton=*\': \'$VIASH_PAR_SINGLETON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SINGLETON=$(ViashRemoveFlags "$1") + shift 1 + ;; + -s) + [ -n "$VIASH_PAR_SINGLETON" ] && ViashError Bad arguments for option \'-s\': \'$VIASH_PAR_SINGLETON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SINGLETON="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -s. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --copy_tags) + [ -n "$VIASH_PAR_COPY_TAGS" ] && ViashError Bad arguments for option \'--copy_tags\': \'$VIASH_PAR_COPY_TAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COPY_TAGS=true + shift 1 + ;; + -t) + [ -n "$VIASH_PAR_COPY_TAGS" ] && ViashError Bad arguments for option \'-t\': \'$VIASH_PAR_COPY_TAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COPY_TAGS=true + shift 1 + ;; + --copy_tags_list) + [ -n "$VIASH_PAR_COPY_TAGS_LIST" ] && ViashError Bad arguments for option \'--copy_tags_list\': \'$VIASH_PAR_COPY_TAGS_LIST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COPY_TAGS_LIST="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --copy_tags_list. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --copy_tags_list=*) + [ -n "$VIASH_PAR_COPY_TAGS_LIST" ] && ViashError Bad arguments for option \'--copy_tags_list=*\': \'$VIASH_PAR_COPY_TAGS_LIST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COPY_TAGS_LIST=$(ViashRemoveFlags "$1") + shift 1 + ;; + -T) + [ -n "$VIASH_PAR_COPY_TAGS_LIST" ] && ViashError Bad arguments for option \'-T\': \'$VIASH_PAR_COPY_TAGS_LIST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COPY_TAGS_LIST="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -T. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read1) + [ -n "$VIASH_PAR_READ1" ] && ViashError Bad arguments for option \'--read1\': \'$VIASH_PAR_READ1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --read1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read1=*) + [ -n "$VIASH_PAR_READ1" ] && ViashError Bad arguments for option \'--read1=*\': \'$VIASH_PAR_READ1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ1=$(ViashRemoveFlags "$1") + shift 1 + ;; + -1) + [ -n "$VIASH_PAR_READ1" ] && ViashError Bad arguments for option \'-1\': \'$VIASH_PAR_READ1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read2) + [ -n "$VIASH_PAR_READ2" ] && ViashError Bad arguments for option \'--read2\': \'$VIASH_PAR_READ2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --read2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read2=*) + [ -n "$VIASH_PAR_READ2" ] && ViashError Bad arguments for option \'--read2=*\': \'$VIASH_PAR_READ2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ2=$(ViashRemoveFlags "$1") + shift 1 + ;; + -2) + [ -n "$VIASH_PAR_READ2" ] && ViashError Bad arguments for option \'-2\': \'$VIASH_PAR_READ2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_reads) + [ -n "$VIASH_PAR_OUTPUT_READS" ] && ViashError Bad arguments for option \'--output_reads\': \'$VIASH_PAR_OUTPUT_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_reads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_reads=*) + [ -n "$VIASH_PAR_OUTPUT_READS" ] && ViashError Bad arguments for option \'--output_reads=*\': \'$VIASH_PAR_OUTPUT_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_READS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTPUT_READS" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTPUT_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_reads_both) + [ -n "$VIASH_PAR_OUTPUT_READS_BOTH" ] && ViashError Bad arguments for option \'--output_reads_both\': \'$VIASH_PAR_OUTPUT_READS_BOTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_READS_BOTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_reads_both. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_reads_both=*) + [ -n "$VIASH_PAR_OUTPUT_READS_BOTH" ] && ViashError Bad arguments for option \'--output_reads_both=*\': \'$VIASH_PAR_OUTPUT_READS_BOTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_READS_BOTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + 0) + [ -n "$VIASH_PAR_OUTPUT_READS_BOTH" ] && ViashError Bad arguments for option \'0\': \'$VIASH_PAR_OUTPUT_READS_BOTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_READS_BOTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to 0. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --filter_flags) + [ -n "$VIASH_PAR_FILTER_FLAGS" ] && ViashError Bad arguments for option \'--filter_flags\': \'$VIASH_PAR_FILTER_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --filter_flags. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --filter_flags=*) + [ -n "$VIASH_PAR_FILTER_FLAGS" ] && ViashError Bad arguments for option \'--filter_flags=*\': \'$VIASH_PAR_FILTER_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_FLAGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_FILTER_FLAGS" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_FILTER_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTER_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -f. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --excl_flags) + [ -n "$VIASH_PAR_EXCL_FLAGS" ] && ViashError Bad arguments for option \'--excl_flags\': \'$VIASH_PAR_EXCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --excl_flags. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --excl_flags=*) + [ -n "$VIASH_PAR_EXCL_FLAGS" ] && ViashError Bad arguments for option \'--excl_flags=*\': \'$VIASH_PAR_EXCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_FLAGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -F) + [ -n "$VIASH_PAR_EXCL_FLAGS" ] && ViashError Bad arguments for option \'-F\': \'$VIASH_PAR_EXCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -F. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --incl_flags) + [ -n "$VIASH_PAR_INCL_FLAGS" ] && ViashError Bad arguments for option \'--incl_flags\': \'$VIASH_PAR_INCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INCL_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --incl_flags. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --incl_flags=*) + [ -n "$VIASH_PAR_INCL_FLAGS" ] && ViashError Bad arguments for option \'--incl_flags=*\': \'$VIASH_PAR_INCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INCL_FLAGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --rf) + [ -n "$VIASH_PAR_INCL_FLAGS" ] && ViashError Bad arguments for option \'--rf\': \'$VIASH_PAR_INCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INCL_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --rf. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --excl_flags_all) + [ -n "$VIASH_PAR_EXCL_FLAGS_ALL" ] && ViashError Bad arguments for option \'--excl_flags_all\': \'$VIASH_PAR_EXCL_FLAGS_ALL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_FLAGS_ALL="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --excl_flags_all. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --excl_flags_all=*) + [ -n "$VIASH_PAR_EXCL_FLAGS_ALL" ] && ViashError Bad arguments for option \'--excl_flags_all=*\': \'$VIASH_PAR_EXCL_FLAGS_ALL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_FLAGS_ALL=$(ViashRemoveFlags "$1") + shift 1 + ;; + -G) + [ -n "$VIASH_PAR_EXCL_FLAGS_ALL" ] && ViashError Bad arguments for option \'-G\': \'$VIASH_PAR_EXCL_FLAGS_ALL\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_FLAGS_ALL="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -G. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --aux_tag) + [ -n "$VIASH_PAR_AUX_TAG" ] && ViashError Bad arguments for option \'--aux_tag\': \'$VIASH_PAR_AUX_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUX_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --aux_tag. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --aux_tag=*) + [ -n "$VIASH_PAR_AUX_TAG" ] && ViashError Bad arguments for option \'--aux_tag=*\': \'$VIASH_PAR_AUX_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUX_TAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + -d) + [ -n "$VIASH_PAR_AUX_TAG" ] && ViashError Bad arguments for option \'-d\': \'$VIASH_PAR_AUX_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUX_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -d. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --aux_tag_file) + [ -n "$VIASH_PAR_AUX_TAG_FILE" ] && ViashError Bad arguments for option \'--aux_tag_file\': \'$VIASH_PAR_AUX_TAG_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUX_TAG_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --aux_tag_file. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --aux_tag_file=*) + [ -n "$VIASH_PAR_AUX_TAG_FILE" ] && ViashError Bad arguments for option \'--aux_tag_file=*\': \'$VIASH_PAR_AUX_TAG_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUX_TAG_FILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -D) + [ -n "$VIASH_PAR_AUX_TAG_FILE" ] && ViashError Bad arguments for option \'-D\': \'$VIASH_PAR_AUX_TAG_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_AUX_TAG_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -D. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --casava) + [ -n "$VIASH_PAR_CASAVA" ] && ViashError Bad arguments for option \'--casava\': \'$VIASH_PAR_CASAVA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CASAVA=true + shift 1 + ;; + -i) + [ -n "$VIASH_PAR_CASAVA" ] && ViashError Bad arguments for option \'-i\': \'$VIASH_PAR_CASAVA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CASAVA=true + shift 1 + ;; + --compression) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'--compression\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --compression. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --compression=*) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'--compression=*\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION=$(ViashRemoveFlags "$1") + shift 1 + ;; + -c) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'-c\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -c. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --index1) + [ -n "$VIASH_PAR_INDEX1" ] && ViashError Bad arguments for option \'--index1\': \'$VIASH_PAR_INDEX1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --index1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --index1=*) + [ -n "$VIASH_PAR_INDEX1" ] && ViashError Bad arguments for option \'--index1=*\': \'$VIASH_PAR_INDEX1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX1=$(ViashRemoveFlags "$1") + shift 1 + ;; + --i1) + [ -n "$VIASH_PAR_INDEX1" ] && ViashError Bad arguments for option \'--i1\': \'$VIASH_PAR_INDEX1\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX1="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --i1. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --index2) + [ -n "$VIASH_PAR_INDEX2" ] && ViashError Bad arguments for option \'--index2\': \'$VIASH_PAR_INDEX2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --index2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --index2=*) + [ -n "$VIASH_PAR_INDEX2" ] && ViashError Bad arguments for option \'--index2=*\': \'$VIASH_PAR_INDEX2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX2=$(ViashRemoveFlags "$1") + shift 1 + ;; + --i2) + [ -n "$VIASH_PAR_INDEX2" ] && ViashError Bad arguments for option \'--i2\': \'$VIASH_PAR_INDEX2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --i2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --barcode_tag) + [ -n "$VIASH_PAR_BARCODE_TAG" ] && ViashError Bad arguments for option \'--barcode_tag\': \'$VIASH_PAR_BARCODE_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BARCODE_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --barcode_tag. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --barcode_tag=*) + [ -n "$VIASH_PAR_BARCODE_TAG" ] && ViashError Bad arguments for option \'--barcode_tag=*\': \'$VIASH_PAR_BARCODE_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BARCODE_TAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + --quality_tag) + [ -n "$VIASH_PAR_QUALITY_TAG" ] && ViashError Bad arguments for option \'--quality_tag\': \'$VIASH_PAR_QUALITY_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --quality_tag. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quality_tag=*) + [ -n "$VIASH_PAR_QUALITY_TAG" ] && ViashError Bad arguments for option \'--quality_tag=*\': \'$VIASH_PAR_QUALITY_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUALITY_TAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + --index_format) + [ -n "$VIASH_PAR_INDEX_FORMAT" ] && ViashError Bad arguments for option \'--index_format\': \'$VIASH_PAR_INDEX_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX_FORMAT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --index_format. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --index_format=*) + [ -n "$VIASH_PAR_INDEX_FORMAT" ] && ViashError Bad arguments for option \'--index_format=*\': \'$VIASH_PAR_INDEX_FORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX_FORMAT=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/samtools/samtools_fastq:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_NO_SUFFIX+x} ]; then + VIASH_PAR_NO_SUFFIX="false" +fi +if [ -z ${VIASH_PAR_SUFFIX+x} ]; then + VIASH_PAR_SUFFIX="false" +fi +if [ -z ${VIASH_PAR_USE_OQ+x} ]; then + VIASH_PAR_USE_OQ="false" +fi +if [ -z ${VIASH_PAR_COPY_TAGS+x} ]; then + VIASH_PAR_COPY_TAGS="false" +fi +if [ -z ${VIASH_PAR_FILTER_FLAGS+x} ]; then + VIASH_PAR_FILTER_FLAGS="0" +fi +if [ -z ${VIASH_PAR_EXCL_FLAGS+x} ]; then + VIASH_PAR_EXCL_FLAGS="2304" +fi +if [ -z ${VIASH_PAR_INCL_FLAGS+x} ]; then + VIASH_PAR_INCL_FLAGS="0" +fi +if [ -z ${VIASH_PAR_EXCL_FLAGS_ALL+x} ]; then + VIASH_PAR_EXCL_FLAGS_ALL="0" +fi +if [ -z ${VIASH_PAR_CASAVA+x} ]; then + VIASH_PAR_CASAVA="false" +fi +if [ -z ${VIASH_PAR_COMPRESSION+x} ]; then + VIASH_PAR_COMPRESSION="0" +fi +if [ -z ${VIASH_PAR_BARCODE_TAG+x} ]; then + VIASH_PAR_BARCODE_TAG="BC" +fi +if [ -z ${VIASH_PAR_QUALITY_TAG+x} ]; then + VIASH_PAR_QUALITY_TAG="QT" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_SINGLETON" ] && [ ! -e "$VIASH_PAR_SINGLETON" ]; then + ViashError "Input file '$VIASH_PAR_SINGLETON' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_INDEX1" ] && [ ! -e "$VIASH_PAR_INDEX1" ]; then + ViashError "Input file '$VIASH_PAR_INDEX1' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_INDEX2" ] && [ ! -e "$VIASH_PAR_INDEX2" ]; then + ViashError "Input file '$VIASH_PAR_INDEX2' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_NO_SUFFIX" ]]; then + if ! [[ "$VIASH_PAR_NO_SUFFIX" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_suffix' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SUFFIX" ]]; then + if ! [[ "$VIASH_PAR_SUFFIX" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--suffix' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_USE_OQ" ]]; then + if ! [[ "$VIASH_PAR_USE_OQ" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--use_oq' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_COPY_TAGS" ]]; then + if ! [[ "$VIASH_PAR_COPY_TAGS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--copy_tags' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FILTER_FLAGS" ]]; then + if ! [[ "$VIASH_PAR_FILTER_FLAGS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--filter_flags' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_EXCL_FLAGS_ALL" ]]; then + if ! [[ "$VIASH_PAR_EXCL_FLAGS_ALL" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--excl_flags_all' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CASAVA" ]]; then + if ! [[ "$VIASH_PAR_CASAVA" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--casava' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_COMPRESSION" ]]; then + if ! [[ "$VIASH_PAR_COMPRESSION" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--compression' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi +if [ ! -z "$VIASH_PAR_READ1" ] && [ ! -d "$(dirname "$VIASH_PAR_READ1")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_READ1")" +fi +if [ ! -z "$VIASH_PAR_READ2" ] && [ ! -d "$(dirname "$VIASH_PAR_READ2")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_READ2")" +fi +if [ ! -z "$VIASH_PAR_OUTPUT_READS" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT_READS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT_READS")" +fi +if [ ! -z "$VIASH_PAR_OUTPUT_READS_BOTH" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT_READS_BOTH")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT_READS_BOTH")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_PAR_SINGLETON" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SINGLETON")" ) + VIASH_PAR_SINGLETON=$(ViashDockerAutodetectMount "$VIASH_PAR_SINGLETON") +fi +if [ ! -z "$VIASH_PAR_READ1" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_READ1")" ) + VIASH_PAR_READ1=$(ViashDockerAutodetectMount "$VIASH_PAR_READ1") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_READ1" ) +fi +if [ ! -z "$VIASH_PAR_READ2" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_READ2")" ) + VIASH_PAR_READ2=$(ViashDockerAutodetectMount "$VIASH_PAR_READ2") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_READ2" ) +fi +if [ ! -z "$VIASH_PAR_OUTPUT_READS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT_READS")" ) + VIASH_PAR_OUTPUT_READS=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT_READS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT_READS" ) +fi +if [ ! -z "$VIASH_PAR_OUTPUT_READS_BOTH" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT_READS_BOTH")" ) + VIASH_PAR_OUTPUT_READS_BOTH=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT_READS_BOTH") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT_READS_BOTH" ) +fi +if [ ! -z "$VIASH_PAR_INDEX1" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INDEX1")" ) + VIASH_PAR_INDEX1=$(ViashDockerAutodetectMount "$VIASH_PAR_INDEX1") +fi +if [ ! -z "$VIASH_PAR_INDEX2" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INDEX2")" ) + VIASH_PAR_INDEX2=$(ViashDockerAutodetectMount "$VIASH_PAR_INDEX2") +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-samtools_fastq-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_SUFFIX+x} ]; then echo "${VIASH_PAR_NO_SUFFIX}" | sed "s#'#'\"'\"'#g;s#.*#par_no_suffix='&'#" ; else echo "# par_no_suffix="; fi ) +$( if [ ! -z ${VIASH_PAR_SUFFIX+x} ]; then echo "${VIASH_PAR_SUFFIX}" | sed "s#'#'\"'\"'#g;s#.*#par_suffix='&'#" ; else echo "# par_suffix="; fi ) +$( if [ ! -z ${VIASH_PAR_USE_OQ+x} ]; then echo "${VIASH_PAR_USE_OQ}" | sed "s#'#'\"'\"'#g;s#.*#par_use_oq='&'#" ; else echo "# par_use_oq="; fi ) +$( if [ ! -z ${VIASH_PAR_SINGLETON+x} ]; then echo "${VIASH_PAR_SINGLETON}" | sed "s#'#'\"'\"'#g;s#.*#par_singleton='&'#" ; else echo "# par_singleton="; fi ) +$( if [ ! -z ${VIASH_PAR_COPY_TAGS+x} ]; then echo "${VIASH_PAR_COPY_TAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_copy_tags='&'#" ; else echo "# par_copy_tags="; fi ) +$( if [ ! -z ${VIASH_PAR_COPY_TAGS_LIST+x} ]; then echo "${VIASH_PAR_COPY_TAGS_LIST}" | sed "s#'#'\"'\"'#g;s#.*#par_copy_tags_list='&'#" ; else echo "# par_copy_tags_list="; fi ) +$( if [ ! -z ${VIASH_PAR_READ1+x} ]; then echo "${VIASH_PAR_READ1}" | sed "s#'#'\"'\"'#g;s#.*#par_read1='&'#" ; else echo "# par_read1="; fi ) +$( if [ ! -z ${VIASH_PAR_READ2+x} ]; then echo "${VIASH_PAR_READ2}" | sed "s#'#'\"'\"'#g;s#.*#par_read2='&'#" ; else echo "# par_read2="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_READS+x} ]; then echo "${VIASH_PAR_OUTPUT_READS}" | sed "s#'#'\"'\"'#g;s#.*#par_output_reads='&'#" ; else echo "# par_output_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_READS_BOTH+x} ]; then echo "${VIASH_PAR_OUTPUT_READS_BOTH}" | sed "s#'#'\"'\"'#g;s#.*#par_output_reads_both='&'#" ; else echo "# par_output_reads_both="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTER_FLAGS+x} ]; then echo "${VIASH_PAR_FILTER_FLAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_filter_flags='&'#" ; else echo "# par_filter_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCL_FLAGS+x} ]; then echo "${VIASH_PAR_EXCL_FLAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_excl_flags='&'#" ; else echo "# par_excl_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_INCL_FLAGS+x} ]; then echo "${VIASH_PAR_INCL_FLAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_incl_flags='&'#" ; else echo "# par_incl_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCL_FLAGS_ALL+x} ]; then echo "${VIASH_PAR_EXCL_FLAGS_ALL}" | sed "s#'#'\"'\"'#g;s#.*#par_excl_flags_all='&'#" ; else echo "# par_excl_flags_all="; fi ) +$( if [ ! -z ${VIASH_PAR_AUX_TAG+x} ]; then echo "${VIASH_PAR_AUX_TAG}" | sed "s#'#'\"'\"'#g;s#.*#par_aux_tag='&'#" ; else echo "# par_aux_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_AUX_TAG_FILE+x} ]; then echo "${VIASH_PAR_AUX_TAG_FILE}" | sed "s#'#'\"'\"'#g;s#.*#par_aux_tag_file='&'#" ; else echo "# par_aux_tag_file="; fi ) +$( if [ ! -z ${VIASH_PAR_CASAVA+x} ]; then echo "${VIASH_PAR_CASAVA}" | sed "s#'#'\"'\"'#g;s#.*#par_casava='&'#" ; else echo "# par_casava="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPRESSION+x} ]; then echo "${VIASH_PAR_COMPRESSION}" | sed "s#'#'\"'\"'#g;s#.*#par_compression='&'#" ; else echo "# par_compression="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX1+x} ]; then echo "${VIASH_PAR_INDEX1}" | sed "s#'#'\"'\"'#g;s#.*#par_index1='&'#" ; else echo "# par_index1="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX2+x} ]; then echo "${VIASH_PAR_INDEX2}" | sed "s#'#'\"'\"'#g;s#.*#par_index2='&'#" ; else echo "# par_index2="; fi ) +$( if [ ! -z ${VIASH_PAR_BARCODE_TAG+x} ]; then echo "${VIASH_PAR_BARCODE_TAG}" | sed "s#'#'\"'\"'#g;s#.*#par_barcode_tag='&'#" ; else echo "# par_barcode_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALITY_TAG+x} ]; then echo "${VIASH_PAR_QUALITY_TAG}" | sed "s#'#'\"'\"'#g;s#.*#par_quality_tag='&'#" ; else echo "# par_quality_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX_FORMAT+x} ]; then echo "${VIASH_PAR_INDEX_FORMAT}" | sed "s#'#'\"'\"'#g;s#.*#par_index_format='&'#" ; else echo "# par_index_format="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\$par_no_suffix" == "false" ]] && unset par_no_suffix +[[ "\$par_suffix" == "false" ]] && unset par_suffix +[[ "\$par_use_oq" == "false" ]] && unset par_use_oq +[[ "\$par_copy_tags" == "false" ]] && unset par_copy_tags +[[ "\$par_casava" == "false" ]] && unset par_casava + +samtools fastq \\ + \${par_no_suffix:+-n} \\ + \${par_suffix:+-N} \\ + \${par_use_oq:+-O} \\ + \${par_singleton:+-s "\$par_singleton"} \\ + \${par_copy_tags:+-t} \\ + \${par_copy_tags_list:+-T "\$par_copy_tags_list"} \\ + \${par_read1:+-1 "\$par_read1"} \\ + \${par_read2:+-2 "\$par_read2"} \\ + \${par_output_reads:+-o "\$par_output_reads"} \\ + \${par_output_reads_both:+-0 "\$par_output_reads_both"} \\ + \${par_filter_flags:+-f "\$par_filter_flags"} \\ + \${par_excl_flags:+-F "\$par_excl_flags"} \\ + \${par_incl_flags:+--rf "\$par_incl_flags"} \\ + \${par_excl_flags_all:+-G "\$par_excl_flags_all"} \\ + \${par_aux_tag:+-d "\$par_aux_tag"} \\ + \${par_aux_tag_file:+-D "\$par_aux_tag_file"} \\ + \${par_casava:+-i} \\ + \${par_compression:+-c "\$par_compression"} \\ + \${par_index1:+--i1 "\$par_index1"} \\ + \${par_index2:+--i2 "\$par_index2"} \\ + \${par_barcode_tag:+--barcode-tag "\$par_barcode_tag"} \\ + \${par_quality_tag:+--quality-tag "\$par_quality_tag"} \\ + \${par_index_format:+--index-format "\$par_index_format"} \\ + "\$par_input" \\ + > "\$par_output" +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_PAR_SINGLETON" ]; then + VIASH_PAR_SINGLETON=$(ViashDockerStripAutomount "$VIASH_PAR_SINGLETON") + fi + if [ ! -z "$VIASH_PAR_READ1" ]; then + VIASH_PAR_READ1=$(ViashDockerStripAutomount "$VIASH_PAR_READ1") + fi + if [ ! -z "$VIASH_PAR_READ2" ]; then + VIASH_PAR_READ2=$(ViashDockerStripAutomount "$VIASH_PAR_READ2") + fi + if [ ! -z "$VIASH_PAR_OUTPUT_READS" ]; then + VIASH_PAR_OUTPUT_READS=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT_READS") + fi + if [ ! -z "$VIASH_PAR_OUTPUT_READS_BOTH" ]; then + VIASH_PAR_OUTPUT_READS_BOTH=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT_READS_BOTH") + fi + if [ ! -z "$VIASH_PAR_INDEX1" ]; then + VIASH_PAR_INDEX1=$(ViashDockerStripAutomount "$VIASH_PAR_INDEX1") + fi + if [ ! -z "$VIASH_PAR_INDEX2" ]; then + VIASH_PAR_INDEX2=$(ViashDockerStripAutomount "$VIASH_PAR_INDEX2") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_READ1" ] && [ ! -e "$VIASH_PAR_READ1" ]; then + ViashError "Output file '$VIASH_PAR_READ1' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_READ2" ] && [ ! -e "$VIASH_PAR_READ2" ]; then + ViashError "Output file '$VIASH_PAR_READ2' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_OUTPUT_READS" ] && [ ! -e "$VIASH_PAR_OUTPUT_READS" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT_READS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_OUTPUT_READS_BOTH" ] && [ ! -e "$VIASH_PAR_OUTPUT_READS_BOTH" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT_READS_BOTH' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/samtools/samtools_flagstat/.config.vsh.yaml b/target/executable/samtools/samtools_flagstat/.config.vsh.yaml new file mode 100644 index 00000000..1972c8e7 --- /dev/null +++ b/target/executable/samtools/samtools_flagstat/.config.vsh.yaml @@ -0,0 +1,185 @@ +name: "samtools_flagstat" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--bam" + description: "BAM input files.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--bai" + description: "BAM index file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + description: "File containing samtools stats output.\n" + info: null + example: + - "output.flagstat" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Counts the number of alignments in SAM/BAM/CRAM files for each FLAG\ + \ type." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "stats" +- "mapping" +- "counts" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-flagstat.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_flagstat/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/samtools/samtools_flagstat" + executable: "target/executable/samtools/samtools_flagstat/samtools_flagstat" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/samtools/samtools_flagstat/samtools_flagstat b/target/executable/samtools/samtools_flagstat/samtools_flagstat new file mode 100755 index 00000000..3a1d0331 --- /dev/null +++ b/target/executable/samtools/samtools_flagstat/samtools_flagstat @@ -0,0 +1,1075 @@ +#!/usr/bin/env bash + +# samtools_flagstat main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="samtools_flagstat" +VIASH_META_FUNCTIONALITY_NAME="samtools_flagstat" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "samtools_flagstat main" + echo "" + echo "Counts the number of alignments in SAM/BAM/CRAM files for each FLAG type." + echo "" + echo "Inputs:" + echo " --bam" + echo " type: file, file must exist" + echo " BAM input files." + echo "" + echo " --bai" + echo " type: file, file must exist" + echo " BAM index file." + echo "" + echo "Outputs:" + echo " --output" + echo " type: file, required parameter, output, file must exist" + echo " example: output.flagstat" + echo " File containing samtools stats output." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 +ENTRYPOINT [] +RUN samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ +sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component samtools samtools_flagstat" +LABEL org.opencontainers.image.created="2024-06-24T08:36:40Z" +LABEL org.opencontainers.image.source="https://github.com/samtools/samtools" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "samtools_flagstat main" + exit + ;; + --bam) + [ -n "$VIASH_PAR_BAM" ] && ViashError Bad arguments for option \'--bam\': \'$VIASH_PAR_BAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bam. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bam=*) + [ -n "$VIASH_PAR_BAM" ] && ViashError Bad arguments for option \'--bam=*\': \'$VIASH_PAR_BAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAM=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bai) + [ -n "$VIASH_PAR_BAI" ] && ViashError Bad arguments for option \'--bai\': \'$VIASH_PAR_BAI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAI="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bai. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bai=*) + [ -n "$VIASH_PAR_BAI" ] && ViashError Bad arguments for option \'--bai=*\': \'$VIASH_PAR_BAI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAI=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/samtools/samtools_flagstat:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_BAM" ] && [ ! -e "$VIASH_PAR_BAM" ]; then + ViashError "Input file '$VIASH_PAR_BAM' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_BAI" ] && [ ! -e "$VIASH_PAR_BAI" ]; then + ViashError "Input file '$VIASH_PAR_BAI' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_BAM" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_BAM")" ) + VIASH_PAR_BAM=$(ViashDockerAutodetectMount "$VIASH_PAR_BAM") +fi +if [ ! -z "$VIASH_PAR_BAI" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_BAI")" ) + VIASH_PAR_BAI=$(ViashDockerAutodetectMount "$VIASH_PAR_BAI") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-samtools_flagstat-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_BAM+x} ]; then echo "${VIASH_PAR_BAM}" | sed "s#'#'\"'\"'#g;s#.*#par_bam='&'#" ; else echo "# par_bam="; fi ) +$( if [ ! -z ${VIASH_PAR_BAI+x} ]; then echo "${VIASH_PAR_BAI}" | sed "s#'#'\"'\"'#g;s#.*#par_bai='&'#" ; else echo "# par_bai="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +samtools flagstat \\ + "\$par_bam" \\ + > "\$par_output" + +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_BAM" ]; then + VIASH_PAR_BAM=$(ViashDockerStripAutomount "$VIASH_PAR_BAM") + fi + if [ ! -z "$VIASH_PAR_BAI" ]; then + VIASH_PAR_BAI=$(ViashDockerStripAutomount "$VIASH_PAR_BAI") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/samtools/samtools_idxstats/.config.vsh.yaml b/target/executable/samtools/samtools_idxstats/.config.vsh.yaml new file mode 100644 index 00000000..f12dbeb1 --- /dev/null +++ b/target/executable/samtools/samtools_idxstats/.config.vsh.yaml @@ -0,0 +1,195 @@ +name: "samtools_idxstats" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--bam" + description: "BAM input file." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--bai" + description: "BAM index file." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--fasta" + description: "Reference file the CRAM was created with (optional)." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + description: "File containing samtools stats output in tab-delimited format.\n" + info: null + example: + - "output.idxstats" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Reports alignment summary statistics for a BAM file." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "stats" +- "mapping" +- "counts" +- "chromosome" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-idxstats.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_idxstats/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/samtools/samtools_idxstats" + executable: "target/executable/samtools/samtools_idxstats/samtools_idxstats" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/samtools/samtools_idxstats/samtools_idxstats b/target/executable/samtools/samtools_idxstats/samtools_idxstats new file mode 100755 index 00000000..8a670f6c --- /dev/null +++ b/target/executable/samtools/samtools_idxstats/samtools_idxstats @@ -0,0 +1,1099 @@ +#!/usr/bin/env bash + +# samtools_idxstats main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="samtools_idxstats" +VIASH_META_FUNCTIONALITY_NAME="samtools_idxstats" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "samtools_idxstats main" + echo "" + echo "Reports alignment summary statistics for a BAM file." + echo "" + echo "Inputs:" + echo " --bam" + echo " type: file, file must exist" + echo " BAM input file." + echo "" + echo " --bai" + echo " type: file, file must exist" + echo " BAM index file." + echo "" + echo " --fasta" + echo " type: file, file must exist" + echo " Reference file the CRAM was created with (optional)." + echo "" + echo "Outputs:" + echo " --output" + echo " type: file, required parameter, output, file must exist" + echo " example: output.idxstats" + echo " File containing samtools stats output in tab-delimited format." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 +ENTRYPOINT [] +RUN samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ +sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component samtools samtools_idxstats" +LABEL org.opencontainers.image.created="2024-06-24T08:36:39Z" +LABEL org.opencontainers.image.source="https://github.com/samtools/samtools" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "samtools_idxstats main" + exit + ;; + --bam) + [ -n "$VIASH_PAR_BAM" ] && ViashError Bad arguments for option \'--bam\': \'$VIASH_PAR_BAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bam. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bam=*) + [ -n "$VIASH_PAR_BAM" ] && ViashError Bad arguments for option \'--bam=*\': \'$VIASH_PAR_BAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAM=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bai) + [ -n "$VIASH_PAR_BAI" ] && ViashError Bad arguments for option \'--bai\': \'$VIASH_PAR_BAI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAI="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bai. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bai=*) + [ -n "$VIASH_PAR_BAI" ] && ViashError Bad arguments for option \'--bai=*\': \'$VIASH_PAR_BAI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAI=$(ViashRemoveFlags "$1") + shift 1 + ;; + --fasta) + [ -n "$VIASH_PAR_FASTA" ] && ViashError Bad arguments for option \'--fasta\': \'$VIASH_PAR_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FASTA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --fasta. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fasta=*) + [ -n "$VIASH_PAR_FASTA" ] && ViashError Bad arguments for option \'--fasta=*\': \'$VIASH_PAR_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FASTA=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/samtools/samtools_idxstats:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_BAM" ] && [ ! -e "$VIASH_PAR_BAM" ]; then + ViashError "Input file '$VIASH_PAR_BAM' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_BAI" ] && [ ! -e "$VIASH_PAR_BAI" ]; then + ViashError "Input file '$VIASH_PAR_BAI' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_FASTA" ] && [ ! -e "$VIASH_PAR_FASTA" ]; then + ViashError "Input file '$VIASH_PAR_FASTA' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_BAM" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_BAM")" ) + VIASH_PAR_BAM=$(ViashDockerAutodetectMount "$VIASH_PAR_BAM") +fi +if [ ! -z "$VIASH_PAR_BAI" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_BAI")" ) + VIASH_PAR_BAI=$(ViashDockerAutodetectMount "$VIASH_PAR_BAI") +fi +if [ ! -z "$VIASH_PAR_FASTA" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FASTA")" ) + VIASH_PAR_FASTA=$(ViashDockerAutodetectMount "$VIASH_PAR_FASTA") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-samtools_idxstats-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_BAM+x} ]; then echo "${VIASH_PAR_BAM}" | sed "s#'#'\"'\"'#g;s#.*#par_bam='&'#" ; else echo "# par_bam="; fi ) +$( if [ ! -z ${VIASH_PAR_BAI+x} ]; then echo "${VIASH_PAR_BAI}" | sed "s#'#'\"'\"'#g;s#.*#par_bai='&'#" ; else echo "# par_bai="; fi ) +$( if [ ! -z ${VIASH_PAR_FASTA+x} ]; then echo "${VIASH_PAR_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_fasta='&'#" ; else echo "# par_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +samtools idxstats "\$par_bam" > "\$par_output" +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_BAM" ]; then + VIASH_PAR_BAM=$(ViashDockerStripAutomount "$VIASH_PAR_BAM") + fi + if [ ! -z "$VIASH_PAR_BAI" ]; then + VIASH_PAR_BAI=$(ViashDockerStripAutomount "$VIASH_PAR_BAI") + fi + if [ ! -z "$VIASH_PAR_FASTA" ]; then + VIASH_PAR_FASTA=$(ViashDockerStripAutomount "$VIASH_PAR_FASTA") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/samtools/samtools_index/.config.vsh.yaml b/target/executable/samtools/samtools_index/.config.vsh.yaml new file mode 100644 index 00000000..ce6d9daa --- /dev/null +++ b/target/executable/samtools/samtools_index/.config.vsh.yaml @@ -0,0 +1,201 @@ +name: "samtools_index" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "Input file name" + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output file name" + info: null + example: + - "out.bam.bai" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Options" + arguments: + - type: "boolean_true" + name: "--bai" + alternatives: + - "-b" + description: "Generate BAM index" + info: null + direction: "input" + - type: "boolean_true" + name: "--csi" + alternatives: + - "-c" + description: "Create a CSI index for BAM files instead of the traditional BAI\ + \ \nindex. This will be required for genomes with larger chromosome \nsizes.\n" + info: null + direction: "input" + - type: "integer" + name: "--min_shift" + alternatives: + - "-m" + description: "Create a CSI index, with a minimum interval size of 2^INT.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Index SAM/BAM/CRAM files." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "index" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-index.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_index/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/samtools/samtools_index" + executable: "target/executable/samtools/samtools_index/samtools_index" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/samtools/samtools_index/samtools_index b/target/executable/samtools/samtools_index/samtools_index new file mode 100755 index 00000000..a12d81c9 --- /dev/null +++ b/target/executable/samtools/samtools_index/samtools_index @@ -0,0 +1,1146 @@ +#!/usr/bin/env bash + +# samtools_index main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="samtools_index" +VIASH_META_FUNCTIONALITY_NAME="samtools_index" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "samtools_index main" + echo "" + echo "Index SAM/BAM/CRAM files." + echo "" + echo "Inputs:" + echo " --input" + echo " type: file, required parameter, file must exist" + echo " Input file name" + echo "" + echo "Outputs:" + echo " -o, --output" + echo " type: file, required parameter, output, file must exist" + echo " example: out.bam.bai" + echo " Output file name" + echo "" + echo "Options:" + echo " -b, --bai" + echo " type: boolean_true" + echo " Generate BAM index" + echo "" + echo " -c, --csi" + echo " type: boolean_true" + echo " Create a CSI index for BAM files instead of the traditional BAI" + echo " index. This will be required for genomes with larger chromosome" + echo " sizes." + echo "" + echo " -m, --min_shift" + echo " type: integer" + echo " Create a CSI index, with a minimum interval size of 2^INT." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 +ENTRYPOINT [] +RUN samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ +sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component samtools samtools_index" +LABEL org.opencontainers.image.created="2024-06-24T08:36:40Z" +LABEL org.opencontainers.image.source="https://github.com/samtools/samtools" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "samtools_index main" + exit + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bai) + [ -n "$VIASH_PAR_BAI" ] && ViashError Bad arguments for option \'--bai\': \'$VIASH_PAR_BAI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAI=true + shift 1 + ;; + -b) + [ -n "$VIASH_PAR_BAI" ] && ViashError Bad arguments for option \'-b\': \'$VIASH_PAR_BAI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAI=true + shift 1 + ;; + --csi) + [ -n "$VIASH_PAR_CSI" ] && ViashError Bad arguments for option \'--csi\': \'$VIASH_PAR_CSI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CSI=true + shift 1 + ;; + -c) + [ -n "$VIASH_PAR_CSI" ] && ViashError Bad arguments for option \'-c\': \'$VIASH_PAR_CSI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CSI=true + shift 1 + ;; + --min_shift) + [ -n "$VIASH_PAR_MIN_SHIFT" ] && ViashError Bad arguments for option \'--min_shift\': \'$VIASH_PAR_MIN_SHIFT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_SHIFT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_shift. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_shift=*) + [ -n "$VIASH_PAR_MIN_SHIFT" ] && ViashError Bad arguments for option \'--min_shift=*\': \'$VIASH_PAR_MIN_SHIFT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_SHIFT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -m) + [ -n "$VIASH_PAR_MIN_SHIFT" ] && ViashError Bad arguments for option \'-m\': \'$VIASH_PAR_MIN_SHIFT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_SHIFT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -m. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/samtools/samtools_index:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_BAI+x} ]; then + VIASH_PAR_BAI="false" +fi +if [ -z ${VIASH_PAR_CSI+x} ]; then + VIASH_PAR_CSI="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_BAI" ]]; then + if ! [[ "$VIASH_PAR_BAI" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--bai' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CSI" ]]; then + if ! [[ "$VIASH_PAR_CSI" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--csi' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_SHIFT" ]]; then + if ! [[ "$VIASH_PAR_MIN_SHIFT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_shift' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-samtools_index-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_BAI+x} ]; then echo "${VIASH_PAR_BAI}" | sed "s#'#'\"'\"'#g;s#.*#par_bai='&'#" ; else echo "# par_bai="; fi ) +$( if [ ! -z ${VIASH_PAR_CSI+x} ]; then echo "${VIASH_PAR_CSI}" | sed "s#'#'\"'\"'#g;s#.*#par_csi='&'#" ; else echo "# par_csi="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_SHIFT+x} ]; then echo "${VIASH_PAR_MIN_SHIFT}" | sed "s#'#'\"'\"'#g;s#.*#par_min_shift='&'#" ; else echo "# par_min_shift="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e +[[ "\$par_multiple" == "false" ]] && unset par_multiple +[[ "\$par_bai" == "false" ]] && unset par_bai +[[ "\$par_csi" == "false" ]] && unset par_csi +[[ "\$par_multiple" == "false" ]] && unset par_multiple + +samtools index \\ + "\$par_input" \\ + \${par_csi:+-c} \\ + \${par_bai:+-b} \\ + \${par_min_shift:+-m "par_min_shift"} \\ + \${par_multiple:+-M} \\ + -o "\$par_output" +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/samtools/samtools_sort/.config.vsh.yaml b/target/executable/samtools/samtools_sort/.config.vsh.yaml new file mode 100644 index 00000000..3cb18e42 --- /dev/null +++ b/target/executable/samtools/samtools_sort/.config.vsh.yaml @@ -0,0 +1,344 @@ +name: "samtools_sort" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "SAM/BAM/CRAM input file." + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + description: "Write final output to file.\n" + info: null + example: + - "out.bam" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt" + alternatives: + - "-O" + description: "Specify output format (SAM, BAM, CRAM).\n" + info: null + example: + - "BAM" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt_option" + description: "Specify a single output file format option in the form\nof OPTION\ + \ or OPTION=VALUE.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reference" + description: "Reference sequence FASTA FILE.\n" + info: null + example: + - "ref.fa" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--write_index" + description: "Automatically index the output files.\n" + info: null + direction: "input" + - type: "string" + name: "--prefix" + alternatives: + - "-T" + description: "Write temporary files to PREFIX.nnnn.bam.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_PG" + description: "Do not add a PG line.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--template_coordinate" + description: "Sort by template-coordinate.\n" + info: null + direction: "input" + - type: "string" + name: "--input_fmt_option" + description: "Specify a single input file format option in the form\nof OPTION\ + \ or OPTION=VALUE.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Options" + arguments: + - type: "integer" + name: "--compression" + alternatives: + - "-l" + description: "Set compression level, from 0 (uncompressed) to 9 (best).\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--uncompressed" + alternatives: + - "-u" + description: "Output uncompressed data (equivalent to --compression 0).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--minimiser" + alternatives: + - "-M" + description: "Use minimiser for clustering unaligned/unplaced reads.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--not_reverse" + alternatives: + - "-R" + description: "Do not use reverse strand (only compatible with --minimiser)\n" + info: null + direction: "input" + - type: "integer" + name: "--kmer_size" + alternatives: + - "-K" + description: "Kmer size to use for minimiser.\n" + info: null + example: + - 20 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--order" + alternatives: + - "-I" + description: "Order minimisers by their position in FILE FASTA.\n" + info: null + example: + - "ref.fa" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--window" + alternatives: + - "-w" + description: "Window size for minimiser INDEXING VIA --order REF.FA.\n" + info: null + example: + - 100 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--homopolymers" + alternatives: + - "-H" + description: "Squash homopolymers when computing minimiser.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--natural_sort" + alternatives: + - "-n" + description: "Sort by read name (natural): cannot be used with samtools index.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--ascii_sort" + alternatives: + - "-N" + description: "Sort by read name (ASCII): cannot be used with samtools index.\n" + info: null + direction: "input" + - type: "string" + name: "--tag" + alternatives: + - "-t" + description: "Sort by value of TAG. Uses position as secondary index \n(or read\ + \ name if --natural_sort is set).\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Sort SAM/BAM/CRAM file." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "sort" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-sort.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_sort/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/samtools/samtools_sort" + executable: "target/executable/samtools/samtools_sort/samtools_sort" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/samtools/samtools_sort/samtools_sort b/target/executable/samtools/samtools_sort/samtools_sort new file mode 100755 index 00000000..c967b262 --- /dev/null +++ b/target/executable/samtools/samtools_sort/samtools_sort @@ -0,0 +1,1542 @@ +#!/usr/bin/env bash + +# samtools_sort main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="samtools_sort" +VIASH_META_FUNCTIONALITY_NAME="samtools_sort" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "samtools_sort main" + echo "" + echo "Sort SAM/BAM/CRAM file." + echo "" + echo "Inputs:" + echo " --input" + echo " type: file, required parameter, file must exist" + echo " SAM/BAM/CRAM input file." + echo "" + echo "Outputs:" + echo " --output" + echo " type: file, required parameter, output, file must exist" + echo " example: out.bam" + echo " Write final output to file." + echo "" + echo " -O, --output_fmt" + echo " type: string" + echo " example: BAM" + echo " Specify output format (SAM, BAM, CRAM)." + echo "" + echo " --output_fmt_option" + echo " type: string" + echo " Specify a single output file format option in the form" + echo " of OPTION or OPTION=VALUE." + echo "" + echo " --reference" + echo " type: file, file must exist" + echo " example: ref.fa" + echo " Reference sequence FASTA FILE." + echo "" + echo " --write_index" + echo " type: boolean_true" + echo " Automatically index the output files." + echo "" + echo " -T, --prefix" + echo " type: string" + echo " Write temporary files to PREFIX.nnnn.bam." + echo "" + echo " --no_PG" + echo " type: boolean_true" + echo " Do not add a PG line." + echo "" + echo " --template_coordinate" + echo " type: boolean_true" + echo " Sort by template-coordinate." + echo "" + echo " --input_fmt_option" + echo " type: string" + echo " Specify a single input file format option in the form" + echo " of OPTION or OPTION=VALUE." + echo "" + echo "Options:" + echo " -l, --compression" + echo " type: integer" + echo " default: 0" + echo " Set compression level, from 0 (uncompressed) to 9 (best)." + echo "" + echo " -u, --uncompressed" + echo " type: boolean_true" + echo " Output uncompressed data (equivalent to --compression 0)." + echo "" + echo " -M, --minimiser" + echo " type: boolean_true" + echo " Use minimiser for clustering unaligned/unplaced reads." + echo "" + echo " -R, --not_reverse" + echo " type: boolean_true" + echo " Do not use reverse strand (only compatible with --minimiser)" + echo "" + echo " -K, --kmer_size" + echo " type: integer" + echo " example: 20" + echo " Kmer size to use for minimiser." + echo "" + echo " -I, --order" + echo " type: file, file must exist" + echo " example: ref.fa" + echo " Order minimisers by their position in FILE FASTA." + echo "" + echo " -w, --window" + echo " type: integer" + echo " example: 100" + echo " Window size for minimiser INDEXING VIA --order REF.FA." + echo "" + echo " -H, --homopolymers" + echo " type: boolean_true" + echo " Squash homopolymers when computing minimiser." + echo "" + echo " -n, --natural_sort" + echo " type: boolean_true" + echo " Sort by read name (natural): cannot be used with samtools index." + echo "" + echo " -N, --ascii_sort" + echo " type: boolean_true" + echo " Sort by read name (ASCII): cannot be used with samtools index." + echo "" + echo " -t, --tag" + echo " type: string" + echo " Sort by value of TAG. Uses position as secondary index" + echo " (or read name if --natural_sort is set)." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 +ENTRYPOINT [] +RUN samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ +sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component samtools samtools_sort" +LABEL org.opencontainers.image.created="2024-06-24T08:36:39Z" +LABEL org.opencontainers.image.source="https://github.com/samtools/samtools" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "samtools_sort main" + exit + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output_fmt) + [ -n "$VIASH_PAR_OUTPUT_FMT" ] && ViashError Bad arguments for option \'--output_fmt\': \'$VIASH_PAR_OUTPUT_FMT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_fmt. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_fmt=*) + [ -n "$VIASH_PAR_OUTPUT_FMT" ] && ViashError Bad arguments for option \'--output_fmt=*\': \'$VIASH_PAR_OUTPUT_FMT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -O) + [ -n "$VIASH_PAR_OUTPUT_FMT" ] && ViashError Bad arguments for option \'-O\': \'$VIASH_PAR_OUTPUT_FMT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -O. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_fmt_option) + [ -n "$VIASH_PAR_OUTPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--output_fmt_option\': \'$VIASH_PAR_OUTPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT_OPTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_fmt_option. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_fmt_option=*) + [ -n "$VIASH_PAR_OUTPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--output_fmt_option=*\': \'$VIASH_PAR_OUTPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT_OPTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --reference) + [ -n "$VIASH_PAR_REFERENCE" ] && ViashError Bad arguments for option \'--reference\': \'$VIASH_PAR_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REFERENCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --reference. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --reference=*) + [ -n "$VIASH_PAR_REFERENCE" ] && ViashError Bad arguments for option \'--reference=*\': \'$VIASH_PAR_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REFERENCE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --write_index) + [ -n "$VIASH_PAR_WRITE_INDEX" ] && ViashError Bad arguments for option \'--write_index\': \'$VIASH_PAR_WRITE_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WRITE_INDEX=true + shift 1 + ;; + --prefix) + [ -n "$VIASH_PAR_PREFIX" ] && ViashError Bad arguments for option \'--prefix\': \'$VIASH_PAR_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --prefix. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --prefix=*) + [ -n "$VIASH_PAR_PREFIX" ] && ViashError Bad arguments for option \'--prefix=*\': \'$VIASH_PAR_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PREFIX=$(ViashRemoveFlags "$1") + shift 1 + ;; + -T) + [ -n "$VIASH_PAR_PREFIX" ] && ViashError Bad arguments for option \'-T\': \'$VIASH_PAR_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -T. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --no_PG) + [ -n "$VIASH_PAR_NO_PG" ] && ViashError Bad arguments for option \'--no_PG\': \'$VIASH_PAR_NO_PG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_PG=true + shift 1 + ;; + --template_coordinate) + [ -n "$VIASH_PAR_TEMPLATE_COORDINATE" ] && ViashError Bad arguments for option \'--template_coordinate\': \'$VIASH_PAR_TEMPLATE_COORDINATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TEMPLATE_COORDINATE=true + shift 1 + ;; + --input_fmt_option) + [ -n "$VIASH_PAR_INPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--input_fmt_option\': \'$VIASH_PAR_INPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_FMT_OPTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input_fmt_option. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input_fmt_option=*) + [ -n "$VIASH_PAR_INPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--input_fmt_option=*\': \'$VIASH_PAR_INPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_FMT_OPTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --compression) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'--compression\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --compression. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --compression=*) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'--compression=*\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION=$(ViashRemoveFlags "$1") + shift 1 + ;; + -l) + [ -n "$VIASH_PAR_COMPRESSION" ] && ViashError Bad arguments for option \'-l\': \'$VIASH_PAR_COMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COMPRESSION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -l. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --uncompressed) + [ -n "$VIASH_PAR_UNCOMPRESSED" ] && ViashError Bad arguments for option \'--uncompressed\': \'$VIASH_PAR_UNCOMPRESSED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNCOMPRESSED=true + shift 1 + ;; + -u) + [ -n "$VIASH_PAR_UNCOMPRESSED" ] && ViashError Bad arguments for option \'-u\': \'$VIASH_PAR_UNCOMPRESSED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNCOMPRESSED=true + shift 1 + ;; + --minimiser) + [ -n "$VIASH_PAR_MINIMISER" ] && ViashError Bad arguments for option \'--minimiser\': \'$VIASH_PAR_MINIMISER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MINIMISER=true + shift 1 + ;; + -M) + [ -n "$VIASH_PAR_MINIMISER" ] && ViashError Bad arguments for option \'-M\': \'$VIASH_PAR_MINIMISER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MINIMISER=true + shift 1 + ;; + --not_reverse) + [ -n "$VIASH_PAR_NOT_REVERSE" ] && ViashError Bad arguments for option \'--not_reverse\': \'$VIASH_PAR_NOT_REVERSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NOT_REVERSE=true + shift 1 + ;; + -R) + [ -n "$VIASH_PAR_NOT_REVERSE" ] && ViashError Bad arguments for option \'-R\': \'$VIASH_PAR_NOT_REVERSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NOT_REVERSE=true + shift 1 + ;; + --kmer_size) + [ -n "$VIASH_PAR_KMER_SIZE" ] && ViashError Bad arguments for option \'--kmer_size\': \'$VIASH_PAR_KMER_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KMER_SIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --kmer_size. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --kmer_size=*) + [ -n "$VIASH_PAR_KMER_SIZE" ] && ViashError Bad arguments for option \'--kmer_size=*\': \'$VIASH_PAR_KMER_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KMER_SIZE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -K) + [ -n "$VIASH_PAR_KMER_SIZE" ] && ViashError Bad arguments for option \'-K\': \'$VIASH_PAR_KMER_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KMER_SIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -K. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --order) + [ -n "$VIASH_PAR_ORDER" ] && ViashError Bad arguments for option \'--order\': \'$VIASH_PAR_ORDER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ORDER="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --order. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --order=*) + [ -n "$VIASH_PAR_ORDER" ] && ViashError Bad arguments for option \'--order=*\': \'$VIASH_PAR_ORDER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ORDER=$(ViashRemoveFlags "$1") + shift 1 + ;; + -I) + [ -n "$VIASH_PAR_ORDER" ] && ViashError Bad arguments for option \'-I\': \'$VIASH_PAR_ORDER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ORDER="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -I. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --window) + [ -n "$VIASH_PAR_WINDOW" ] && ViashError Bad arguments for option \'--window\': \'$VIASH_PAR_WINDOW\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINDOW="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --window. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --window=*) + [ -n "$VIASH_PAR_WINDOW" ] && ViashError Bad arguments for option \'--window=*\': \'$VIASH_PAR_WINDOW\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINDOW=$(ViashRemoveFlags "$1") + shift 1 + ;; + -w) + [ -n "$VIASH_PAR_WINDOW" ] && ViashError Bad arguments for option \'-w\': \'$VIASH_PAR_WINDOW\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINDOW="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -w. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --homopolymers) + [ -n "$VIASH_PAR_HOMOPOLYMERS" ] && ViashError Bad arguments for option \'--homopolymers\': \'$VIASH_PAR_HOMOPOLYMERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HOMOPOLYMERS=true + shift 1 + ;; + -H) + [ -n "$VIASH_PAR_HOMOPOLYMERS" ] && ViashError Bad arguments for option \'-H\': \'$VIASH_PAR_HOMOPOLYMERS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HOMOPOLYMERS=true + shift 1 + ;; + --natural_sort) + [ -n "$VIASH_PAR_NATURAL_SORT" ] && ViashError Bad arguments for option \'--natural_sort\': \'$VIASH_PAR_NATURAL_SORT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NATURAL_SORT=true + shift 1 + ;; + -n) + [ -n "$VIASH_PAR_NATURAL_SORT" ] && ViashError Bad arguments for option \'-n\': \'$VIASH_PAR_NATURAL_SORT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NATURAL_SORT=true + shift 1 + ;; + --ascii_sort) + [ -n "$VIASH_PAR_ASCII_SORT" ] && ViashError Bad arguments for option \'--ascii_sort\': \'$VIASH_PAR_ASCII_SORT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ASCII_SORT=true + shift 1 + ;; + -N) + [ -n "$VIASH_PAR_ASCII_SORT" ] && ViashError Bad arguments for option \'-N\': \'$VIASH_PAR_ASCII_SORT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ASCII_SORT=true + shift 1 + ;; + --tag) + [ -n "$VIASH_PAR_TAG" ] && ViashError Bad arguments for option \'--tag\': \'$VIASH_PAR_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --tag. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tag=*) + [ -n "$VIASH_PAR_TAG" ] && ViashError Bad arguments for option \'--tag=*\': \'$VIASH_PAR_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + -t) + [ -n "$VIASH_PAR_TAG" ] && ViashError Bad arguments for option \'-t\': \'$VIASH_PAR_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -t. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/samtools/samtools_sort:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_WRITE_INDEX+x} ]; then + VIASH_PAR_WRITE_INDEX="false" +fi +if [ -z ${VIASH_PAR_NO_PG+x} ]; then + VIASH_PAR_NO_PG="false" +fi +if [ -z ${VIASH_PAR_TEMPLATE_COORDINATE+x} ]; then + VIASH_PAR_TEMPLATE_COORDINATE="false" +fi +if [ -z ${VIASH_PAR_COMPRESSION+x} ]; then + VIASH_PAR_COMPRESSION="0" +fi +if [ -z ${VIASH_PAR_UNCOMPRESSED+x} ]; then + VIASH_PAR_UNCOMPRESSED="false" +fi +if [ -z ${VIASH_PAR_MINIMISER+x} ]; then + VIASH_PAR_MINIMISER="false" +fi +if [ -z ${VIASH_PAR_NOT_REVERSE+x} ]; then + VIASH_PAR_NOT_REVERSE="false" +fi +if [ -z ${VIASH_PAR_HOMOPOLYMERS+x} ]; then + VIASH_PAR_HOMOPOLYMERS="false" +fi +if [ -z ${VIASH_PAR_NATURAL_SORT+x} ]; then + VIASH_PAR_NATURAL_SORT="false" +fi +if [ -z ${VIASH_PAR_ASCII_SORT+x} ]; then + VIASH_PAR_ASCII_SORT="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REFERENCE" ] && [ ! -e "$VIASH_PAR_REFERENCE" ]; then + ViashError "Input file '$VIASH_PAR_REFERENCE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_ORDER" ] && [ ! -e "$VIASH_PAR_ORDER" ]; then + ViashError "Input file '$VIASH_PAR_ORDER' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_WRITE_INDEX" ]]; then + if ! [[ "$VIASH_PAR_WRITE_INDEX" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--write_index' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_PG" ]]; then + if ! [[ "$VIASH_PAR_NO_PG" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_PG' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TEMPLATE_COORDINATE" ]]; then + if ! [[ "$VIASH_PAR_TEMPLATE_COORDINATE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--template_coordinate' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_COMPRESSION" ]]; then + if ! [[ "$VIASH_PAR_COMPRESSION" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--compression' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_UNCOMPRESSED" ]]; then + if ! [[ "$VIASH_PAR_UNCOMPRESSED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--uncompressed' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MINIMISER" ]]; then + if ! [[ "$VIASH_PAR_MINIMISER" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--minimiser' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NOT_REVERSE" ]]; then + if ! [[ "$VIASH_PAR_NOT_REVERSE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--not_reverse' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_KMER_SIZE" ]]; then + if ! [[ "$VIASH_PAR_KMER_SIZE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--kmer_size' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WINDOW" ]]; then + if ! [[ "$VIASH_PAR_WINDOW" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--window' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_HOMOPOLYMERS" ]]; then + if ! [[ "$VIASH_PAR_HOMOPOLYMERS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--homopolymers' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NATURAL_SORT" ]]; then + if ! [[ "$VIASH_PAR_NATURAL_SORT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--natural_sort' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ASCII_SORT" ]]; then + if ! [[ "$VIASH_PAR_ASCII_SORT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--ascii_sort' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_PAR_REFERENCE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REFERENCE")" ) + VIASH_PAR_REFERENCE=$(ViashDockerAutodetectMount "$VIASH_PAR_REFERENCE") +fi +if [ ! -z "$VIASH_PAR_ORDER" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_ORDER")" ) + VIASH_PAR_ORDER=$(ViashDockerAutodetectMount "$VIASH_PAR_ORDER") +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-samtools_sort-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT}" | sed "s#'#'\"'\"'#g;s#.*#par_output_fmt='&'#" ; else echo "# par_output_fmt="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT_OPTION}" | sed "s#'#'\"'\"'#g;s#.*#par_output_fmt_option='&'#" ; else echo "# par_output_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_REFERENCE+x} ]; then echo "${VIASH_PAR_REFERENCE}" | sed "s#'#'\"'\"'#g;s#.*#par_reference='&'#" ; else echo "# par_reference="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_INDEX+x} ]; then echo "${VIASH_PAR_WRITE_INDEX}" | sed "s#'#'\"'\"'#g;s#.*#par_write_index='&'#" ; else echo "# par_write_index="; fi ) +$( if [ ! -z ${VIASH_PAR_PREFIX+x} ]; then echo "${VIASH_PAR_PREFIX}" | sed "s#'#'\"'\"'#g;s#.*#par_prefix='&'#" ; else echo "# par_prefix="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_PG+x} ]; then echo "${VIASH_PAR_NO_PG}" | sed "s#'#'\"'\"'#g;s#.*#par_no_PG='&'#" ; else echo "# par_no_PG="; fi ) +$( if [ ! -z ${VIASH_PAR_TEMPLATE_COORDINATE+x} ]; then echo "${VIASH_PAR_TEMPLATE_COORDINATE}" | sed "s#'#'\"'\"'#g;s#.*#par_template_coordinate='&'#" ; else echo "# par_template_coordinate="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_INPUT_FMT_OPTION}" | sed "s#'#'\"'\"'#g;s#.*#par_input_fmt_option='&'#" ; else echo "# par_input_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPRESSION+x} ]; then echo "${VIASH_PAR_COMPRESSION}" | sed "s#'#'\"'\"'#g;s#.*#par_compression='&'#" ; else echo "# par_compression="; fi ) +$( if [ ! -z ${VIASH_PAR_UNCOMPRESSED+x} ]; then echo "${VIASH_PAR_UNCOMPRESSED}" | sed "s#'#'\"'\"'#g;s#.*#par_uncompressed='&'#" ; else echo "# par_uncompressed="; fi ) +$( if [ ! -z ${VIASH_PAR_MINIMISER+x} ]; then echo "${VIASH_PAR_MINIMISER}" | sed "s#'#'\"'\"'#g;s#.*#par_minimiser='&'#" ; else echo "# par_minimiser="; fi ) +$( if [ ! -z ${VIASH_PAR_NOT_REVERSE+x} ]; then echo "${VIASH_PAR_NOT_REVERSE}" | sed "s#'#'\"'\"'#g;s#.*#par_not_reverse='&'#" ; else echo "# par_not_reverse="; fi ) +$( if [ ! -z ${VIASH_PAR_KMER_SIZE+x} ]; then echo "${VIASH_PAR_KMER_SIZE}" | sed "s#'#'\"'\"'#g;s#.*#par_kmer_size='&'#" ; else echo "# par_kmer_size="; fi ) +$( if [ ! -z ${VIASH_PAR_ORDER+x} ]; then echo "${VIASH_PAR_ORDER}" | sed "s#'#'\"'\"'#g;s#.*#par_order='&'#" ; else echo "# par_order="; fi ) +$( if [ ! -z ${VIASH_PAR_WINDOW+x} ]; then echo "${VIASH_PAR_WINDOW}" | sed "s#'#'\"'\"'#g;s#.*#par_window='&'#" ; else echo "# par_window="; fi ) +$( if [ ! -z ${VIASH_PAR_HOMOPOLYMERS+x} ]; then echo "${VIASH_PAR_HOMOPOLYMERS}" | sed "s#'#'\"'\"'#g;s#.*#par_homopolymers='&'#" ; else echo "# par_homopolymers="; fi ) +$( if [ ! -z ${VIASH_PAR_NATURAL_SORT+x} ]; then echo "${VIASH_PAR_NATURAL_SORT}" | sed "s#'#'\"'\"'#g;s#.*#par_natural_sort='&'#" ; else echo "# par_natural_sort="; fi ) +$( if [ ! -z ${VIASH_PAR_ASCII_SORT+x} ]; then echo "${VIASH_PAR_ASCII_SORT}" | sed "s#'#'\"'\"'#g;s#.*#par_ascii_sort='&'#" ; else echo "# par_ascii_sort="; fi ) +$( if [ ! -z ${VIASH_PAR_TAG+x} ]; then echo "${VIASH_PAR_TAG}" | sed "s#'#'\"'\"'#g;s#.*#par_tag='&'#" ; else echo "# par_tag="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\$par_uncompressed" == "false" ]] && unset par_uncompressed +[[ "\$par_minimiser" == "false" ]] && unset par_minimiser +[[ "\$par_not_reverse" == "false" ]] && unset par_not_reverse +[[ "\$par_homopolymers" == "false" ]] && unset par_homopolymers +[[ "\$par_natural_sort" == "false" ]] && unset par_natural_sort +[[ "\$par_ascii_sort" == "false" ]] && unset par_ascii_sort +[[ "\$par_template_coordinate" == "false" ]] && unset par_template_coordinate +[[ "\$par_write_index" == "false" ]] && unset par_write_index +[[ "\$par_no_PG" == "false" ]] && unset par_no_PG + + +samtools sort \\ + \${par_compression:+-l "\$par_compression"} \\ + \${par_uncompressed:+-u} \\ + \${par_minimiser:+-M} \\ + \${par_not_reverse:+-R} \\ + \${par_kmer_size:+-K "\$par_kmer_size"} \\ + \${par_order:+-I "\$par_order"} \\ + \${par_window:+-w "\$par_window"} \\ + \${par_homopolymers:+-H} \\ + \${par_natural_sort:+-n} \\ + \${par_ascii_sort:+-N} \\ + \${par_tag:+-t "\$par_tag"} \\ + \${par_input_fmt_option:+--input-fmt-option "\$par_input_fmt_option"} \\ + \${par_template_coordinate:+--template-coordinate} \\ + \${par_write_index:+--write-index} \\ + \${par_prefix:+-T "\$par_prefix"} \\ + \${par_no_PG:+--no-PG} \\ + \${par_output_fmt:+-O "\$par_output_fmt"} \\ + \${par_output_fmt_option:+--output-fmt-option "\$par_output_fmt_option"} \\ + \${par_reference:+--reference "\$par_reference"} \\ + -o "\$par_output" \\ + "\$par_input" + +# save text files containing the output of samtools view for later comparison +samtools view "\$par_output" -o "\$par_output".txt +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_PAR_REFERENCE" ]; then + VIASH_PAR_REFERENCE=$(ViashDockerStripAutomount "$VIASH_PAR_REFERENCE") + fi + if [ ! -z "$VIASH_PAR_ORDER" ]; then + VIASH_PAR_ORDER=$(ViashDockerStripAutomount "$VIASH_PAR_ORDER") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/samtools/samtools_stats/.config.vsh.yaml b/target/executable/samtools/samtools_stats/.config.vsh.yaml new file mode 100644 index 00000000..e562edf2 --- /dev/null +++ b/target/executable/samtools/samtools_stats/.config.vsh.yaml @@ -0,0 +1,406 @@ +name: "samtools_stats" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "Input file.\n" + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--bai" + description: "Index file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--fasta" + description: "Reference file the CRAM was created with.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--coverage" + alternatives: + - "-c" + description: "Coverage distribution min,max,step [1,1000,1].\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "boolean_true" + name: "--remove_dups" + alternatives: + - "-d" + description: "Exclude from statistics reads marked as duplicates.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--customized_index_file" + alternatives: + - "-X" + description: "Use a customized index file.\n" + info: null + direction: "input" + - type: "string" + name: "--required_flag" + alternatives: + - "-f" + description: "Required flag, 0 for unset. See also `samtools flags`.\n" + info: null + default: + - "0" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--filtering_flag" + alternatives: + - "-F" + description: "Filtering flag, 0 for unset. See also `samtools flags`.\n" + info: null + default: + - "0" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--GC_depth" + description: "The size of GC-depth bins (decreasing bin size increases memory\ + \ requirement).\n" + info: null + default: + - 20000.0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--insert_size" + alternatives: + - "-i" + description: "Maximum insert size.\n" + info: null + default: + - 8000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--id" + alternatives: + - "-I" + description: "Include only listed read group or sample name.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--read_length" + alternatives: + - "-l" + description: "Include in the statistics only reads with the given read length.\n" + info: null + default: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--most_inserts" + alternatives: + - "-m" + description: "Report only the main part of inserts.\n" + info: null + default: + - 0.99 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--split_prefix" + alternatives: + - "-P" + description: "Path or string prefix for filepaths output by --split (default is\ + \ input filename).\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--trim_quality" + alternatives: + - "-q" + description: "The BWA trimming parameter.\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--ref_seq" + alternatives: + - "-r" + description: "Reference sequence (required for GC-depth and mismatches-per-cycle\ + \ calculation).\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--split" + alternatives: + - "-S" + description: "Also write statistics to separate files split by tagged field.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--target_regions" + alternatives: + - "-t" + description: "Do stats in these regions only. Tab-delimited file chr,from,to,\ + \ 1-based, inclusive.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--sparse" + alternatives: + - "-x" + description: "Suppress outputting IS rows where there are no insertions.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--remove_overlaps" + alternatives: + - "-p" + description: "Remove overlaps of paired-end reads from coverage and base count\ + \ computations.\n" + info: null + direction: "input" + - type: "integer" + name: "--cov_threshold" + alternatives: + - "-g" + description: "Only bases with coverage above this value will be included in the\ + \ target percentage computation.\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--input_fmt_option" + description: "Specify a single input file format option in the form of OPTION\ + \ or OPTION=VALUE.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reference" + description: "Reference sequence FASTA FILE.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output file.\n" + info: null + default: + - "out.txt" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Reports alignment summary statistics for a BAM file." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "statistics" +- "counts" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-stats.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_stats/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/samtools/samtools_stats" + executable: "target/executable/samtools/samtools_stats/samtools_stats" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/samtools/samtools_stats/samtools_stats b/target/executable/samtools/samtools_stats/samtools_stats new file mode 100755 index 00000000..60e5f05f --- /dev/null +++ b/target/executable/samtools/samtools_stats/samtools_stats @@ -0,0 +1,1699 @@ +#!/usr/bin/env bash + +# samtools_stats main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="samtools_stats" +VIASH_META_FUNCTIONALITY_NAME="samtools_stats" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "samtools_stats main" + echo "" + echo "Reports alignment summary statistics for a BAM file." + echo "" + echo "Inputs:" + echo " --input" + echo " type: file, required parameter, file must exist" + echo " Input file." + echo "" + echo " --bai" + echo " type: file, file must exist" + echo " Index file." + echo "" + echo " --fasta" + echo " type: file, file must exist" + echo " Reference file the CRAM was created with." + echo "" + echo " -c, --coverage" + echo " type: integer, multiple values allowed" + echo " Coverage distribution min,max,step [1,1000,1]." + echo "" + echo " -d, --remove_dups" + echo " type: boolean_true" + echo " Exclude from statistics reads marked as duplicates." + echo "" + echo " -X, --customized_index_file" + echo " type: boolean_true" + echo " Use a customized index file." + echo "" + echo " -f, --required_flag" + echo " type: string" + echo " default: 0" + echo " Required flag, 0 for unset. See also \`samtools flags\`." + echo "" + echo " -F, --filtering_flag" + echo " type: string" + echo " default: 0" + echo " Filtering flag, 0 for unset. See also \`samtools flags\`." + echo "" + echo " --GC_depth" + echo " type: double" + echo " default: 20000.0" + echo " The size of GC-depth bins (decreasing bin size increases memory" + echo " requirement)." + echo "" + echo " -i, --insert_size" + echo " type: integer" + echo " default: 8000" + echo " Maximum insert size." + echo "" + echo " -I, --id" + echo " type: string" + echo " Include only listed read group or sample name." + echo "" + echo " -l, --read_length" + echo " type: integer" + echo " default: -1" + echo " Include in the statistics only reads with the given read length." + echo "" + echo " -m, --most_inserts" + echo " type: double" + echo " default: 0.99" + echo " Report only the main part of inserts." + echo "" + echo " -P, --split_prefix" + echo " type: string" + echo " Path or string prefix for filepaths output by --split (default is input" + echo " filename)." + echo "" + echo " -q, --trim_quality" + echo " type: integer" + echo " default: 0" + echo " The BWA trimming parameter." + echo "" + echo " -r, --ref_seq" + echo " type: file, file must exist" + echo " Reference sequence (required for GC-depth and mismatches-per-cycle" + echo " calculation)." + echo "" + echo " -S, --split" + echo " type: string" + echo " Also write statistics to separate files split by tagged field." + echo "" + echo " -t, --target_regions" + echo " type: file, file must exist" + echo " Do stats in these regions only. Tab-delimited file chr,from,to, 1-based," + echo " inclusive." + echo "" + echo " -x, --sparse" + echo " type: boolean_true" + echo " Suppress outputting IS rows where there are no insertions." + echo "" + echo " -p, --remove_overlaps" + echo " type: boolean_true" + echo " Remove overlaps of paired-end reads from coverage and base count" + echo " computations." + echo "" + echo " -g, --cov_threshold" + echo " type: integer" + echo " default: 0" + echo " Only bases with coverage above this value will be included in the target" + echo " percentage computation." + echo "" + echo " --input_fmt_option" + echo " type: string" + echo " Specify a single input file format option in the form of OPTION or" + echo " OPTION=VALUE." + echo "" + echo " --reference" + echo " type: file, file must exist" + echo " Reference sequence FASTA FILE." + echo "" + echo "Outputs:" + echo " -o, --output" + echo " type: file, required parameter, output, file must exist" + echo " default: out.txt" + echo " Output file." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 +ENTRYPOINT [] +RUN samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ +sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component samtools samtools_stats" +LABEL org.opencontainers.image.created="2024-06-24T08:36:39Z" +LABEL org.opencontainers.image.source="https://github.com/samtools/samtools" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "samtools_stats main" + exit + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bai) + [ -n "$VIASH_PAR_BAI" ] && ViashError Bad arguments for option \'--bai\': \'$VIASH_PAR_BAI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAI="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bai. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bai=*) + [ -n "$VIASH_PAR_BAI" ] && ViashError Bad arguments for option \'--bai=*\': \'$VIASH_PAR_BAI\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAI=$(ViashRemoveFlags "$1") + shift 1 + ;; + --fasta) + [ -n "$VIASH_PAR_FASTA" ] && ViashError Bad arguments for option \'--fasta\': \'$VIASH_PAR_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FASTA="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --fasta. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fasta=*) + [ -n "$VIASH_PAR_FASTA" ] && ViashError Bad arguments for option \'--fasta=*\': \'$VIASH_PAR_FASTA\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FASTA=$(ViashRemoveFlags "$1") + shift 1 + ;; + --coverage) + if [ -z "$VIASH_PAR_COVERAGE" ]; then + VIASH_PAR_COVERAGE="$2" + else + VIASH_PAR_COVERAGE="$VIASH_PAR_COVERAGE,""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --coverage. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --coverage=*) + if [ -z "$VIASH_PAR_COVERAGE" ]; then + VIASH_PAR_COVERAGE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_COVERAGE="$VIASH_PAR_COVERAGE,"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + -c) + if [ -z "$VIASH_PAR_COVERAGE" ]; then + VIASH_PAR_COVERAGE="$2" + else + VIASH_PAR_COVERAGE="$VIASH_PAR_COVERAGE,""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to -c. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --remove_dups) + [ -n "$VIASH_PAR_REMOVE_DUPS" ] && ViashError Bad arguments for option \'--remove_dups\': \'$VIASH_PAR_REMOVE_DUPS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REMOVE_DUPS=true + shift 1 + ;; + -d) + [ -n "$VIASH_PAR_REMOVE_DUPS" ] && ViashError Bad arguments for option \'-d\': \'$VIASH_PAR_REMOVE_DUPS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REMOVE_DUPS=true + shift 1 + ;; + --customized_index_file) + [ -n "$VIASH_PAR_CUSTOMIZED_INDEX_FILE" ] && ViashError Bad arguments for option \'--customized_index_file\': \'$VIASH_PAR_CUSTOMIZED_INDEX_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUSTOMIZED_INDEX_FILE=true + shift 1 + ;; + -X) + [ -n "$VIASH_PAR_CUSTOMIZED_INDEX_FILE" ] && ViashError Bad arguments for option \'-X\': \'$VIASH_PAR_CUSTOMIZED_INDEX_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUSTOMIZED_INDEX_FILE=true + shift 1 + ;; + --required_flag) + [ -n "$VIASH_PAR_REQUIRED_FLAG" ] && ViashError Bad arguments for option \'--required_flag\': \'$VIASH_PAR_REQUIRED_FLAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REQUIRED_FLAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --required_flag. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --required_flag=*) + [ -n "$VIASH_PAR_REQUIRED_FLAG" ] && ViashError Bad arguments for option \'--required_flag=*\': \'$VIASH_PAR_REQUIRED_FLAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REQUIRED_FLAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_REQUIRED_FLAG" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_REQUIRED_FLAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REQUIRED_FLAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -f. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --filtering_flag) + [ -n "$VIASH_PAR_FILTERING_FLAG" ] && ViashError Bad arguments for option \'--filtering_flag\': \'$VIASH_PAR_FILTERING_FLAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTERING_FLAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --filtering_flag. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --filtering_flag=*) + [ -n "$VIASH_PAR_FILTERING_FLAG" ] && ViashError Bad arguments for option \'--filtering_flag=*\': \'$VIASH_PAR_FILTERING_FLAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTERING_FLAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + -F) + [ -n "$VIASH_PAR_FILTERING_FLAG" ] && ViashError Bad arguments for option \'-F\': \'$VIASH_PAR_FILTERING_FLAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FILTERING_FLAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -F. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --GC_depth) + [ -n "$VIASH_PAR_GC_DEPTH" ] && ViashError Bad arguments for option \'--GC_depth\': \'$VIASH_PAR_GC_DEPTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GC_DEPTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --GC_depth. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --GC_depth=*) + [ -n "$VIASH_PAR_GC_DEPTH" ] && ViashError Bad arguments for option \'--GC_depth=*\': \'$VIASH_PAR_GC_DEPTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GC_DEPTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + --insert_size) + [ -n "$VIASH_PAR_INSERT_SIZE" ] && ViashError Bad arguments for option \'--insert_size\': \'$VIASH_PAR_INSERT_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INSERT_SIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --insert_size. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --insert_size=*) + [ -n "$VIASH_PAR_INSERT_SIZE" ] && ViashError Bad arguments for option \'--insert_size=*\': \'$VIASH_PAR_INSERT_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INSERT_SIZE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -i) + [ -n "$VIASH_PAR_INSERT_SIZE" ] && ViashError Bad arguments for option \'-i\': \'$VIASH_PAR_INSERT_SIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INSERT_SIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -i. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --id) + [ -n "$VIASH_PAR_ID" ] && ViashError Bad arguments for option \'--id\': \'$VIASH_PAR_ID\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ID="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --id. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --id=*) + [ -n "$VIASH_PAR_ID" ] && ViashError Bad arguments for option \'--id=*\': \'$VIASH_PAR_ID\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ID=$(ViashRemoveFlags "$1") + shift 1 + ;; + -I) + [ -n "$VIASH_PAR_ID" ] && ViashError Bad arguments for option \'-I\': \'$VIASH_PAR_ID\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ID="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -I. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read_length) + [ -n "$VIASH_PAR_READ_LENGTH" ] && ViashError Bad arguments for option \'--read_length\': \'$VIASH_PAR_READ_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --read_length. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read_length=*) + [ -n "$VIASH_PAR_READ_LENGTH" ] && ViashError Bad arguments for option \'--read_length=*\': \'$VIASH_PAR_READ_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_LENGTH=$(ViashRemoveFlags "$1") + shift 1 + ;; + -l) + [ -n "$VIASH_PAR_READ_LENGTH" ] && ViashError Bad arguments for option \'-l\': \'$VIASH_PAR_READ_LENGTH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_LENGTH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -l. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --most_inserts) + [ -n "$VIASH_PAR_MOST_INSERTS" ] && ViashError Bad arguments for option \'--most_inserts\': \'$VIASH_PAR_MOST_INSERTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MOST_INSERTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --most_inserts. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --most_inserts=*) + [ -n "$VIASH_PAR_MOST_INSERTS" ] && ViashError Bad arguments for option \'--most_inserts=*\': \'$VIASH_PAR_MOST_INSERTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MOST_INSERTS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -m) + [ -n "$VIASH_PAR_MOST_INSERTS" ] && ViashError Bad arguments for option \'-m\': \'$VIASH_PAR_MOST_INSERTS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MOST_INSERTS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -m. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --split_prefix) + [ -n "$VIASH_PAR_SPLIT_PREFIX" ] && ViashError Bad arguments for option \'--split_prefix\': \'$VIASH_PAR_SPLIT_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLIT_PREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --split_prefix. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --split_prefix=*) + [ -n "$VIASH_PAR_SPLIT_PREFIX" ] && ViashError Bad arguments for option \'--split_prefix=*\': \'$VIASH_PAR_SPLIT_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLIT_PREFIX=$(ViashRemoveFlags "$1") + shift 1 + ;; + -P) + [ -n "$VIASH_PAR_SPLIT_PREFIX" ] && ViashError Bad arguments for option \'-P\': \'$VIASH_PAR_SPLIT_PREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLIT_PREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -P. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --trim_quality) + [ -n "$VIASH_PAR_TRIM_QUALITY" ] && ViashError Bad arguments for option \'--trim_quality\': \'$VIASH_PAR_TRIM_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_QUALITY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --trim_quality. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --trim_quality=*) + [ -n "$VIASH_PAR_TRIM_QUALITY" ] && ViashError Bad arguments for option \'--trim_quality=*\': \'$VIASH_PAR_TRIM_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_QUALITY=$(ViashRemoveFlags "$1") + shift 1 + ;; + -q) + [ -n "$VIASH_PAR_TRIM_QUALITY" ] && ViashError Bad arguments for option \'-q\': \'$VIASH_PAR_TRIM_QUALITY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TRIM_QUALITY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -q. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ref_seq) + [ -n "$VIASH_PAR_REF_SEQ" ] && ViashError Bad arguments for option \'--ref_seq\': \'$VIASH_PAR_REF_SEQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF_SEQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --ref_seq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --ref_seq=*) + [ -n "$VIASH_PAR_REF_SEQ" ] && ViashError Bad arguments for option \'--ref_seq=*\': \'$VIASH_PAR_REF_SEQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF_SEQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -r) + [ -n "$VIASH_PAR_REF_SEQ" ] && ViashError Bad arguments for option \'-r\': \'$VIASH_PAR_REF_SEQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REF_SEQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -r. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --split) + [ -n "$VIASH_PAR_SPLIT" ] && ViashError Bad arguments for option \'--split\': \'$VIASH_PAR_SPLIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --split. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --split=*) + [ -n "$VIASH_PAR_SPLIT" ] && ViashError Bad arguments for option \'--split=*\': \'$VIASH_PAR_SPLIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLIT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -S) + [ -n "$VIASH_PAR_SPLIT" ] && ViashError Bad arguments for option \'-S\': \'$VIASH_PAR_SPLIT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLIT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -S. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --target_regions) + [ -n "$VIASH_PAR_TARGET_REGIONS" ] && ViashError Bad arguments for option \'--target_regions\': \'$VIASH_PAR_TARGET_REGIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TARGET_REGIONS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --target_regions. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --target_regions=*) + [ -n "$VIASH_PAR_TARGET_REGIONS" ] && ViashError Bad arguments for option \'--target_regions=*\': \'$VIASH_PAR_TARGET_REGIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TARGET_REGIONS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -t) + [ -n "$VIASH_PAR_TARGET_REGIONS" ] && ViashError Bad arguments for option \'-t\': \'$VIASH_PAR_TARGET_REGIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TARGET_REGIONS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -t. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sparse) + [ -n "$VIASH_PAR_SPARSE" ] && ViashError Bad arguments for option \'--sparse\': \'$VIASH_PAR_SPARSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPARSE=true + shift 1 + ;; + -x) + [ -n "$VIASH_PAR_SPARSE" ] && ViashError Bad arguments for option \'-x\': \'$VIASH_PAR_SPARSE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPARSE=true + shift 1 + ;; + --remove_overlaps) + [ -n "$VIASH_PAR_REMOVE_OVERLAPS" ] && ViashError Bad arguments for option \'--remove_overlaps\': \'$VIASH_PAR_REMOVE_OVERLAPS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REMOVE_OVERLAPS=true + shift 1 + ;; + -p) + [ -n "$VIASH_PAR_REMOVE_OVERLAPS" ] && ViashError Bad arguments for option \'-p\': \'$VIASH_PAR_REMOVE_OVERLAPS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REMOVE_OVERLAPS=true + shift 1 + ;; + --cov_threshold) + [ -n "$VIASH_PAR_COV_THRESHOLD" ] && ViashError Bad arguments for option \'--cov_threshold\': \'$VIASH_PAR_COV_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COV_THRESHOLD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --cov_threshold. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --cov_threshold=*) + [ -n "$VIASH_PAR_COV_THRESHOLD" ] && ViashError Bad arguments for option \'--cov_threshold=*\': \'$VIASH_PAR_COV_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COV_THRESHOLD=$(ViashRemoveFlags "$1") + shift 1 + ;; + -g) + [ -n "$VIASH_PAR_COV_THRESHOLD" ] && ViashError Bad arguments for option \'-g\': \'$VIASH_PAR_COV_THRESHOLD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COV_THRESHOLD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -g. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input_fmt_option) + [ -n "$VIASH_PAR_INPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--input_fmt_option\': \'$VIASH_PAR_INPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_FMT_OPTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input_fmt_option. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input_fmt_option=*) + [ -n "$VIASH_PAR_INPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--input_fmt_option=*\': \'$VIASH_PAR_INPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_FMT_OPTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --reference) + [ -n "$VIASH_PAR_REFERENCE" ] && ViashError Bad arguments for option \'--reference\': \'$VIASH_PAR_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REFERENCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --reference. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --reference=*) + [ -n "$VIASH_PAR_REFERENCE" ] && ViashError Bad arguments for option \'--reference=*\': \'$VIASH_PAR_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REFERENCE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/samtools/samtools_stats:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_REMOVE_DUPS+x} ]; then + VIASH_PAR_REMOVE_DUPS="false" +fi +if [ -z ${VIASH_PAR_CUSTOMIZED_INDEX_FILE+x} ]; then + VIASH_PAR_CUSTOMIZED_INDEX_FILE="false" +fi +if [ -z ${VIASH_PAR_REQUIRED_FLAG+x} ]; then + VIASH_PAR_REQUIRED_FLAG="0" +fi +if [ -z ${VIASH_PAR_FILTERING_FLAG+x} ]; then + VIASH_PAR_FILTERING_FLAG="0" +fi +if [ -z ${VIASH_PAR_GC_DEPTH+x} ]; then + VIASH_PAR_GC_DEPTH="20000.0" +fi +if [ -z ${VIASH_PAR_INSERT_SIZE+x} ]; then + VIASH_PAR_INSERT_SIZE="8000" +fi +if [ -z ${VIASH_PAR_READ_LENGTH+x} ]; then + VIASH_PAR_READ_LENGTH="-1" +fi +if [ -z ${VIASH_PAR_MOST_INSERTS+x} ]; then + VIASH_PAR_MOST_INSERTS="0.99" +fi +if [ -z ${VIASH_PAR_TRIM_QUALITY+x} ]; then + VIASH_PAR_TRIM_QUALITY="0" +fi +if [ -z ${VIASH_PAR_SPARSE+x} ]; then + VIASH_PAR_SPARSE="false" +fi +if [ -z ${VIASH_PAR_REMOVE_OVERLAPS+x} ]; then + VIASH_PAR_REMOVE_OVERLAPS="false" +fi +if [ -z ${VIASH_PAR_COV_THRESHOLD+x} ]; then + VIASH_PAR_COV_THRESHOLD="0" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_BAI" ] && [ ! -e "$VIASH_PAR_BAI" ]; then + ViashError "Input file '$VIASH_PAR_BAI' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_FASTA" ] && [ ! -e "$VIASH_PAR_FASTA" ]; then + ViashError "Input file '$VIASH_PAR_FASTA' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REF_SEQ" ] && [ ! -e "$VIASH_PAR_REF_SEQ" ]; then + ViashError "Input file '$VIASH_PAR_REF_SEQ' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_TARGET_REGIONS" ] && [ ! -e "$VIASH_PAR_TARGET_REGIONS" ]; then + ViashError "Input file '$VIASH_PAR_TARGET_REGIONS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REFERENCE" ] && [ ! -e "$VIASH_PAR_REFERENCE" ]; then + ViashError "Input file '$VIASH_PAR_REFERENCE' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [ -n "$VIASH_PAR_COVERAGE" ]; then + IFS=',' + set -f + for val in $VIASH_PAR_COVERAGE; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--coverage' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [[ -n "$VIASH_PAR_REMOVE_DUPS" ]]; then + if ! [[ "$VIASH_PAR_REMOVE_DUPS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--remove_dups' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUSTOMIZED_INDEX_FILE" ]]; then + if ! [[ "$VIASH_PAR_CUSTOMIZED_INDEX_FILE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--customized_index_file' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GC_DEPTH" ]]; then + if ! [[ "$VIASH_PAR_GC_DEPTH" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--GC_depth' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_INSERT_SIZE" ]]; then + if ! [[ "$VIASH_PAR_INSERT_SIZE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--insert_size' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_READ_LENGTH" ]]; then + if ! [[ "$VIASH_PAR_READ_LENGTH" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--read_length' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MOST_INSERTS" ]]; then + if ! [[ "$VIASH_PAR_MOST_INSERTS" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--most_inserts' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TRIM_QUALITY" ]]; then + if ! [[ "$VIASH_PAR_TRIM_QUALITY" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--trim_quality' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SPARSE" ]]; then + if ! [[ "$VIASH_PAR_SPARSE" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--sparse' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_REMOVE_OVERLAPS" ]]; then + if ! [[ "$VIASH_PAR_REMOVE_OVERLAPS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--remove_overlaps' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_COV_THRESHOLD" ]]; then + if ! [[ "$VIASH_PAR_COV_THRESHOLD" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--cov_threshold' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_BAI" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_BAI")" ) + VIASH_PAR_BAI=$(ViashDockerAutodetectMount "$VIASH_PAR_BAI") +fi +if [ ! -z "$VIASH_PAR_FASTA" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FASTA")" ) + VIASH_PAR_FASTA=$(ViashDockerAutodetectMount "$VIASH_PAR_FASTA") +fi +if [ ! -z "$VIASH_PAR_REF_SEQ" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REF_SEQ")" ) + VIASH_PAR_REF_SEQ=$(ViashDockerAutodetectMount "$VIASH_PAR_REF_SEQ") +fi +if [ ! -z "$VIASH_PAR_TARGET_REGIONS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_TARGET_REGIONS")" ) + VIASH_PAR_TARGET_REGIONS=$(ViashDockerAutodetectMount "$VIASH_PAR_TARGET_REGIONS") +fi +if [ ! -z "$VIASH_PAR_REFERENCE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REFERENCE")" ) + VIASH_PAR_REFERENCE=$(ViashDockerAutodetectMount "$VIASH_PAR_REFERENCE") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-samtools_stats-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_BAI+x} ]; then echo "${VIASH_PAR_BAI}" | sed "s#'#'\"'\"'#g;s#.*#par_bai='&'#" ; else echo "# par_bai="; fi ) +$( if [ ! -z ${VIASH_PAR_FASTA+x} ]; then echo "${VIASH_PAR_FASTA}" | sed "s#'#'\"'\"'#g;s#.*#par_fasta='&'#" ; else echo "# par_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_COVERAGE+x} ]; then echo "${VIASH_PAR_COVERAGE}" | sed "s#'#'\"'\"'#g;s#.*#par_coverage='&'#" ; else echo "# par_coverage="; fi ) +$( if [ ! -z ${VIASH_PAR_REMOVE_DUPS+x} ]; then echo "${VIASH_PAR_REMOVE_DUPS}" | sed "s#'#'\"'\"'#g;s#.*#par_remove_dups='&'#" ; else echo "# par_remove_dups="; fi ) +$( if [ ! -z ${VIASH_PAR_CUSTOMIZED_INDEX_FILE+x} ]; then echo "${VIASH_PAR_CUSTOMIZED_INDEX_FILE}" | sed "s#'#'\"'\"'#g;s#.*#par_customized_index_file='&'#" ; else echo "# par_customized_index_file="; fi ) +$( if [ ! -z ${VIASH_PAR_REQUIRED_FLAG+x} ]; then echo "${VIASH_PAR_REQUIRED_FLAG}" | sed "s#'#'\"'\"'#g;s#.*#par_required_flag='&'#" ; else echo "# par_required_flag="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTERING_FLAG+x} ]; then echo "${VIASH_PAR_FILTERING_FLAG}" | sed "s#'#'\"'\"'#g;s#.*#par_filtering_flag='&'#" ; else echo "# par_filtering_flag="; fi ) +$( if [ ! -z ${VIASH_PAR_GC_DEPTH+x} ]; then echo "${VIASH_PAR_GC_DEPTH}" | sed "s#'#'\"'\"'#g;s#.*#par_GC_depth='&'#" ; else echo "# par_GC_depth="; fi ) +$( if [ ! -z ${VIASH_PAR_INSERT_SIZE+x} ]; then echo "${VIASH_PAR_INSERT_SIZE}" | sed "s#'#'\"'\"'#g;s#.*#par_insert_size='&'#" ; else echo "# par_insert_size="; fi ) +$( if [ ! -z ${VIASH_PAR_ID+x} ]; then echo "${VIASH_PAR_ID}" | sed "s#'#'\"'\"'#g;s#.*#par_id='&'#" ; else echo "# par_id="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_LENGTH+x} ]; then echo "${VIASH_PAR_READ_LENGTH}" | sed "s#'#'\"'\"'#g;s#.*#par_read_length='&'#" ; else echo "# par_read_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MOST_INSERTS+x} ]; then echo "${VIASH_PAR_MOST_INSERTS}" | sed "s#'#'\"'\"'#g;s#.*#par_most_inserts='&'#" ; else echo "# par_most_inserts="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLIT_PREFIX+x} ]; then echo "${VIASH_PAR_SPLIT_PREFIX}" | sed "s#'#'\"'\"'#g;s#.*#par_split_prefix='&'#" ; else echo "# par_split_prefix="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_QUALITY+x} ]; then echo "${VIASH_PAR_TRIM_QUALITY}" | sed "s#'#'\"'\"'#g;s#.*#par_trim_quality='&'#" ; else echo "# par_trim_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_REF_SEQ+x} ]; then echo "${VIASH_PAR_REF_SEQ}" | sed "s#'#'\"'\"'#g;s#.*#par_ref_seq='&'#" ; else echo "# par_ref_seq="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLIT+x} ]; then echo "${VIASH_PAR_SPLIT}" | sed "s#'#'\"'\"'#g;s#.*#par_split='&'#" ; else echo "# par_split="; fi ) +$( if [ ! -z ${VIASH_PAR_TARGET_REGIONS+x} ]; then echo "${VIASH_PAR_TARGET_REGIONS}" | sed "s#'#'\"'\"'#g;s#.*#par_target_regions='&'#" ; else echo "# par_target_regions="; fi ) +$( if [ ! -z ${VIASH_PAR_SPARSE+x} ]; then echo "${VIASH_PAR_SPARSE}" | sed "s#'#'\"'\"'#g;s#.*#par_sparse='&'#" ; else echo "# par_sparse="; fi ) +$( if [ ! -z ${VIASH_PAR_REMOVE_OVERLAPS+x} ]; then echo "${VIASH_PAR_REMOVE_OVERLAPS}" | sed "s#'#'\"'\"'#g;s#.*#par_remove_overlaps='&'#" ; else echo "# par_remove_overlaps="; fi ) +$( if [ ! -z ${VIASH_PAR_COV_THRESHOLD+x} ]; then echo "${VIASH_PAR_COV_THRESHOLD}" | sed "s#'#'\"'\"'#g;s#.*#par_cov_threshold='&'#" ; else echo "# par_cov_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_INPUT_FMT_OPTION}" | sed "s#'#'\"'\"'#g;s#.*#par_input_fmt_option='&'#" ; else echo "# par_input_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_REFERENCE+x} ]; then echo "${VIASH_PAR_REFERENCE}" | sed "s#'#'\"'\"'#g;s#.*#par_reference='&'#" ; else echo "# par_reference="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\$par_remove_dups" == "false" ]] && unset par_remove_dups +[[ "\$par_customized_index_file" == "false" ]] && unset par_customized_index_file +[[ "\$par_sparse" == "false" ]] && unset par_sparse +[[ "\$par_remove_overlaps" == "false" ]] && unset par_remove_overlaps + +samtools stats \\ + \${par_coverage:+-c "\$par_coverage"} \\ + \${par_remove_dups:+-d} \\ + \${par_required_flag:+-f "\$par_required_flag"} \\ + \${par_filtering_flag:+-F "\$par_filtering_flag"} \\ + \${par_GC_depth:+--GC-depth "\$par_GC_depth"} \\ + \${par_insert_size:+-i "\$par_insert_size"} \\ + \${par_id:+-I "\$par_id"} \\ + \${par_read_length:+-l "\$par_read_length"} \\ + \${par_most_inserts:+-m "\$par_most_inserts"} \\ + \${par_split_prefix:+-P "\$par_split_prefix"} \\ + \${par_trim_quality:+-q "\$par_trim_quality"} \\ + \${par_ref_seq:+-r "\$par_ref_seq"} \\ + \${par_split:+-S "\$par_split"} \\ + \${par_target_regions:+-t "\$par_target_regions"} \\ + \${par_sparse:+-x} \\ + \${par_remove_overlaps:+-p} \\ + \${par_cov_threshold:+-g "\$par_cov_threshold"} \\ + \${par_input_fmt_option:+-O "\$par_input_fmt_option"} \\ + \${par_reference:+-R "\$par_reference"} \\ + "\$par_input" \\ + > "\$par_output" + +exit 0 +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_BAI" ]; then + VIASH_PAR_BAI=$(ViashDockerStripAutomount "$VIASH_PAR_BAI") + fi + if [ ! -z "$VIASH_PAR_FASTA" ]; then + VIASH_PAR_FASTA=$(ViashDockerStripAutomount "$VIASH_PAR_FASTA") + fi + if [ ! -z "$VIASH_PAR_REF_SEQ" ]; then + VIASH_PAR_REF_SEQ=$(ViashDockerStripAutomount "$VIASH_PAR_REF_SEQ") + fi + if [ ! -z "$VIASH_PAR_TARGET_REGIONS" ]; then + VIASH_PAR_TARGET_REGIONS=$(ViashDockerStripAutomount "$VIASH_PAR_TARGET_REGIONS") + fi + if [ ! -z "$VIASH_PAR_REFERENCE" ]; then + VIASH_PAR_REFERENCE=$(ViashDockerStripAutomount "$VIASH_PAR_REFERENCE") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/samtools/samtools_view/.config.vsh.yaml b/target/executable/samtools/samtools_view/.config.vsh.yaml new file mode 100644 index 00000000..3873e2aa --- /dev/null +++ b/target/executable/samtools/samtools_view/.config.vsh.yaml @@ -0,0 +1,677 @@ +name: "samtools_view" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "Input SAM, BAM, or CRAM file." + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--fai_reference" + alternatives: + - "-t" + description: "A tab-delimited FILE. Each line must contain the reference name\ + \ in the first column\nand the length of the reference in the second column,\ + \ with one line for each distinct\nreference. Any additional fields beyond the\ + \ second column are ignored. This file also\ndefines the order of the reference\ + \ sequences in sorting. If you run: `samtools faidx ',\nthe resulting\ + \ index file .fai can be used as this FILE.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reference" + alternatives: + - "-T" + description: "A FASTA format reference FILE, optionally compressed by bgzip and\ + \ ideally indexed by samtools faidx.\nIf an index is not present one will be\ + \ generated for you, if the reference file is local.\nIf the reference file\ + \ is not local, but is accessed instead via an https://, s3:// or other URL,\n\ + the index file will need to be supplied by the server alongside the reference.\ + \ It is possible to\nhave the reference and index files in different locations\ + \ by supplying both to this option separated\nby the string \"##idx##\", for\ + \ example:\n--reference ftp://x.com/ref.fa##idx##ftp://y.com/index.fa.fai\n\ + However, note that only the location of the reference will be stored in the\ + \ output file header.\nIf this method is used to make CRAM files, the cram reader\ + \ may not be able to find the index,\nand may not be able to decode the file\ + \ unless it can get the references it needs using a different\nmethod.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--target_file" + alternatives: + - "-L" + description: "Only output alignments overlapping the input BED FILE [null].\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--region_file" + description: "Use an index and multi-region iterator to only output alignments\ + \ overlapping the input BED FILE.\nEquivalent to --use_index --target_file FILE.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--qname_file" + alternatives: + - "-N" + description: "Output only alignments with read names listed in FILE. If FILE starts\ + \ with ^ then the operation is\nnegated and only outputs alignment with read\ + \ groups not listed in FILE. It is not permissible to mix\nboth the filter-in\ + \ and filter-out style syntax in the same command.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--read_group_file" + alternatives: + - "-R" + description: "Output alignments in read groups listed in FILE [null]. If FILE\ + \ starts with ^ then the operation is\nnegated and only outputs alignment with\ + \ read names not listed in FILE. It is not permissible to mix\nboth the filter-in\ + \ and filter-out style syntax in the same command. Note that records with no\ + \ RG tag\nwill also be output when using this option. This behaviour may change\ + \ in a future release.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--use_index" + alternatives: + - "-M" + description: "Use the multi-region iterator on the union of a BED file and command-line\ + \ region arguments.\nThis avoids re-reading the same regions of files so can\ + \ sometimes be much faster. Note this also\nremoves duplicate sequences. Without\ + \ this a sequence that overlaps multiple regions specified on\nthe command line\ + \ will be reported multiple times. The usage of a BED file is optional and its\ + \ path\nhas to be preceded by --target_file option.\n" + info: null + direction: "input" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output to FILE instead of [stdout]." + info: null + example: + - "output.bam" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--bam" + alternatives: + - "-b" + description: "Output in the BAM format." + info: null + direction: "input" + - type: "boolean_true" + name: "--cram" + alternatives: + - "-C" + description: "Output in the CRAM format (requires --reference).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--fast" + description: "Enable fast compression. This also changes the default output format\ + \ to BAM,\nbut this can be overridden by the explicit format options or using\ + \ a filename\nwith a known suffix.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--uncompressed" + alternatives: + - "-u" + description: "Output uncompressed data. This also changes the default output format\ + \ to BAM,\nbut this can be overridden by the explicit format options or using\ + \ a filename\nwith a known suffix.\nThis option saves time spent on compression/decompression\ + \ and is thus preferred\nwhen the output is piped to another samtools command.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--with_header" + description: "Include the header in the output.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--header_only" + alternatives: + - "-H" + description: "Output the header only.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_header" + description: "When producing SAM format, output alignment records but not headers.\n\ + This is the default; the option can be used to reset the effect of \n--with_header/--header_only.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--count" + alternatives: + - "-c" + description: "Instead of printing the alignments, only count them and print the\ + \ total number.\nAll filter options, such as --require_flags, --excl_flags,\ + \ and --min_MQ, are taken\ninto account. The --unmap option is ignored in this\ + \ mode.\n" + info: null + direction: "input" + - type: "file" + name: "--output_unselected" + alternatives: + - "-U" + description: "Write alignments that are not selected by the various filter options\ + \ to FILE.\nWhen this option is used, all alignments (or all alignments intersecting\ + \ the regions\nspecified) are written to either the output file or this file,\ + \ but never both.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--unmap" + alternatives: + - "-p" + description: "Set the UNMAP flag on alignments that are not selected by the filter\ + \ options.\nThese alignments are then written to the normal output. This is\ + \ not compatible\nwith --output_unselected.\n" + info: null + direction: "input" + - type: "string" + name: "--read_group" + alternatives: + - "-r" + description: "Output alignments in read group STR [null]. Note that records with\ + \ no RG tag will also be output\nwhen using this option. This behaviour may\ + \ change in a future release.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--tag" + alternatives: + - "-d" + description: "Only output alignments with tag STR1 and associated value STR2,\ + \ which can be a string or an integer\n[null].\nThe value can be omitted, in\ + \ which case only the tag is considered.\nNote that this option does not specify\ + \ a tag type. For example, use --tag XX:42 to select alignments\nwith an XX:i:42\ + \ field, not --tag XX:i:42.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--tag_file" + alternatives: + - "-D" + description: "Only output alignments with tag STR and associated values listed\ + \ in FILE.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_MQ" + alternatives: + - "-q" + description: "Skip alignments with MAPQ smaller than INT.\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--library" + alternatives: + - "-l" + description: "Only output alignments in library STR.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_qlen" + alternatives: + - "-m" + description: "Only output alignments with number of CIGAR bases consuming query\ + \ sequence >= INT.\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--expr" + alternatives: + - "-e" + description: "Only include alignments that match the filter expression STR. The\ + \ syntax for these expressions is\ndescribed in the main samtools.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--require_flags" + alternatives: + - "-f" + description: "Only output alignments with all bits set in FLAG present in the\ + \ FLAG field. FLAG can be specified\nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/),\ + \ in octal by beginning with `0' (i.e. /^0[0-7]+/),\nas a decimal number not\ + \ beginning with '0' or as a comma-separated list of flag names.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--excl_flags" + alternatives: + - "-F" + description: "Do not output alignments with any bits set in FLAG present in the\ + \ FLAG field. FLAG can be specified\nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/),\ + \ in octal by beginning with `0' (i.e. /^0[0-7]+/),\nas a decimal number not\ + \ beginning with '0' or as a comma-separated list of flag names.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--excl_all_flags" + alternatives: + - "-G" + description: "Do not output alignments with all bits set in INT present in the\ + \ FLAG field. This is the opposite of\n--require_flags such that --require_flags\ + \ 12 --exclude_all_flags 12 is the same as no filtering at all.\nFLAG can be\ + \ specified in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by\ + \ beginning with `0'\n(i.e. /^0[0-7]+/), as a decimal number not beginning with\ + \ '0' or as a comma-separated list of flag names.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--incl_flags" + alternatives: + - "--rf" + description: "Only output alignments with any bit set in FLAG present in the FLAG\ + \ field. FLAG can be specified in hex\nby beginning with `0x' (i.e. /^0x[0-9A-F]+/),\ + \ in octal by beginning with `0' (i.e. /^0[0-7]+/), as a decimal\nnumber not\ + \ beginning with '0' or as a comma-separated list of flag names.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--remove_tag" + alternatives: + - "-x" + description: "Read tag(s) to exclude from output (repeatable) [null]. This can\ + \ be a single tag or a comma separated list.\nAlternatively the option itself\ + \ can be repeated multiple times.\nIf the list starts with a `^' then it is\ + \ negated and treated as a request to remove all tags except those in STR.\n\ + The list may be empty, so --remove_tag ^ will remove all tags.\nNote that tags\ + \ will only be removed from reads that pass filtering.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--keep_tag" + description: "This keeps only tags listed in STR and is directly equivalent to\ + \ --remove_tag ^STR. Specifying an empty list\nwill remove all tags. If both\ + \ --keep_tag and --remove_tag are specified then --keep_tag has precedence.\n\ + Note that tags will only be removed from reads that pass filtering.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--remove_B" + alternatives: + - "-B" + description: "Collapse the backward CIGAR operation.\n" + info: null + direction: "input" + - type: "string" + name: "--add_flags" + description: "Adds flag(s) to read. FLAG can be specified in hex by beginning\ + \ with `0x' (i.e. /^0x[0-9A-F]+/), in octal\nby beginning with `0' (i.e. /^0[0-7]+/),\ + \ as a decimal number not beginning with '0' or as a comma-separated\nlist of\ + \ flag names.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--remove_flags" + description: "Remove flag(s) from read. FLAG is specified in the same way as with\ + \ the --add_flags option.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--subsample" + description: "Output only a proportion of the input alignments, as specified by\ + \ 0.0 <= FLOAT <= 1.0, which gives the fraction\nof templates/pairs to be kept.\ + \ This subsampling acts in the same way on all of the alignment records in the\ + \ same\ntemplate or read pair, so it never keeps a read but not its mate.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--subsample_seed" + description: "Subsampling seed used to influence which subset of reads is kept.\ + \ When subsampling data that has previously\nbeen subsampled, be sure to use\ + \ a different seed value from those used previously; otherwise more reads will\n\ + be retained than expected.\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--fetch_pairs" + alternatives: + - "-P" + description: "Retrieve pairs even when the mate is outside of the requested region.\ + \ Enabling this option also turns on the\nmulti-region iterator (-M). A region\ + \ to search must be specified, either on the command-line, or using the\n--target_file\ + \ option. The input file must be an indexed regular file.\nThis option first\ + \ scans the requested region, using the RNEXT and PNEXT fields of the records\ + \ that have the\nPAIRED flag set and pass other filtering options to find where\ + \ paired reads are located. These locations are\nused to build an expanded region\ + \ list, and a set of QNAMEs to allow from the new regions. It will then make\n\ + a second pass, collecting all reads from the originally-specified region list\ + \ together with reads from additional\nlocations that match the allowed set\ + \ of QNAMEs. Any other filtering options used will be applied to all reads\n\ + found during this second pass.\nAs this option links reads using RNEXT and PNEXT,\ + \ it is important that these fields are set accurately. Use\n'samtools fixmate'\ + \ to correct them if necessary.\nNote that this option does not work with the\ + \ --count, --output-unselected or --unmap options.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--customized_index" + alternatives: + - "-X" + description: "Include customized index file as a part of arguments. See EXAMPLES\ + \ section for sample of usage.\n" + info: null + direction: "input" + - type: "string" + name: "--sanitize" + alternatives: + - "-z" + description: "Perform some sanity checks on the state of SAM record fields, fixing\ + \ up common mistakes made by aligners.\nThese include soft-clipping alignments\ + \ when they extend beyond the end of the reference, marking records as\nunmapped\ + \ when they have reference * or position 0, and ensuring unmapped alignments\ + \ have no CIGAR or mapping\nquality for unmapped alignments and no MD, NM, CG\ + \ or SM tags.\nFLAGs is a comma-separated list of keywords chosen from the following\ + \ list.\n\nunmap: The UNMAPPED BAM flag. This is set for reads with position\ + \ <= 0, reference name \"*\" or reads starting\nbeyond the end of the reference.\ + \ Note CIGAR \"*\" is permitted for mapped data so does not trigger this.\n\n\ + pos: Position and reference name fields. These may be cleared when a sequence\ + \ is unmapped due to the\ncoordinates being beyond the end of the reference.\ + \ Selecting this may change the sort order of the file,\nso it is not a part\ + \ of the on compound argument.\nmqual: Mapping quality. This is set to zero\ + \ for unmapped reads.\ncigar: Modifies CIGAR fields, either by adding soft-clips\ + \ for reads that overlap the end of the reference or\n by clearing it\ + \ for unmapped reads.\naux: For unmapped data, some auxiliary fields are meaningless\ + \ and will be removed. These include NM, MD, CG and SM.\noff: Perform no sanity\ + \ fixing. This is the default\non: Sanitize data in a way that guarantees the\ + \ same sort order. This is everything except for pos.\nall: All sanitizing options,\ + \ including pos.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_PG" + description: "Do not add a @PG line to the header of the output file.\n" + info: null + direction: "input" + - type: "string" + name: "--input_fmt_option" + description: "Specify a single input file format option in the form of OPTION\ + \ or OPTION=VALUE.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt" + alternatives: + - "-O" + description: "Specify output format (SAM, BAM, CRAM).\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt_option" + description: "Specify a single output file format option in the form of OPTION\ + \ or OPTION=VALUE.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--write_index" + description: "Automatically index the output files.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Views and converts SAM/BAM/CRAM files." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "view" +- "convert" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-view.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_view/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/samtools/samtools_view" + executable: "target/executable/samtools/samtools_view/samtools_view" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/samtools/samtools_view/samtools_view b/target/executable/samtools/samtools_view/samtools_view new file mode 100755 index 00000000..a0169701 --- /dev/null +++ b/target/executable/samtools/samtools_view/samtools_view @@ -0,0 +1,2333 @@ +#!/usr/bin/env bash + +# samtools_view main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="samtools_view" +VIASH_META_FUNCTIONALITY_NAME="samtools_view" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "samtools_view main" + echo "" + echo "Views and converts SAM/BAM/CRAM files." + echo "" + echo "Inputs:" + echo " --input" + echo " type: file, required parameter, file must exist" + echo " Input SAM, BAM, or CRAM file." + echo "" + echo " -t, --fai_reference" + echo " type: file, file must exist" + echo " A tab-delimited FILE. Each line must contain the reference name in the" + echo " first column" + echo " and the length of the reference in the second column, with one line for" + echo " each distinct" + echo " reference. Any additional fields beyond the second column are ignored." + echo " This file also" + echo " defines the order of the reference sequences in sorting. If you run:" + echo " \`samtools faidx '," + echo " the resulting index file .fai can be used as this FILE." + echo "" + echo " -T, --reference" + echo " type: file, file must exist" + echo " A FASTA format reference FILE, optionally compressed by bgzip and" + echo " ideally indexed by samtools faidx." + echo " If an index is not present one will be generated for you, if the" + echo " reference file is local." + echo " If the reference file is not local, but is accessed instead via an" + echo " https://, s3:// or other URL," + echo " the index file will need to be supplied by the server alongside the" + echo " reference. It is possible to" + echo " have the reference and index files in different locations by supplying" + echo " both to this option separated" + echo " by the string \"##idx##\", for example:" + echo " --reference ftp://x.com/ref.fa##idx##ftp://y.com/index.fa.fai" + echo " However, note that only the location of the reference will be stored in" + echo " the output file header." + echo " If this method is used to make CRAM files, the cram reader may not be" + echo " able to find the index," + echo " and may not be able to decode the file unless it can get the references" + echo " it needs using a different" + echo " method." + echo "" + echo " -L, --target_file" + echo " type: file, file must exist" + echo " Only output alignments overlapping the input BED FILE [null]." + echo "" + echo " --region_file" + echo " type: file, file must exist" + echo " Use an index and multi-region iterator to only output alignments" + echo " overlapping the input BED FILE." + echo " Equivalent to --use_index --target_file FILE." + echo "" + echo " -N, --qname_file" + echo " type: file, file must exist" + echo " Output only alignments with read names listed in FILE. If FILE starts" + echo " with ^ then the operation is" + echo " negated and only outputs alignment with read groups not listed in FILE." + echo " It is not permissible to mix" + echo " both the filter-in and filter-out style syntax in the same command." + echo "" + echo " -R, --read_group_file" + echo " type: file, file must exist" + echo " Output alignments in read groups listed in FILE [null]. If FILE starts" + echo " with ^ then the operation is" + echo " negated and only outputs alignment with read names not listed in FILE." + echo " It is not permissible to mix" + echo " both the filter-in and filter-out style syntax in the same command. Note" + echo " that records with no RG tag" + echo " will also be output when using this option. This behaviour may change in" + echo " a future release." + echo "" + echo " -M, --use_index" + echo " type: boolean_true" + echo " Use the multi-region iterator on the union of a BED file and" + echo " command-line region arguments." + echo " This avoids re-reading the same regions of files so can sometimes be" + echo " much faster. Note this also" + echo " removes duplicate sequences. Without this a sequence that overlaps" + echo " multiple regions specified on" + echo " the command line will be reported multiple times. The usage of a BED" + echo " file is optional and its path" + echo " has to be preceded by --target_file option." + echo "" + echo "Outputs:" + echo " -o, --output" + echo " type: file, required parameter, output, file must exist" + echo " example: output.bam" + echo " Output to FILE instead of [stdout]." + echo "" + echo " -b, --bam" + echo " type: boolean_true" + echo " Output in the BAM format." + echo "" + echo " -C, --cram" + echo " type: boolean_true" + echo " Output in the CRAM format (requires --reference)." + echo "" + echo " --fast" + echo " type: boolean_true" + echo " Enable fast compression. This also changes the default output format to" + echo " BAM," + echo " but this can be overridden by the explicit format options or using a" + echo " filename" + echo " with a known suffix." + echo "" + echo " -u, --uncompressed" + echo " type: boolean_true" + echo " Output uncompressed data. This also changes the default output format to" + echo " BAM," + echo " but this can be overridden by the explicit format options or using a" + echo " filename" + echo " with a known suffix." + echo " This option saves time spent on compression/decompression and is thus" + echo " preferred" + echo " when the output is piped to another samtools command." + echo "" + echo " --with_header" + echo " type: boolean_true" + echo " Include the header in the output." + echo "" + echo " -H, --header_only" + echo " type: boolean_true" + echo " Output the header only." + echo "" + echo " --no_header" + echo " type: boolean_true" + echo " When producing SAM format, output alignment records but not headers." + echo " This is the default; the option can be used to reset the effect of" + echo " --with_header/--header_only." + echo "" + echo " -c, --count" + echo " type: boolean_true" + echo " Instead of printing the alignments, only count them and print the total" + echo " number." + echo " All filter options, such as --require_flags, --excl_flags, and --min_MQ," + echo " are taken" + echo " into account. The --unmap option is ignored in this mode." + echo "" + echo " -U, --output_unselected" + echo " type: file, file must exist" + echo " Write alignments that are not selected by the various filter options to" + echo " FILE." + echo " When this option is used, all alignments (or all alignments intersecting" + echo " the regions" + echo " specified) are written to either the output file or this file, but never" + echo " both." + echo "" + echo " -p, --unmap" + echo " type: boolean_true" + echo " Set the UNMAP flag on alignments that are not selected by the filter" + echo " options." + echo " These alignments are then written to the normal output. This is not" + echo " compatible" + echo " with --output_unselected." + echo "" + echo " -r, --read_group" + echo " type: string" + echo " Output alignments in read group STR [null]. Note that records with no RG" + echo " tag will also be output" + echo " when using this option. This behaviour may change in a future release." + echo "" + echo " -d, --tag" + echo " type: string" + echo " Only output alignments with tag STR1 and associated value STR2, which" + echo " can be a string or an integer" + echo " [null]." + echo " The value can be omitted, in which case only the tag is considered." + echo " Note that this option does not specify a tag type. For example, use" + echo " --tag XX:42 to select alignments" + echo " with an XX:i:42 field, not --tag XX:i:42." + echo "" + echo " -D, --tag_file" + echo " type: file, file must exist" + echo " Only output alignments with tag STR and associated values listed in" + echo " FILE." + echo "" + echo " -q, --min_MQ" + echo " type: integer" + echo " default: 0" + echo " Skip alignments with MAPQ smaller than INT." + echo "" + echo " -l, --library" + echo " type: string" + echo " Only output alignments in library STR." + echo "" + echo " -m, --min_qlen" + echo " type: integer" + echo " default: 0" + echo " Only output alignments with number of CIGAR bases consuming query" + echo " sequence >= INT." + echo "" + echo " -e, --expr" + echo " type: string" + echo " Only include alignments that match the filter expression STR. The syntax" + echo " for these expressions is" + echo " described in the main samtools." + echo "" + echo " -f, --require_flags" + echo " type: string" + echo " Only output alignments with all bits set in FLAG present in the FLAG" + echo " field. FLAG can be specified" + echo " in hex by beginning with \`0x' (i.e. /^0x[0-9A-F]+/), in octal by" + echo " beginning with \`0' (i.e. /^0[0-7]+/)," + echo " as a decimal number not beginning with '0' or as a comma-separated list" + echo " of flag names." + echo "" + echo " -F, --excl_flags" + echo " type: string" + echo " Do not output alignments with any bits set in FLAG present in the FLAG" + echo " field. FLAG can be specified" + echo " in hex by beginning with \`0x' (i.e. /^0x[0-9A-F]+/), in octal by" + echo " beginning with \`0' (i.e. /^0[0-7]+/)," + echo " as a decimal number not beginning with '0' or as a comma-separated list" + echo " of flag names." + echo "" + echo " -G, --excl_all_flags" + echo " type: integer" + echo " Do not output alignments with all bits set in INT present in the FLAG" + echo " field. This is the opposite of" + echo " --require_flags such that --require_flags 12 --exclude_all_flags 12 is" + echo " the same as no filtering at all." + echo " FLAG can be specified in hex by beginning with \`0x' (i.e." + echo " /^0x[0-9A-F]+/), in octal by beginning with \`0'" + echo " (i.e. /^0[0-7]+/), as a decimal number not beginning with '0' or as a" + echo " comma-separated list of flag names." + echo "" + echo " --rf, --incl_flags" + echo " type: string" + echo " Only output alignments with any bit set in FLAG present in the FLAG" + echo " field. FLAG can be specified in hex" + echo " by beginning with \`0x' (i.e. /^0x[0-9A-F]+/), in octal by beginning with" + echo " \`0' (i.e. /^0[0-7]+/), as a decimal" + echo " number not beginning with '0' or as a comma-separated list of flag" + echo " names." + echo "" + echo " -x, --remove_tag" + echo " type: string" + echo " Read tag(s) to exclude from output (repeatable) [null]. This can be a" + echo " single tag or a comma separated list." + echo " Alternatively the option itself can be repeated multiple times." + echo " If the list starts with a \`^' then it is negated and treated as a" + echo " request to remove all tags except those in STR." + echo " The list may be empty, so --remove_tag ^ will remove all tags." + echo " Note that tags will only be removed from reads that pass filtering." + echo "" + echo " --keep_tag" + echo " type: string" + echo " This keeps only tags listed in STR and is directly equivalent to" + echo " --remove_tag ^STR. Specifying an empty list" + echo " will remove all tags. If both --keep_tag and --remove_tag are specified" + echo " then --keep_tag has precedence." + echo " Note that tags will only be removed from reads that pass filtering." + echo "" + echo " -B, --remove_B" + echo " type: boolean_true" + echo " Collapse the backward CIGAR operation." + echo "" + echo " --add_flags" + echo " type: string" + echo " Adds flag(s) to read. FLAG can be specified in hex by beginning with" + echo " \`0x' (i.e. /^0x[0-9A-F]+/), in octal" + echo " by beginning with \`0' (i.e. /^0[0-7]+/), as a decimal number not" + echo " beginning with '0' or as a comma-separated" + echo " list of flag names." + echo "" + echo " --remove_flags" + echo " type: string" + echo " Remove flag(s) from read. FLAG is specified in the same way as with the" + echo " --add_flags option." + echo "" + echo " --subsample" + echo " type: double" + echo " Output only a proportion of the input alignments, as specified by 0.0 <=" + echo " FLOAT <= 1.0, which gives the fraction" + echo " of templates/pairs to be kept. This subsampling acts in the same way on" + echo " all of the alignment records in the same" + echo " template or read pair, so it never keeps a read but not its mate." + echo "" + echo " --subsample_seed" + echo " type: integer" + echo " default: 0" + echo " Subsampling seed used to influence which subset of reads is kept. When" + echo " subsampling data that has previously" + echo " been subsampled, be sure to use a different seed value from those used" + echo " previously; otherwise more reads will" + echo " be retained than expected." + echo "" + echo " -P, --fetch_pairs" + echo " type: boolean_true" + echo " Retrieve pairs even when the mate is outside of the requested region." + echo " Enabling this option also turns on the" + echo " multi-region iterator (-M). A region to search must be specified, either" + echo " on the command-line, or using the" + echo " --target_file option. The input file must be an indexed regular file." + echo " This option first scans the requested region, using the RNEXT and PNEXT" + echo " fields of the records that have the" + echo " PAIRED flag set and pass other filtering options to find where paired" + echo " reads are located. These locations are" + echo " used to build an expanded region list, and a set of QNAMEs to allow from" + echo " the new regions. It will then make" + echo " a second pass, collecting all reads from the originally-specified region" + echo " list together with reads from additional" + echo " locations that match the allowed set of QNAMEs. Any other filtering" + echo " options used will be applied to all reads" + echo " found during this second pass." + echo " As this option links reads using RNEXT and PNEXT, it is important that" + echo " these fields are set accurately. Use" + echo " 'samtools fixmate' to correct them if necessary." + echo " Note that this option does not work with the --count," + echo " --output-unselected or --unmap options." + echo "" + echo " -X, --customized_index" + echo " type: boolean_true" + echo " Include customized index file as a part of arguments. See EXAMPLES" + echo " section for sample of usage." + echo "" + echo " -z, --sanitize" + echo " type: string" + echo " Perform some sanity checks on the state of SAM record fields, fixing up" + echo " common mistakes made by aligners." + echo " These include soft-clipping alignments when they extend beyond the end" + echo " of the reference, marking records as" + echo " unmapped when they have reference * or position 0, and ensuring unmapped" + echo " alignments have no CIGAR or mapping" + echo " quality for unmapped alignments and no MD, NM, CG or SM tags." + echo " FLAGs is a comma-separated list of keywords chosen from the following" + echo " list." + echo " unmap: The UNMAPPED BAM flag. This is set for reads with position <= 0," + echo " reference name \"*\" or reads starting" + echo " beyond the end of the reference. Note CIGAR \"*\" is permitted for mapped" + echo " data so does not trigger this." + echo " pos: Position and reference name fields. These may be cleared when a" + echo " sequence is unmapped due to the" + echo " coordinates being beyond the end of the reference. Selecting this may" + echo " change the sort order of the file," + echo " so it is not a part of the on compound argument." + echo " mqual: Mapping quality. This is set to zero for unmapped reads." + echo " cigar: Modifies CIGAR fields, either by adding soft-clips for reads that" + echo " overlap the end of the reference or" + echo " by clearing it for unmapped reads." + echo " aux: For unmapped data, some auxiliary fields are meaningless and will" + echo " be removed. These include NM, MD, CG and SM." + echo " off: Perform no sanity fixing. This is the default" + echo " on: Sanitize data in a way that guarantees the same sort order. This is" + echo " everything except for pos." + echo " all: All sanitizing options, including pos." + echo "" + echo " --no_PG" + echo " type: boolean_true" + echo " Do not add a @PG line to the header of the output file." + echo "" + echo " --input_fmt_option" + echo " type: string" + echo " Specify a single input file format option in the form of OPTION or" + echo " OPTION=VALUE." + echo "" + echo " -O, --output_fmt" + echo " type: string" + echo " Specify output format (SAM, BAM, CRAM)." + echo "" + echo " --output_fmt_option" + echo " type: string" + echo " Specify a single output file format option in the form of OPTION or" + echo " OPTION=VALUE." + echo "" + echo " --write_index" + echo " type: boolean_true" + echo " Automatically index the output files." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1 +ENTRYPOINT [] +RUN samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \ +sed 's#Using ##;s# \([0-9\.]*\)$#: \1#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component samtools samtools_view" +LABEL org.opencontainers.image.created="2024-06-24T08:36:38Z" +LABEL org.opencontainers.image.source="https://github.com/samtools/samtools" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "samtools_view main" + exit + ;; + --input) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + [ -n "$VIASH_PAR_INPUT" ] && ViashError Bad arguments for option \'--input=*\': \'$VIASH_PAR_INPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --fai_reference) + [ -n "$VIASH_PAR_FAI_REFERENCE" ] && ViashError Bad arguments for option \'--fai_reference\': \'$VIASH_PAR_FAI_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FAI_REFERENCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --fai_reference. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --fai_reference=*) + [ -n "$VIASH_PAR_FAI_REFERENCE" ] && ViashError Bad arguments for option \'--fai_reference=*\': \'$VIASH_PAR_FAI_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FAI_REFERENCE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -t) + [ -n "$VIASH_PAR_FAI_REFERENCE" ] && ViashError Bad arguments for option \'-t\': \'$VIASH_PAR_FAI_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FAI_REFERENCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -t. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --reference) + [ -n "$VIASH_PAR_REFERENCE" ] && ViashError Bad arguments for option \'--reference\': \'$VIASH_PAR_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REFERENCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --reference. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --reference=*) + [ -n "$VIASH_PAR_REFERENCE" ] && ViashError Bad arguments for option \'--reference=*\': \'$VIASH_PAR_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REFERENCE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -T) + [ -n "$VIASH_PAR_REFERENCE" ] && ViashError Bad arguments for option \'-T\': \'$VIASH_PAR_REFERENCE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REFERENCE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -T. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --target_file) + [ -n "$VIASH_PAR_TARGET_FILE" ] && ViashError Bad arguments for option \'--target_file\': \'$VIASH_PAR_TARGET_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TARGET_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --target_file. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --target_file=*) + [ -n "$VIASH_PAR_TARGET_FILE" ] && ViashError Bad arguments for option \'--target_file=*\': \'$VIASH_PAR_TARGET_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TARGET_FILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -L) + [ -n "$VIASH_PAR_TARGET_FILE" ] && ViashError Bad arguments for option \'-L\': \'$VIASH_PAR_TARGET_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TARGET_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -L. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --region_file) + [ -n "$VIASH_PAR_REGION_FILE" ] && ViashError Bad arguments for option \'--region_file\': \'$VIASH_PAR_REGION_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REGION_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --region_file. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --region_file=*) + [ -n "$VIASH_PAR_REGION_FILE" ] && ViashError Bad arguments for option \'--region_file=*\': \'$VIASH_PAR_REGION_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REGION_FILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --qname_file) + [ -n "$VIASH_PAR_QNAME_FILE" ] && ViashError Bad arguments for option \'--qname_file\': \'$VIASH_PAR_QNAME_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QNAME_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --qname_file. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --qname_file=*) + [ -n "$VIASH_PAR_QNAME_FILE" ] && ViashError Bad arguments for option \'--qname_file=*\': \'$VIASH_PAR_QNAME_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QNAME_FILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -N) + [ -n "$VIASH_PAR_QNAME_FILE" ] && ViashError Bad arguments for option \'-N\': \'$VIASH_PAR_QNAME_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QNAME_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -N. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read_group_file) + [ -n "$VIASH_PAR_READ_GROUP_FILE" ] && ViashError Bad arguments for option \'--read_group_file\': \'$VIASH_PAR_READ_GROUP_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_GROUP_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --read_group_file. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read_group_file=*) + [ -n "$VIASH_PAR_READ_GROUP_FILE" ] && ViashError Bad arguments for option \'--read_group_file=*\': \'$VIASH_PAR_READ_GROUP_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_GROUP_FILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -R) + [ -n "$VIASH_PAR_READ_GROUP_FILE" ] && ViashError Bad arguments for option \'-R\': \'$VIASH_PAR_READ_GROUP_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_GROUP_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -R. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --use_index) + [ -n "$VIASH_PAR_USE_INDEX" ] && ViashError Bad arguments for option \'--use_index\': \'$VIASH_PAR_USE_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_USE_INDEX=true + shift 1 + ;; + -M) + [ -n "$VIASH_PAR_USE_INDEX" ] && ViashError Bad arguments for option \'-M\': \'$VIASH_PAR_USE_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_USE_INDEX=true + shift 1 + ;; + --output) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output=*) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'--output=*\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -o) + [ -n "$VIASH_PAR_OUTPUT" ] && ViashError Bad arguments for option \'-o\': \'$VIASH_PAR_OUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -o. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bam) + [ -n "$VIASH_PAR_BAM" ] && ViashError Bad arguments for option \'--bam\': \'$VIASH_PAR_BAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAM=true + shift 1 + ;; + -b) + [ -n "$VIASH_PAR_BAM" ] && ViashError Bad arguments for option \'-b\': \'$VIASH_PAR_BAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAM=true + shift 1 + ;; + --cram) + [ -n "$VIASH_PAR_CRAM" ] && ViashError Bad arguments for option \'--cram\': \'$VIASH_PAR_CRAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CRAM=true + shift 1 + ;; + -C) + [ -n "$VIASH_PAR_CRAM" ] && ViashError Bad arguments for option \'-C\': \'$VIASH_PAR_CRAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CRAM=true + shift 1 + ;; + --fast) + [ -n "$VIASH_PAR_FAST" ] && ViashError Bad arguments for option \'--fast\': \'$VIASH_PAR_FAST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FAST=true + shift 1 + ;; + --uncompressed) + [ -n "$VIASH_PAR_UNCOMPRESSED" ] && ViashError Bad arguments for option \'--uncompressed\': \'$VIASH_PAR_UNCOMPRESSED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNCOMPRESSED=true + shift 1 + ;; + -u) + [ -n "$VIASH_PAR_UNCOMPRESSED" ] && ViashError Bad arguments for option \'-u\': \'$VIASH_PAR_UNCOMPRESSED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNCOMPRESSED=true + shift 1 + ;; + --with_header) + [ -n "$VIASH_PAR_WITH_HEADER" ] && ViashError Bad arguments for option \'--with_header\': \'$VIASH_PAR_WITH_HEADER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WITH_HEADER=true + shift 1 + ;; + --header_only) + [ -n "$VIASH_PAR_HEADER_ONLY" ] && ViashError Bad arguments for option \'--header_only\': \'$VIASH_PAR_HEADER_ONLY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HEADER_ONLY=true + shift 1 + ;; + -H) + [ -n "$VIASH_PAR_HEADER_ONLY" ] && ViashError Bad arguments for option \'-H\': \'$VIASH_PAR_HEADER_ONLY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_HEADER_ONLY=true + shift 1 + ;; + --no_header) + [ -n "$VIASH_PAR_NO_HEADER" ] && ViashError Bad arguments for option \'--no_header\': \'$VIASH_PAR_NO_HEADER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_HEADER=true + shift 1 + ;; + --count) + [ -n "$VIASH_PAR_COUNT" ] && ViashError Bad arguments for option \'--count\': \'$VIASH_PAR_COUNT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COUNT=true + shift 1 + ;; + -c) + [ -n "$VIASH_PAR_COUNT" ] && ViashError Bad arguments for option \'-c\': \'$VIASH_PAR_COUNT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_COUNT=true + shift 1 + ;; + --output_unselected) + [ -n "$VIASH_PAR_OUTPUT_UNSELECTED" ] && ViashError Bad arguments for option \'--output_unselected\': \'$VIASH_PAR_OUTPUT_UNSELECTED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_UNSELECTED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_unselected. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_unselected=*) + [ -n "$VIASH_PAR_OUTPUT_UNSELECTED" ] && ViashError Bad arguments for option \'--output_unselected=*\': \'$VIASH_PAR_OUTPUT_UNSELECTED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_UNSELECTED=$(ViashRemoveFlags "$1") + shift 1 + ;; + -U) + [ -n "$VIASH_PAR_OUTPUT_UNSELECTED" ] && ViashError Bad arguments for option \'-U\': \'$VIASH_PAR_OUTPUT_UNSELECTED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_UNSELECTED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -U. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unmap) + [ -n "$VIASH_PAR_UNMAP" ] && ViashError Bad arguments for option \'--unmap\': \'$VIASH_PAR_UNMAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNMAP=true + shift 1 + ;; + -p) + [ -n "$VIASH_PAR_UNMAP" ] && ViashError Bad arguments for option \'-p\': \'$VIASH_PAR_UNMAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNMAP=true + shift 1 + ;; + --read_group) + [ -n "$VIASH_PAR_READ_GROUP" ] && ViashError Bad arguments for option \'--read_group\': \'$VIASH_PAR_READ_GROUP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_GROUP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --read_group. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --read_group=*) + [ -n "$VIASH_PAR_READ_GROUP" ] && ViashError Bad arguments for option \'--read_group=*\': \'$VIASH_PAR_READ_GROUP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_GROUP=$(ViashRemoveFlags "$1") + shift 1 + ;; + -r) + [ -n "$VIASH_PAR_READ_GROUP" ] && ViashError Bad arguments for option \'-r\': \'$VIASH_PAR_READ_GROUP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READ_GROUP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -r. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tag) + [ -n "$VIASH_PAR_TAG" ] && ViashError Bad arguments for option \'--tag\': \'$VIASH_PAR_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --tag. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tag=*) + [ -n "$VIASH_PAR_TAG" ] && ViashError Bad arguments for option \'--tag=*\': \'$VIASH_PAR_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + -d) + [ -n "$VIASH_PAR_TAG" ] && ViashError Bad arguments for option \'-d\': \'$VIASH_PAR_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -d. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tag_file) + [ -n "$VIASH_PAR_TAG_FILE" ] && ViashError Bad arguments for option \'--tag_file\': \'$VIASH_PAR_TAG_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAG_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --tag_file. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --tag_file=*) + [ -n "$VIASH_PAR_TAG_FILE" ] && ViashError Bad arguments for option \'--tag_file=*\': \'$VIASH_PAR_TAG_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAG_FILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -D) + [ -n "$VIASH_PAR_TAG_FILE" ] && ViashError Bad arguments for option \'-D\': \'$VIASH_PAR_TAG_FILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TAG_FILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -D. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_MQ) + [ -n "$VIASH_PAR_MIN_MQ" ] && ViashError Bad arguments for option \'--min_MQ\': \'$VIASH_PAR_MIN_MQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_MQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_MQ. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_MQ=*) + [ -n "$VIASH_PAR_MIN_MQ" ] && ViashError Bad arguments for option \'--min_MQ=*\': \'$VIASH_PAR_MIN_MQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_MQ=$(ViashRemoveFlags "$1") + shift 1 + ;; + -q) + [ -n "$VIASH_PAR_MIN_MQ" ] && ViashError Bad arguments for option \'-q\': \'$VIASH_PAR_MIN_MQ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_MQ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -q. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --library) + [ -n "$VIASH_PAR_LIBRARY" ] && ViashError Bad arguments for option \'--library\': \'$VIASH_PAR_LIBRARY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIBRARY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --library. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --library=*) + [ -n "$VIASH_PAR_LIBRARY" ] && ViashError Bad arguments for option \'--library=*\': \'$VIASH_PAR_LIBRARY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIBRARY=$(ViashRemoveFlags "$1") + shift 1 + ;; + -l) + [ -n "$VIASH_PAR_LIBRARY" ] && ViashError Bad arguments for option \'-l\': \'$VIASH_PAR_LIBRARY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIBRARY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -l. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_qlen) + [ -n "$VIASH_PAR_MIN_QLEN" ] && ViashError Bad arguments for option \'--min_qlen\': \'$VIASH_PAR_MIN_QLEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_QLEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --min_qlen. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --min_qlen=*) + [ -n "$VIASH_PAR_MIN_QLEN" ] && ViashError Bad arguments for option \'--min_qlen=*\': \'$VIASH_PAR_MIN_QLEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_QLEN=$(ViashRemoveFlags "$1") + shift 1 + ;; + -m) + [ -n "$VIASH_PAR_MIN_QLEN" ] && ViashError Bad arguments for option \'-m\': \'$VIASH_PAR_MIN_QLEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_MIN_QLEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -m. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --expr) + [ -n "$VIASH_PAR_EXPR" ] && ViashError Bad arguments for option \'--expr\': \'$VIASH_PAR_EXPR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXPR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --expr. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --expr=*) + [ -n "$VIASH_PAR_EXPR" ] && ViashError Bad arguments for option \'--expr=*\': \'$VIASH_PAR_EXPR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXPR=$(ViashRemoveFlags "$1") + shift 1 + ;; + -e) + [ -n "$VIASH_PAR_EXPR" ] && ViashError Bad arguments for option \'-e\': \'$VIASH_PAR_EXPR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXPR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -e. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --require_flags) + [ -n "$VIASH_PAR_REQUIRE_FLAGS" ] && ViashError Bad arguments for option \'--require_flags\': \'$VIASH_PAR_REQUIRE_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REQUIRE_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --require_flags. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --require_flags=*) + [ -n "$VIASH_PAR_REQUIRE_FLAGS" ] && ViashError Bad arguments for option \'--require_flags=*\': \'$VIASH_PAR_REQUIRE_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REQUIRE_FLAGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -f) + [ -n "$VIASH_PAR_REQUIRE_FLAGS" ] && ViashError Bad arguments for option \'-f\': \'$VIASH_PAR_REQUIRE_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REQUIRE_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -f. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --excl_flags) + [ -n "$VIASH_PAR_EXCL_FLAGS" ] && ViashError Bad arguments for option \'--excl_flags\': \'$VIASH_PAR_EXCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --excl_flags. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --excl_flags=*) + [ -n "$VIASH_PAR_EXCL_FLAGS" ] && ViashError Bad arguments for option \'--excl_flags=*\': \'$VIASH_PAR_EXCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_FLAGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -F) + [ -n "$VIASH_PAR_EXCL_FLAGS" ] && ViashError Bad arguments for option \'-F\': \'$VIASH_PAR_EXCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -F. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --excl_all_flags) + [ -n "$VIASH_PAR_EXCL_ALL_FLAGS" ] && ViashError Bad arguments for option \'--excl_all_flags\': \'$VIASH_PAR_EXCL_ALL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_ALL_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --excl_all_flags. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --excl_all_flags=*) + [ -n "$VIASH_PAR_EXCL_ALL_FLAGS" ] && ViashError Bad arguments for option \'--excl_all_flags=*\': \'$VIASH_PAR_EXCL_ALL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_ALL_FLAGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + -G) + [ -n "$VIASH_PAR_EXCL_ALL_FLAGS" ] && ViashError Bad arguments for option \'-G\': \'$VIASH_PAR_EXCL_ALL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_EXCL_ALL_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -G. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --incl_flags) + [ -n "$VIASH_PAR_INCL_FLAGS" ] && ViashError Bad arguments for option \'--incl_flags\': \'$VIASH_PAR_INCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INCL_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --incl_flags. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --incl_flags=*) + [ -n "$VIASH_PAR_INCL_FLAGS" ] && ViashError Bad arguments for option \'--incl_flags=*\': \'$VIASH_PAR_INCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INCL_FLAGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --rf) + [ -n "$VIASH_PAR_INCL_FLAGS" ] && ViashError Bad arguments for option \'--rf\': \'$VIASH_PAR_INCL_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INCL_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --rf. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --remove_tag) + [ -n "$VIASH_PAR_REMOVE_TAG" ] && ViashError Bad arguments for option \'--remove_tag\': \'$VIASH_PAR_REMOVE_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REMOVE_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --remove_tag. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --remove_tag=*) + [ -n "$VIASH_PAR_REMOVE_TAG" ] && ViashError Bad arguments for option \'--remove_tag=*\': \'$VIASH_PAR_REMOVE_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REMOVE_TAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + -x) + [ -n "$VIASH_PAR_REMOVE_TAG" ] && ViashError Bad arguments for option \'-x\': \'$VIASH_PAR_REMOVE_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REMOVE_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -x. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --keep_tag) + [ -n "$VIASH_PAR_KEEP_TAG" ] && ViashError Bad arguments for option \'--keep_tag\': \'$VIASH_PAR_KEEP_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KEEP_TAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --keep_tag. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --keep_tag=*) + [ -n "$VIASH_PAR_KEEP_TAG" ] && ViashError Bad arguments for option \'--keep_tag=*\': \'$VIASH_PAR_KEEP_TAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_KEEP_TAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + --remove_B) + [ -n "$VIASH_PAR_REMOVE_B" ] && ViashError Bad arguments for option \'--remove_B\': \'$VIASH_PAR_REMOVE_B\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REMOVE_B=true + shift 1 + ;; + -B) + [ -n "$VIASH_PAR_REMOVE_B" ] && ViashError Bad arguments for option \'-B\': \'$VIASH_PAR_REMOVE_B\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REMOVE_B=true + shift 1 + ;; + --add_flags) + [ -n "$VIASH_PAR_ADD_FLAGS" ] && ViashError Bad arguments for option \'--add_flags\': \'$VIASH_PAR_ADD_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADD_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --add_flags. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --add_flags=*) + [ -n "$VIASH_PAR_ADD_FLAGS" ] && ViashError Bad arguments for option \'--add_flags=*\': \'$VIASH_PAR_ADD_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ADD_FLAGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --remove_flags) + [ -n "$VIASH_PAR_REMOVE_FLAGS" ] && ViashError Bad arguments for option \'--remove_flags\': \'$VIASH_PAR_REMOVE_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REMOVE_FLAGS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --remove_flags. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --remove_flags=*) + [ -n "$VIASH_PAR_REMOVE_FLAGS" ] && ViashError Bad arguments for option \'--remove_flags=*\': \'$VIASH_PAR_REMOVE_FLAGS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_REMOVE_FLAGS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --subsample) + [ -n "$VIASH_PAR_SUBSAMPLE" ] && ViashError Bad arguments for option \'--subsample\': \'$VIASH_PAR_SUBSAMPLE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUBSAMPLE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --subsample. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --subsample=*) + [ -n "$VIASH_PAR_SUBSAMPLE" ] && ViashError Bad arguments for option \'--subsample=*\': \'$VIASH_PAR_SUBSAMPLE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUBSAMPLE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --subsample_seed) + [ -n "$VIASH_PAR_SUBSAMPLE_SEED" ] && ViashError Bad arguments for option \'--subsample_seed\': \'$VIASH_PAR_SUBSAMPLE_SEED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUBSAMPLE_SEED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --subsample_seed. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --subsample_seed=*) + [ -n "$VIASH_PAR_SUBSAMPLE_SEED" ] && ViashError Bad arguments for option \'--subsample_seed=*\': \'$VIASH_PAR_SUBSAMPLE_SEED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SUBSAMPLE_SEED=$(ViashRemoveFlags "$1") + shift 1 + ;; + --fetch_pairs) + [ -n "$VIASH_PAR_FETCH_PAIRS" ] && ViashError Bad arguments for option \'--fetch_pairs\': \'$VIASH_PAR_FETCH_PAIRS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FETCH_PAIRS=true + shift 1 + ;; + -P) + [ -n "$VIASH_PAR_FETCH_PAIRS" ] && ViashError Bad arguments for option \'-P\': \'$VIASH_PAR_FETCH_PAIRS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_FETCH_PAIRS=true + shift 1 + ;; + --customized_index) + [ -n "$VIASH_PAR_CUSTOMIZED_INDEX" ] && ViashError Bad arguments for option \'--customized_index\': \'$VIASH_PAR_CUSTOMIZED_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUSTOMIZED_INDEX=true + shift 1 + ;; + -X) + [ -n "$VIASH_PAR_CUSTOMIZED_INDEX" ] && ViashError Bad arguments for option \'-X\': \'$VIASH_PAR_CUSTOMIZED_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CUSTOMIZED_INDEX=true + shift 1 + ;; + --sanitize) + [ -n "$VIASH_PAR_SANITIZE" ] && ViashError Bad arguments for option \'--sanitize\': \'$VIASH_PAR_SANITIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SANITIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sanitize. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sanitize=*) + [ -n "$VIASH_PAR_SANITIZE" ] && ViashError Bad arguments for option \'--sanitize=*\': \'$VIASH_PAR_SANITIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SANITIZE=$(ViashRemoveFlags "$1") + shift 1 + ;; + -z) + [ -n "$VIASH_PAR_SANITIZE" ] && ViashError Bad arguments for option \'-z\': \'$VIASH_PAR_SANITIZE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SANITIZE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -z. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --no_PG) + [ -n "$VIASH_PAR_NO_PG" ] && ViashError Bad arguments for option \'--no_PG\': \'$VIASH_PAR_NO_PG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_NO_PG=true + shift 1 + ;; + --input_fmt_option) + [ -n "$VIASH_PAR_INPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--input_fmt_option\': \'$VIASH_PAR_INPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_FMT_OPTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input_fmt_option. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input_fmt_option=*) + [ -n "$VIASH_PAR_INPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--input_fmt_option=*\': \'$VIASH_PAR_INPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INPUT_FMT_OPTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --output_fmt) + [ -n "$VIASH_PAR_OUTPUT_FMT" ] && ViashError Bad arguments for option \'--output_fmt\': \'$VIASH_PAR_OUTPUT_FMT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_fmt. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_fmt=*) + [ -n "$VIASH_PAR_OUTPUT_FMT" ] && ViashError Bad arguments for option \'--output_fmt=*\': \'$VIASH_PAR_OUTPUT_FMT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT=$(ViashRemoveFlags "$1") + shift 1 + ;; + -O) + [ -n "$VIASH_PAR_OUTPUT_FMT" ] && ViashError Bad arguments for option \'-O\': \'$VIASH_PAR_OUTPUT_FMT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to -O. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_fmt_option) + [ -n "$VIASH_PAR_OUTPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--output_fmt_option\': \'$VIASH_PAR_OUTPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT_OPTION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --output_fmt_option. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --output_fmt_option=*) + [ -n "$VIASH_PAR_OUTPUT_FMT_OPTION" ] && ViashError Bad arguments for option \'--output_fmt_option=*\': \'$VIASH_PAR_OUTPUT_FMT_OPTION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTPUT_FMT_OPTION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --write_index) + [ -n "$VIASH_PAR_WRITE_INDEX" ] && ViashError Bad arguments for option \'--write_index\': \'$VIASH_PAR_WRITE_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WRITE_INDEX=true + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/samtools/samtools_view:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_OUTPUT+x} ]; then + ViashError '--output' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# filling in defaults +if [ -z ${VIASH_PAR_USE_INDEX+x} ]; then + VIASH_PAR_USE_INDEX="false" +fi +if [ -z ${VIASH_PAR_BAM+x} ]; then + VIASH_PAR_BAM="false" +fi +if [ -z ${VIASH_PAR_CRAM+x} ]; then + VIASH_PAR_CRAM="false" +fi +if [ -z ${VIASH_PAR_FAST+x} ]; then + VIASH_PAR_FAST="false" +fi +if [ -z ${VIASH_PAR_UNCOMPRESSED+x} ]; then + VIASH_PAR_UNCOMPRESSED="false" +fi +if [ -z ${VIASH_PAR_WITH_HEADER+x} ]; then + VIASH_PAR_WITH_HEADER="false" +fi +if [ -z ${VIASH_PAR_HEADER_ONLY+x} ]; then + VIASH_PAR_HEADER_ONLY="false" +fi +if [ -z ${VIASH_PAR_NO_HEADER+x} ]; then + VIASH_PAR_NO_HEADER="false" +fi +if [ -z ${VIASH_PAR_COUNT+x} ]; then + VIASH_PAR_COUNT="false" +fi +if [ -z ${VIASH_PAR_UNMAP+x} ]; then + VIASH_PAR_UNMAP="false" +fi +if [ -z ${VIASH_PAR_MIN_MQ+x} ]; then + VIASH_PAR_MIN_MQ="0" +fi +if [ -z ${VIASH_PAR_MIN_QLEN+x} ]; then + VIASH_PAR_MIN_QLEN="0" +fi +if [ -z ${VIASH_PAR_REMOVE_B+x} ]; then + VIASH_PAR_REMOVE_B="false" +fi +if [ -z ${VIASH_PAR_SUBSAMPLE_SEED+x} ]; then + VIASH_PAR_SUBSAMPLE_SEED="0" +fi +if [ -z ${VIASH_PAR_FETCH_PAIRS+x} ]; then + VIASH_PAR_FETCH_PAIRS="false" +fi +if [ -z ${VIASH_PAR_CUSTOMIZED_INDEX+x} ]; then + VIASH_PAR_CUSTOMIZED_INDEX="false" +fi +if [ -z ${VIASH_PAR_NO_PG+x} ]; then + VIASH_PAR_NO_PG="false" +fi +if [ -z ${VIASH_PAR_WRITE_INDEX+x} ]; then + VIASH_PAR_WRITE_INDEX="false" +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INPUT" ] && [ ! -e "$VIASH_PAR_INPUT" ]; then + ViashError "Input file '$VIASH_PAR_INPUT' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_FAI_REFERENCE" ] && [ ! -e "$VIASH_PAR_FAI_REFERENCE" ]; then + ViashError "Input file '$VIASH_PAR_FAI_REFERENCE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REFERENCE" ] && [ ! -e "$VIASH_PAR_REFERENCE" ]; then + ViashError "Input file '$VIASH_PAR_REFERENCE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_TARGET_FILE" ] && [ ! -e "$VIASH_PAR_TARGET_FILE" ]; then + ViashError "Input file '$VIASH_PAR_TARGET_FILE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_REGION_FILE" ] && [ ! -e "$VIASH_PAR_REGION_FILE" ]; then + ViashError "Input file '$VIASH_PAR_REGION_FILE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_QNAME_FILE" ] && [ ! -e "$VIASH_PAR_QNAME_FILE" ]; then + ViashError "Input file '$VIASH_PAR_QNAME_FILE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_READ_GROUP_FILE" ] && [ ! -e "$VIASH_PAR_READ_GROUP_FILE" ]; then + ViashError "Input file '$VIASH_PAR_READ_GROUP_FILE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_OUTPUT_UNSELECTED" ] && [ ! -e "$VIASH_PAR_OUTPUT_UNSELECTED" ]; then + ViashError "Input file '$VIASH_PAR_OUTPUT_UNSELECTED' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_TAG_FILE" ] && [ ! -e "$VIASH_PAR_TAG_FILE" ]; then + ViashError "Input file '$VIASH_PAR_TAG_FILE' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_USE_INDEX" ]]; then + if ! [[ "$VIASH_PAR_USE_INDEX" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--use_index' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BAM" ]]; then + if ! [[ "$VIASH_PAR_BAM" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--bam' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CRAM" ]]; then + if ! [[ "$VIASH_PAR_CRAM" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--cram' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FAST" ]]; then + if ! [[ "$VIASH_PAR_FAST" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--fast' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_UNCOMPRESSED" ]]; then + if ! [[ "$VIASH_PAR_UNCOMPRESSED" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--uncompressed' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WITH_HEADER" ]]; then + if ! [[ "$VIASH_PAR_WITH_HEADER" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--with_header' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_HEADER_ONLY" ]]; then + if ! [[ "$VIASH_PAR_HEADER_ONLY" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--header_only' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_HEADER" ]]; then + if ! [[ "$VIASH_PAR_NO_HEADER" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_header' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_COUNT" ]]; then + if ! [[ "$VIASH_PAR_COUNT" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--count' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_UNMAP" ]]; then + if ! [[ "$VIASH_PAR_UNMAP" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--unmap' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_MQ" ]]; then + if ! [[ "$VIASH_PAR_MIN_MQ" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_MQ' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_MIN_QLEN" ]]; then + if ! [[ "$VIASH_PAR_MIN_QLEN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--min_qlen' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_EXCL_ALL_FLAGS" ]]; then + if ! [[ "$VIASH_PAR_EXCL_ALL_FLAGS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--excl_all_flags' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_REMOVE_B" ]]; then + if ! [[ "$VIASH_PAR_REMOVE_B" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--remove_B' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SUBSAMPLE" ]]; then + if ! [[ "$VIASH_PAR_SUBSAMPLE" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--subsample' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SUBSAMPLE_SEED" ]]; then + if ! [[ "$VIASH_PAR_SUBSAMPLE_SEED" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--subsample_seed' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_FETCH_PAIRS" ]]; then + if ! [[ "$VIASH_PAR_FETCH_PAIRS" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--fetch_pairs' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CUSTOMIZED_INDEX" ]]; then + if ! [[ "$VIASH_PAR_CUSTOMIZED_INDEX" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--customized_index' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_NO_PG" ]]; then + if ! [[ "$VIASH_PAR_NO_PG" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--no_PG' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WRITE_INDEX" ]]; then + if ! [[ "$VIASH_PAR_WRITE_INDEX" =~ ^(true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO)$ ]]; then + ViashError '--write_index' has to be a boolean_true. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -d "$(dirname "$VIASH_PAR_OUTPUT")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_OUTPUT")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INPUT")" ) + VIASH_PAR_INPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_INPUT") +fi +if [ ! -z "$VIASH_PAR_FAI_REFERENCE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_FAI_REFERENCE")" ) + VIASH_PAR_FAI_REFERENCE=$(ViashDockerAutodetectMount "$VIASH_PAR_FAI_REFERENCE") +fi +if [ ! -z "$VIASH_PAR_REFERENCE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REFERENCE")" ) + VIASH_PAR_REFERENCE=$(ViashDockerAutodetectMount "$VIASH_PAR_REFERENCE") +fi +if [ ! -z "$VIASH_PAR_TARGET_FILE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_TARGET_FILE")" ) + VIASH_PAR_TARGET_FILE=$(ViashDockerAutodetectMount "$VIASH_PAR_TARGET_FILE") +fi +if [ ! -z "$VIASH_PAR_REGION_FILE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_REGION_FILE")" ) + VIASH_PAR_REGION_FILE=$(ViashDockerAutodetectMount "$VIASH_PAR_REGION_FILE") +fi +if [ ! -z "$VIASH_PAR_QNAME_FILE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_QNAME_FILE")" ) + VIASH_PAR_QNAME_FILE=$(ViashDockerAutodetectMount "$VIASH_PAR_QNAME_FILE") +fi +if [ ! -z "$VIASH_PAR_READ_GROUP_FILE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_READ_GROUP_FILE")" ) + VIASH_PAR_READ_GROUP_FILE=$(ViashDockerAutodetectMount "$VIASH_PAR_READ_GROUP_FILE") +fi +if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT")" ) + VIASH_PAR_OUTPUT=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_OUTPUT" ) +fi +if [ ! -z "$VIASH_PAR_OUTPUT_UNSELECTED" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_OUTPUT_UNSELECTED")" ) + VIASH_PAR_OUTPUT_UNSELECTED=$(ViashDockerAutodetectMount "$VIASH_PAR_OUTPUT_UNSELECTED") +fi +if [ ! -z "$VIASH_PAR_TAG_FILE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_TAG_FILE")" ) + VIASH_PAR_TAG_FILE=$(ViashDockerAutodetectMount "$VIASH_PAR_TAG_FILE") +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-samtools_view-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_FAI_REFERENCE+x} ]; then echo "${VIASH_PAR_FAI_REFERENCE}" | sed "s#'#'\"'\"'#g;s#.*#par_fai_reference='&'#" ; else echo "# par_fai_reference="; fi ) +$( if [ ! -z ${VIASH_PAR_REFERENCE+x} ]; then echo "${VIASH_PAR_REFERENCE}" | sed "s#'#'\"'\"'#g;s#.*#par_reference='&'#" ; else echo "# par_reference="; fi ) +$( if [ ! -z ${VIASH_PAR_TARGET_FILE+x} ]; then echo "${VIASH_PAR_TARGET_FILE}" | sed "s#'#'\"'\"'#g;s#.*#par_target_file='&'#" ; else echo "# par_target_file="; fi ) +$( if [ ! -z ${VIASH_PAR_REGION_FILE+x} ]; then echo "${VIASH_PAR_REGION_FILE}" | sed "s#'#'\"'\"'#g;s#.*#par_region_file='&'#" ; else echo "# par_region_file="; fi ) +$( if [ ! -z ${VIASH_PAR_QNAME_FILE+x} ]; then echo "${VIASH_PAR_QNAME_FILE}" | sed "s#'#'\"'\"'#g;s#.*#par_qname_file='&'#" ; else echo "# par_qname_file="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_GROUP_FILE+x} ]; then echo "${VIASH_PAR_READ_GROUP_FILE}" | sed "s#'#'\"'\"'#g;s#.*#par_read_group_file='&'#" ; else echo "# par_read_group_file="; fi ) +$( if [ ! -z ${VIASH_PAR_USE_INDEX+x} ]; then echo "${VIASH_PAR_USE_INDEX}" | sed "s#'#'\"'\"'#g;s#.*#par_use_index='&'#" ; else echo "# par_use_index="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\"'\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_BAM+x} ]; then echo "${VIASH_PAR_BAM}" | sed "s#'#'\"'\"'#g;s#.*#par_bam='&'#" ; else echo "# par_bam="; fi ) +$( if [ ! -z ${VIASH_PAR_CRAM+x} ]; then echo "${VIASH_PAR_CRAM}" | sed "s#'#'\"'\"'#g;s#.*#par_cram='&'#" ; else echo "# par_cram="; fi ) +$( if [ ! -z ${VIASH_PAR_FAST+x} ]; then echo "${VIASH_PAR_FAST}" | sed "s#'#'\"'\"'#g;s#.*#par_fast='&'#" ; else echo "# par_fast="; fi ) +$( if [ ! -z ${VIASH_PAR_UNCOMPRESSED+x} ]; then echo "${VIASH_PAR_UNCOMPRESSED}" | sed "s#'#'\"'\"'#g;s#.*#par_uncompressed='&'#" ; else echo "# par_uncompressed="; fi ) +$( if [ ! -z ${VIASH_PAR_WITH_HEADER+x} ]; then echo "${VIASH_PAR_WITH_HEADER}" | sed "s#'#'\"'\"'#g;s#.*#par_with_header='&'#" ; else echo "# par_with_header="; fi ) +$( if [ ! -z ${VIASH_PAR_HEADER_ONLY+x} ]; then echo "${VIASH_PAR_HEADER_ONLY}" | sed "s#'#'\"'\"'#g;s#.*#par_header_only='&'#" ; else echo "# par_header_only="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_HEADER+x} ]; then echo "${VIASH_PAR_NO_HEADER}" | sed "s#'#'\"'\"'#g;s#.*#par_no_header='&'#" ; else echo "# par_no_header="; fi ) +$( if [ ! -z ${VIASH_PAR_COUNT+x} ]; then echo "${VIASH_PAR_COUNT}" | sed "s#'#'\"'\"'#g;s#.*#par_count='&'#" ; else echo "# par_count="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_UNSELECTED+x} ]; then echo "${VIASH_PAR_OUTPUT_UNSELECTED}" | sed "s#'#'\"'\"'#g;s#.*#par_output_unselected='&'#" ; else echo "# par_output_unselected="; fi ) +$( if [ ! -z ${VIASH_PAR_UNMAP+x} ]; then echo "${VIASH_PAR_UNMAP}" | sed "s#'#'\"'\"'#g;s#.*#par_unmap='&'#" ; else echo "# par_unmap="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_GROUP+x} ]; then echo "${VIASH_PAR_READ_GROUP}" | sed "s#'#'\"'\"'#g;s#.*#par_read_group='&'#" ; else echo "# par_read_group="; fi ) +$( if [ ! -z ${VIASH_PAR_TAG+x} ]; then echo "${VIASH_PAR_TAG}" | sed "s#'#'\"'\"'#g;s#.*#par_tag='&'#" ; else echo "# par_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_TAG_FILE+x} ]; then echo "${VIASH_PAR_TAG_FILE}" | sed "s#'#'\"'\"'#g;s#.*#par_tag_file='&'#" ; else echo "# par_tag_file="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_MQ+x} ]; then echo "${VIASH_PAR_MIN_MQ}" | sed "s#'#'\"'\"'#g;s#.*#par_min_MQ='&'#" ; else echo "# par_min_MQ="; fi ) +$( if [ ! -z ${VIASH_PAR_LIBRARY+x} ]; then echo "${VIASH_PAR_LIBRARY}" | sed "s#'#'\"'\"'#g;s#.*#par_library='&'#" ; else echo "# par_library="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_QLEN+x} ]; then echo "${VIASH_PAR_MIN_QLEN}" | sed "s#'#'\"'\"'#g;s#.*#par_min_qlen='&'#" ; else echo "# par_min_qlen="; fi ) +$( if [ ! -z ${VIASH_PAR_EXPR+x} ]; then echo "${VIASH_PAR_EXPR}" | sed "s#'#'\"'\"'#g;s#.*#par_expr='&'#" ; else echo "# par_expr="; fi ) +$( if [ ! -z ${VIASH_PAR_REQUIRE_FLAGS+x} ]; then echo "${VIASH_PAR_REQUIRE_FLAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_require_flags='&'#" ; else echo "# par_require_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCL_FLAGS+x} ]; then echo "${VIASH_PAR_EXCL_FLAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_excl_flags='&'#" ; else echo "# par_excl_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCL_ALL_FLAGS+x} ]; then echo "${VIASH_PAR_EXCL_ALL_FLAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_excl_all_flags='&'#" ; else echo "# par_excl_all_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_INCL_FLAGS+x} ]; then echo "${VIASH_PAR_INCL_FLAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_incl_flags='&'#" ; else echo "# par_incl_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_REMOVE_TAG+x} ]; then echo "${VIASH_PAR_REMOVE_TAG}" | sed "s#'#'\"'\"'#g;s#.*#par_remove_tag='&'#" ; else echo "# par_remove_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_TAG+x} ]; then echo "${VIASH_PAR_KEEP_TAG}" | sed "s#'#'\"'\"'#g;s#.*#par_keep_tag='&'#" ; else echo "# par_keep_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_REMOVE_B+x} ]; then echo "${VIASH_PAR_REMOVE_B}" | sed "s#'#'\"'\"'#g;s#.*#par_remove_B='&'#" ; else echo "# par_remove_B="; fi ) +$( if [ ! -z ${VIASH_PAR_ADD_FLAGS+x} ]; then echo "${VIASH_PAR_ADD_FLAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_add_flags='&'#" ; else echo "# par_add_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_REMOVE_FLAGS+x} ]; then echo "${VIASH_PAR_REMOVE_FLAGS}" | sed "s#'#'\"'\"'#g;s#.*#par_remove_flags='&'#" ; else echo "# par_remove_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_SUBSAMPLE+x} ]; then echo "${VIASH_PAR_SUBSAMPLE}" | sed "s#'#'\"'\"'#g;s#.*#par_subsample='&'#" ; else echo "# par_subsample="; fi ) +$( if [ ! -z ${VIASH_PAR_SUBSAMPLE_SEED+x} ]; then echo "${VIASH_PAR_SUBSAMPLE_SEED}" | sed "s#'#'\"'\"'#g;s#.*#par_subsample_seed='&'#" ; else echo "# par_subsample_seed="; fi ) +$( if [ ! -z ${VIASH_PAR_FETCH_PAIRS+x} ]; then echo "${VIASH_PAR_FETCH_PAIRS}" | sed "s#'#'\"'\"'#g;s#.*#par_fetch_pairs='&'#" ; else echo "# par_fetch_pairs="; fi ) +$( if [ ! -z ${VIASH_PAR_CUSTOMIZED_INDEX+x} ]; then echo "${VIASH_PAR_CUSTOMIZED_INDEX}" | sed "s#'#'\"'\"'#g;s#.*#par_customized_index='&'#" ; else echo "# par_customized_index="; fi ) +$( if [ ! -z ${VIASH_PAR_SANITIZE+x} ]; then echo "${VIASH_PAR_SANITIZE}" | sed "s#'#'\"'\"'#g;s#.*#par_sanitize='&'#" ; else echo "# par_sanitize="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_PG+x} ]; then echo "${VIASH_PAR_NO_PG}" | sed "s#'#'\"'\"'#g;s#.*#par_no_PG='&'#" ; else echo "# par_no_PG="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_INPUT_FMT_OPTION}" | sed "s#'#'\"'\"'#g;s#.*#par_input_fmt_option='&'#" ; else echo "# par_input_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT}" | sed "s#'#'\"'\"'#g;s#.*#par_output_fmt='&'#" ; else echo "# par_output_fmt="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT_OPTION}" | sed "s#'#'\"'\"'#g;s#.*#par_output_fmt_option='&'#" ; else echo "# par_output_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_INDEX+x} ]; then echo "${VIASH_PAR_WRITE_INDEX}" | sed "s#'#'\"'\"'#g;s#.*#par_write_index='&'#" ; else echo "# par_write_index="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\$par_bam" == "false" ]] && unset par_bam +[[ "\$par_cram" == "false" ]] && unset par_cram +[[ "\$par_fast" == "false" ]] && unset par_fast +[[ "\$par_uncompressed" == "false" ]] && unset par_uncompressed +[[ "\$par_with_header" == "false" ]] && unset par_with_header +[[ "\$par_header_only" == "false" ]] && unset par_header_only +[[ "\$par_no_header" == "false" ]] && unset par_no_header +[[ "\$par_count" == "false" ]] && unset par_count +[[ "\$par_unmap" == "false" ]] && unset par_unmap +[[ "\$par_use_index" == "false" ]] && unset par_use_index +[[ "\$par_fetch_pairs" == "false" ]] && unset par_fetch_pairs +[[ "\$par_customized_index" == "false" ]] && unset par_customized_index +[[ "\$par_no_PG" == "false" ]] && unset par_no_PG +[[ "\$par_write_index" == "false" ]] && unset par_write_index +[[ "\$par_remove_B" == "false" ]] && unset par_remove_B + +samtools view \\ + \${par_bam:+-b} \\ + \${par_cram:+-C} \\ + \${par_fast:+--fast} \\ + \${par_uncompressed:+-u} \\ + \${par_with_header:+--with-header} \\ + \${par_header_only:+-H} \\ + \${par_no_header:+--no-header} \\ + \${par_count:+-c} \\ + \${par_output:+-o "\$par_output"} \\ + \${par_output_unselected:+-U "\$par_output_unselected"} \\ + \${par_unmap:+-p "\$par_unmap"} \\ + \${par_fetch_pairs:+-P "\$par_fetch_pairs"} \\ + \${par_fai_reference:+-t "\$par_fai_reference"} \\ + \${par_use_index:+-M "\$par_use_index"} \\ + \${par_region_file:+--region-file "\$par_region_file"} \\ + \${par_customized_index:+-X} \\ + \${par_target_file:+-L "\$par_target_file"} \\ + \${par_qname_file:+-N "\$par_qname_file"} \\ + \${par_read_group:+-r "\$par_read_group"} \\ + \${par_read_group_file:+-R "\$par_read_group_file"} \\ + \${par_tag:+-d "\$par_tag"} \\ + \${par_tag_file:+-D "\$par_tag_file"} \\ + \${par_min_MQ:+-q "\$par_min_MQ"} \\ + \${par_library:+-l "\$par_library"} \\ + \${par_min_qlen:+-m "\$par_min_qlen"} \\ + \${par_expr:+-e "\$par_expr"} \\ + \${par_require_flags:+-f "\$par_require_flags"} \\ + \${par_excl_flags:+-F "\$par_excl_flags"} \\ + \${par_incl_flags:+--rf "\$par_incl_flags"} \\ + \${par_excl_all_flags:+-G "\$par_excl_all_flags"} \\ + \${par_subsample:+--subsample "\$par_subsample"} \\ + \${par_subsample_seed:+--subsample-seed "\$par_subsample_seed"} \\ + \${par_add_flags:+--add-flags "\$par_add_flags"} \\ + \${par_remove_flags:+--remove-flags "\$par_remove_flags"} \\ + \${par_remove_tag:+-x "\$par_remove_tag"} \\ + \${par_keep_tag:+--keep-tag "\$par_keep_tag"} \\ + \${par_remove_B:+-B} \\ + \${par_sanitize:+-z "\$par_sanitize"} \\ + \${par_input_fmt_option:+--input-fmt-option "\$par_input_fmt_option"} \\ + \${par_output_fmt:+-O "\$par_output_fmt"} \\ + \${par_output_fmt_option:+--output-fmt-option "\$par_output_fmt_option"} \\ + \${par_reference:+-T "\$par_reference"} \\ + \${par_write_index:+--write-index} \\ + \${par_no_PG:+--no-PG} \\ + "\$par_input" + +exit 0 +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashDockerStripAutomount "$VIASH_PAR_INPUT") + fi + if [ ! -z "$VIASH_PAR_FAI_REFERENCE" ]; then + VIASH_PAR_FAI_REFERENCE=$(ViashDockerStripAutomount "$VIASH_PAR_FAI_REFERENCE") + fi + if [ ! -z "$VIASH_PAR_REFERENCE" ]; then + VIASH_PAR_REFERENCE=$(ViashDockerStripAutomount "$VIASH_PAR_REFERENCE") + fi + if [ ! -z "$VIASH_PAR_TARGET_FILE" ]; then + VIASH_PAR_TARGET_FILE=$(ViashDockerStripAutomount "$VIASH_PAR_TARGET_FILE") + fi + if [ ! -z "$VIASH_PAR_REGION_FILE" ]; then + VIASH_PAR_REGION_FILE=$(ViashDockerStripAutomount "$VIASH_PAR_REGION_FILE") + fi + if [ ! -z "$VIASH_PAR_QNAME_FILE" ]; then + VIASH_PAR_QNAME_FILE=$(ViashDockerStripAutomount "$VIASH_PAR_QNAME_FILE") + fi + if [ ! -z "$VIASH_PAR_READ_GROUP_FILE" ]; then + VIASH_PAR_READ_GROUP_FILE=$(ViashDockerStripAutomount "$VIASH_PAR_READ_GROUP_FILE") + fi + if [ ! -z "$VIASH_PAR_OUTPUT" ]; then + VIASH_PAR_OUTPUT=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT") + fi + if [ ! -z "$VIASH_PAR_OUTPUT_UNSELECTED" ]; then + VIASH_PAR_OUTPUT_UNSELECTED=$(ViashDockerStripAutomount "$VIASH_PAR_OUTPUT_UNSELECTED") + fi + if [ ! -z "$VIASH_PAR_TAG_FILE" ]; then + VIASH_PAR_TAG_FILE=$(ViashDockerStripAutomount "$VIASH_PAR_TAG_FILE") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_OUTPUT" ] && [ ! -e "$VIASH_PAR_OUTPUT" ]; then + ViashError "Output file '$VIASH_PAR_OUTPUT' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/star/star_align_reads/.config.vsh.yaml b/target/executable/star/star_align_reads/.config.vsh.yaml new file mode 100644 index 00000000..4fa8fad7 --- /dev/null +++ b/target/executable/star/star_align_reads/.config.vsh.yaml @@ -0,0 +1,2121 @@ +name: "star_align_reads" +namespace: "star" +version: "main" +argument_groups: +- name: "Run Parameters" + arguments: + - type: "integer" + name: "--runRNGseed" + description: "random number generator seed." + info: null + example: + - 777 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Genome Parameters" + arguments: + - type: "file" + name: "--genomeDir" + description: "path to the directory where genome files are stored (for --runMode\ + \ alignReads) or will be generated (for --runMode generateGenome)" + info: null + example: + - "./GenomeDir" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--genomeLoad" + description: "mode of shared memory usage for the genome files. Only used with\ + \ --runMode alignReads.\n\n- LoadAndKeep ... load genome into shared and\ + \ keep it in memory after run\n- LoadAndRemove ... load genome into shared\ + \ but remove it after run\n- LoadAndExit ... load genome into shared memory\ + \ and exit, keeping the genome in memory for future runs\n- Remove \ + \ ... do not map anything, just remove loaded genome from memory\n- NoSharedMemory\ + \ ... do not use shared memory, each job will have its own private copy of\ + \ the genome" + info: null + example: + - "NoSharedMemory" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--genomeFastaFiles" + description: "path(s) to the fasta files with the genome sequences, separated\ + \ by spaces. These files should be plain text FASTA files, they *cannot* be\ + \ zipped.\n\nRequired for the genome generation (--runMode genomeGenerate).\ + \ Can also be used in the mapping (--runMode alignReads) to add extra (new)\ + \ sequences to the genome (e.g. spike-ins)." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--genomeFileSizes" + description: "genome files exact sizes in bytes. Typically, this should not be\ + \ defined by the user." + info: null + example: + - 0 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--genomeTransformOutput" + description: "which output to transform back to original genome\n\n- SAM ...\ + \ SAM/BAM alignments\n- SJ ... splice junctions (SJ.out.tab)\n- Quant \ + \ ... quantifications (from --quantMode option)\n- None ... no transformation\ + \ of the output" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--genomeChrSetMitochondrial" + description: "names of the mitochondrial chromosomes. Presently only used for\ + \ STARsolo statistics output/" + info: null + example: + - "chrM" + - "M" + - "MT" + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Splice Junctions Database" + arguments: + - type: "string" + name: "--sjdbFileChrStartEnd" + description: "path to the files with genomic coordinates (chr start \ + \ end strand) for the splice junction introns. Multiple files can be supplied\ + \ and will be concatenated." + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--sjdbGTFfile" + description: "path to the GTF file with annotations" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFchrPrefix" + description: "prefix for chromosome names in a GTF file (e.g. 'chr' for using\ + \ ENSMEBL annotations with UCSC genomes)" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFfeatureExon" + description: "feature type in GTF file to be used as exons for building transcripts" + info: null + example: + - "exon" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentTranscript" + description: "GTF attribute name for parent transcript ID (default \"transcript_id\"\ + \ works for GTF files)" + info: null + example: + - "transcript_id" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGene" + description: "GTF attribute name for parent gene ID (default \"gene_id\" works\ + \ for GTF files)" + info: null + example: + - "gene_id" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGeneName" + description: "GTF attribute name for parent gene name" + info: null + example: + - "gene_name" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGeneType" + description: "GTF attribute name for parent gene type" + info: null + example: + - "gene_type" + - "gene_biotype" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--sjdbOverhang" + description: "length of the donor/acceptor sequence on each side of the junctions,\ + \ ideally = (mate_length - 1)" + info: null + example: + - 100 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--sjdbScore" + description: "extra alignment score for alignments that cross database junctions" + info: null + example: + - 2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbInsertSave" + description: "which files to save when sjdb junctions are inserted on the fly\ + \ at the mapping step\n\n- Basic ... only small junction / transcript files\n\ + - All ... all files including big Genome, SA and SAindex - this will create\ + \ a complete genome directory" + info: null + example: + - "Basic" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Variation parameters" + arguments: + - type: "string" + name: "--varVCFfile" + description: "path to the VCF file that contains variation data. The 10th column\ + \ should contain the genotype information, e.g. 0/1" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Read Parameters" + arguments: + - type: "string" + name: "--readFilesType" + description: "format of input read files\n\n- Fastx ... FASTA or FASTQ\n\ + - SAM SE ... SAM or BAM single-end reads; for BAM use --readFilesCommand\ + \ samtools view\n- SAM PE ... SAM or BAM paired-end reads; for BAM use\ + \ --readFilesCommand samtools view" + info: null + example: + - "Fastx" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--readFilesSAMattrKeep" + description: "for --readFilesType SAM SE/PE, which SAM tags to keep in the output\ + \ BAM, e.g.: --readFilesSAMtagsKeep RG PL\n\n- All ... keep all tags\n-\ + \ None ... do not keep any tags" + info: null + example: + - "All" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--readFilesManifest" + description: "path to the \"manifest\" file with the names of read files. The\ + \ manifest file should contain 3 tab-separated columns:\n\npaired-end reads:\ + \ read1_file_name $tab$ read2_file_name $tab$ read_group_line.\nsingle-end reads:\ + \ read1_file_name $tab$ - $tab$ read_group_line.\nSpaces, but\ + \ not tabs are allowed in file names.\nIf read_group_line does not start with\ + \ ID:, it can only contain one ID field, and ID: will be added to it.\nIf read_group_line\ + \ starts with ID:, it can contain several fields separated by $tab$, and all\ + \ fields will be be copied verbatim into SAM @RG header line." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--readFilesPrefix" + description: "prefix for the read files names, i.e. it will be added in front\ + \ of the strings in --readFilesIn" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--readFilesCommand" + description: "command line to execute for each of the input file. This command\ + \ should generate FASTA or FASTQ text and send it to stdout\n\nFor example:\ + \ zcat - to uncompress .gz files, bzcat - to uncompress .bz2 files, etc." + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--readMapNumber" + description: "number of reads to map from the beginning of the file\n\n-1: map\ + \ all reads" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--readMatesLengthsIn" + description: "Equal/NotEqual - lengths of names,sequences,qualities for both mates\ + \ are the same / not the same. NotEqual is safe in all situations." + info: null + example: + - "NotEqual" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--readNameSeparator" + description: "character(s) separating the part of the read names that will be\ + \ trimmed in output (read name after space is always trimmed)" + info: null + example: + - "/" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--readQualityScoreBase" + description: "number to be subtracted from the ASCII code to get Phred quality\ + \ score" + info: null + example: + - 33 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Read Clipping" + arguments: + - type: "string" + name: "--clipAdapterType" + description: "adapter clipping type\n\n- Hamming ... adapter clipping based on\ + \ Hamming distance, with the number of mismatches controlled by --clip5pAdapterMMp\n\ + - CellRanger4 ... 5p and 3p adapter clipping similar to CellRanger4. Utilizes\ + \ Opal package by Martin Sosic: https://github.com/Martinsos/opal\n- None ...\ + \ no adapter clipping, all other clip* parameters are disregarded" + info: null + example: + - "Hamming" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--clip3pNbases" + description: "number(s) of bases to clip from 3p of each mate. If one value is\ + \ given, it will be assumed the same for both mates." + info: null + example: + - 0 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--clip3pAdapterSeq" + description: "adapter sequences to clip from 3p of each mate. If one value is\ + \ given, it will be assumed the same for both mates.\n\n- polyA ... polyA sequence\ + \ with the length equal to read length" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "double" + name: "--clip3pAdapterMMp" + description: "max proportion of mismatches for 3p adapter clipping for each mate.\ + \ If one value is given, it will be assumed the same for both mates." + info: null + example: + - 0.1 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--clip3pAfterAdapterNbases" + description: "number of bases to clip from 3p of each mate after the adapter clipping.\ + \ If one value is given, it will be assumed the same for both mates." + info: null + example: + - 0 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--clip5pNbases" + description: "number(s) of bases to clip from 5p of each mate. If one value is\ + \ given, it will be assumed the same for both mates." + info: null + example: + - 0 + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Limits" + arguments: + - type: "long" + name: "--limitGenomeGenerateRAM" + description: "maximum available RAM (bytes) for genome generation" + info: null + example: + - 31000000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "long" + name: "--limitIObufferSize" + description: "max available buffers size (bytes) for input/output, per thread" + info: null + example: + - 30000000 + - 50000000 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "long" + name: "--limitOutSAMoneReadBytes" + description: "max size of the SAM record (bytes) for one read. Recommended value:\ + \ >(2*(LengthMate1+LengthMate2+100)*outFilterMultimapNmax" + info: null + example: + - 100000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--limitOutSJoneRead" + description: "max number of junctions for one read (including all multi-mappers)" + info: null + example: + - 1000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--limitOutSJcollapsed" + description: "max number of collapsed junctions" + info: null + example: + - 1000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "long" + name: "--limitBAMsortRAM" + description: "maximum available RAM (bytes) for sorting BAM. If =0, it will be\ + \ set to the genome index size. 0 value can only be used with --genomeLoad NoSharedMemory\ + \ option." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--limitSjdbInsertNsj" + description: "maximum number of junctions to be inserted to the genome on the\ + \ fly at the mapping stage, including those from annotations and those detected\ + \ in the 1st step of the 2-pass run" + info: null + example: + - 1000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--limitNreadsSoft" + description: "soft limit on the number of reads" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output: general" + arguments: + - type: "string" + name: "--outTmpKeep" + description: "whether to keep the temporary files after STAR runs is finished\n\ + \n- None ... remove all temporary files\n- All ... keep all files" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outStd" + description: "which output will be directed to stdout (standard out)\n\n- Log\ + \ ... log messages\n- SAM ... alignments\ + \ in SAM format (which normally are output to Aligned.out.sam file), normal\ + \ standard output will go into Log.std.out\n- BAM_Unsorted ... alignments\ + \ in BAM format, unsorted. Requires --outSAMtype BAM Unsorted\n- BAM_SortedByCoordinate\ + \ ... alignments in BAM format, sorted by coordinate. Requires --outSAMtype\ + \ BAM SortedByCoordinate\n- BAM_Quant ... alignments to transcriptome\ + \ in BAM format, unsorted. Requires --quantMode TranscriptomeSAM" + info: null + example: + - "Log" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outReadsUnmapped" + description: "output of unmapped and partially mapped (i.e. mapped only one mate\ + \ of a paired end read) reads in separate file(s).\n\n- None ... no output\n\ + - Fastx ... output in separate fasta/fastq files, Unmapped.out.mate1/2" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outQSconversionAdd" + description: "add this number to the quality score (e.g. to convert from Illumina\ + \ to Sanger, use -31)" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outMultimapperOrder" + description: "order of multimapping alignments in the output files\n\n- Old_2.4\ + \ ... quasi-random order used before 2.5.0\n- Random \ + \ ... random order of alignments for each multi-mapper. Read mates (pairs)\ + \ are always adjacent, all alignment for each read stay together. This option\ + \ will become default in the future releases." + info: null + example: + - "Old_2.4" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output: SAM and BAM" + arguments: + - type: "string" + name: "--outSAMtype" + description: "type of SAM/BAM output\n\n1st word:\n- BAM ... output BAM without\ + \ sorting\n- SAM ... output SAM without sorting\n- None ... no SAM/BAM output\n\ + 2nd, 3rd:\n- Unsorted ... standard unsorted\n- SortedByCoordinate\ + \ ... sorted by coordinate. This option will allocate extra memory for sorting\ + \ which can be specified by --limitBAMsortRAM." + info: null + example: + - "SAM" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outSAMmode" + description: "mode of SAM output\n\n- None ... no SAM output\n- Full ... full\ + \ SAM output\n- NoQS ... full SAM but without quality scores" + info: null + example: + - "Full" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMstrandField" + description: "Cufflinks-like strand field flag\n\n- None ... not used\n\ + - intronMotif ... strand derived from the intron motif. This option changes\ + \ the output alignments: reads with inconsistent and/or non-canonical introns\ + \ are filtered out." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMattributes" + description: "a string of desired SAM attributes, in the order desired for the\ + \ output SAM. Tags can be listed in any combination/order.\n\n***Presets:\n\ + - None ... no attributes\n- Standard ... NH HI AS nM\n- All \ + \ ... NH HI AS nM NM MD jM jI MC ch\n***Alignment:\n- NH ... number\ + \ of loci the reads maps to: =1 for unique mappers, >1 for multimappers. Standard\ + \ SAM tag.\n- HI ... multiple alignment index, starts with --outSAMattrIHstart\ + \ (=1 by default). Standard SAM tag.\n- AS ... local alignment score,\ + \ +1/-1 for matches/mismateches, score* penalties for indels and gaps. For PE\ + \ reads, total score for two mates. Stadnard SAM tag.\n- nM ... number\ + \ of mismatches. For PE reads, sum over two mates.\n- NM ... edit distance\ + \ to the reference (number of mismatched + inserted + deleted bases) for each\ + \ mate. Standard SAM tag.\n- MD ... string encoding mismatched and\ + \ deleted reference bases (see standard SAM specifications). Standard SAM tag.\n\ + - jM ... intron motifs for all junctions (i.e. N in CIGAR): 0: non-canonical;\ + \ 1: GT/AG, 2: CT/AC, 3: GC/AG, 4: CT/GC, 5: AT/AC, 6: GT/AT. If splice junctions\ + \ database is used, and a junction is annotated, 20 is added to its motif value.\n\ + - jI ... start and end of introns for all junctions (1-based).\n- XS\ + \ ... alignment strand according to --outSAMstrandField.\n- MC \ + \ ... mate's CIGAR string. Standard SAM tag.\n- ch ... marks all\ + \ segment of all chimeric alingments for --chimOutType WithinBAM output.\n-\ + \ cN ... number of bases clipped from the read ends: 5' and 3'\n***Variation:\n\ + - vA ... variant allele\n- vG ... genomic coordinate of the\ + \ variant overlapped by the read.\n- vW ... 1 - alignment passes WASP\ + \ filtering; 2,3,4,5,6,7 - alignment does not pass WASP filtering. Requires\ + \ --waspOutputMode SAMtag.\n- ha ... haplotype (1/2) when mapping to\ + \ the diploid genome. Requires genome generated with --genomeTransformType Diploid\ + \ .\n***STARsolo:\n- CR CY UR UY ... sequences and quality scores of cell barcodes\ + \ and UMIs for the solo* demultiplexing.\n- GX GN ... gene ID and gene\ + \ name for unique-gene reads.\n- gx gn ... gene IDs and gene names for\ + \ unique- and multi-gene reads.\n- CB UB ... error-corrected cell barcodes\ + \ and UMIs for solo* demultiplexing. Requires --outSAMtype BAM SortedByCoordinate.\n\ + - sM ... assessment of CB and UMI.\n- sS ... sequence of the\ + \ entire barcode (CB,UMI,adapter).\n- sQ ... quality of the entire\ + \ barcode.\n- sF ... type of feature overlap and number of features\ + \ for each alignment\n***Unsupported/undocumented:\n- rB ... alignment\ + \ block read/genomic coordinates.\n- vR ... read coordinate of the\ + \ variant." + info: null + example: + - "Standard" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSAMattrIHstart" + description: "start value for the IH attribute. 0 may be required by some downstream\ + \ software, such as Cufflinks or StringTie." + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMunmapped" + description: "output of unmapped reads in the SAM format\n\n1st word:\n- None\ + \ ... no output\n- Within ... output unmapped reads within the main SAM file\ + \ (i.e. Aligned.out.sam)\n2nd word:\n- KeepPairs ... record unmapped mate for\ + \ each alignment, and, in case of unsorted output, keep it adjacent to its mapped\ + \ mate. Only affects multi-mapping reads." + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outSAMorder" + description: "type of sorting for the SAM output\n\nPaired: one mate after the\ + \ other for all paired alignments\nPairedKeepInputOrder: one mate after the\ + \ other for all paired alignments, the order is kept the same as in the input\ + \ FASTQ files" + info: null + example: + - "Paired" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMprimaryFlag" + description: "which alignments are considered primary - all others will be marked\ + \ with 0x100 bit in the FLAG\n\n- OneBestScore ... only one alignment with the\ + \ best score is primary\n- AllBestScore ... all alignments with the best score\ + \ are primary" + info: null + example: + - "OneBestScore" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMreadID" + description: "read ID record type\n\n- Standard ... first word (until space) from\ + \ the FASTx read ID line, removing /1,/2 from the end\n- Number ... read number\ + \ (index) in the FASTx file" + info: null + example: + - "Standard" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outSAMmapqUnique" + description: "0 to 255: the MAPQ value for unique mappers" + info: null + example: + - 255 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outSAMflagOR" + description: "0 to 65535: sam FLAG will be bitwise OR'd with this value, i.e.\ + \ FLAG=FLAG | outSAMflagOR. This is applied after all flags have been set by\ + \ STAR, and after outSAMflagAND. Can be used to set specific bits that are not\ + \ set otherwise." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outSAMflagAND" + description: "0 to 65535: sam FLAG will be bitwise AND'd with this value, i.e.\ + \ FLAG=FLAG & outSAMflagOR. This is applied after all flags have been set by\ + \ STAR, but before outSAMflagOR. Can be used to unset specific bits that are\ + \ not set otherwise." + info: null + example: + - 65535 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMattrRGline" + description: "SAM/BAM read group line. The first word contains the read group\ + \ identifier and must start with \"ID:\", e.g. --outSAMattrRGline ID:xxx CN:yy\ + \ \"DS:z z z\".\n\nxxx will be added as RG tag to each output alignment. Any\ + \ spaces in the tag values have to be double quoted.\nComma separated RG lines\ + \ correspons to different (comma separated) input files in --readFilesIn. Commas\ + \ have to be surrounded by spaces, e.g.\n--outSAMattrRGline ID:xxx , ID:zzz\ + \ \"DS:z z\" , ID:yyy DS:yyyy" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outSAMheaderHD" + description: "@HD (header) line of the SAM header" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outSAMheaderPG" + description: "extra @PG (software) line of the SAM header (in addition to STAR)" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outSAMheaderCommentFile" + description: "path to the file with @CO (comment) lines of the SAM header" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMfilter" + description: "filter the output into main SAM/BAM files\n\n- KeepOnlyAddedReferences\ + \ ... only keep the reads for which all alignments are to the extra reference\ + \ sequences added with --genomeFastaFiles at the mapping stage.\n- KeepAllAddedReferences\ + \ ... keep all alignments to the extra reference sequences added with --genomeFastaFiles\ + \ at the mapping stage." + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSAMmultNmax" + description: "max number of multiple alignments for a read that will be output\ + \ to the SAM/BAM files. Note that if this value is not equal to -1, the top\ + \ scoring alignment will be output first\n\n- -1 ... all alignments (up to --outFilterMultimapNmax)\ + \ will be output" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outSAMtlen" + description: "calculation method for the TLEN field in the SAM/BAM files\n\n-\ + \ 1 ... leftmost base of the (+)strand mate to rightmost base of the (-)mate.\ + \ (+)sign for the (+)strand mate\n- 2 ... leftmost base of any mate to rightmost\ + \ base of any mate. (+)sign for the mate with the leftmost base. This is different\ + \ from 1 for overlapping mates with protruding ends" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outBAMcompression" + description: "-1 to 10 BAM compression level, -1=default compression (6?), 0=no\ + \ compression, 10=maximum compression" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outBAMsortingThreadN" + description: ">=0: number of threads for BAM sorting. 0 will default to min(6,--runThreadN)." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outBAMsortingBinsN" + description: ">0: number of genome bins for coordinate-sorting" + info: null + example: + - 50 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "BAM processing" + arguments: + - type: "string" + name: "--bamRemoveDuplicatesType" + description: "mark duplicates in the BAM file, for now only works with (i) sorted\ + \ BAM fed with inputBAMfile, and (ii) for paired-end alignments only\n\n- -\ + \ ... no duplicate removal/marking\n- UniqueIdentical\ + \ ... mark all multimappers, and duplicate unique mappers. The coordinates,\ + \ FLAG, CIGAR must be identical\n- UniqueIdenticalNotMulti ... mark duplicate\ + \ unique mappers but not multimappers." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bamRemoveDuplicatesMate2basesN" + description: "number of bases from the 5' of mate 2 to use in collapsing (e.g.\ + \ for RAMPAGE)" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output Wiggle" + arguments: + - type: "string" + name: "--outWigType" + description: "type of signal output, e.g. \"bedGraph\" OR \"bedGraph read1_5p\"\ + . Requires sorted BAM: --outSAMtype BAM SortedByCoordinate .\n\n1st word:\n\ + - None ... no signal output\n- bedGraph ... bedGraph format\n- wiggle\ + \ ... wiggle format\n2nd word:\n- read1_5p ... signal from only 5' of\ + \ the 1st read, useful for CAGE/RAMPAGE etc\n- read2 ... signal from only\ + \ 2nd read" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outWigStrand" + description: "strandedness of wiggle/bedGraph output\n\n- Stranded ... separate\ + \ strands, str1 and str2\n- Unstranded ... collapsed strands" + info: null + example: + - "Stranded" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outWigReferencesPrefix" + description: "prefix matching reference names to include in the output wiggle\ + \ file, e.g. \"chr\", default \"-\" - include all references" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outWigNorm" + description: "type of normalization for the signal\n\n- RPM ... reads per million\ + \ of mapped reads\n- None ... no normalization, \"raw\" counts" + info: null + example: + - "RPM" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output Filtering" + arguments: + - type: "string" + name: "--outFilterType" + description: "type of filtering\n\n- Normal ... standard filtering using only\ + \ current alignment\n- BySJout ... keep only those reads that contain junctions\ + \ that passed filtering into SJ.out.tab" + info: null + example: + - "Normal" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outFilterMultimapScoreRange" + description: "the score range below the maximum score for multimapping alignments" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outFilterMultimapNmax" + description: "maximum number of loci the read is allowed to map to. Alignments\ + \ (all of them) will be output only if the read maps to no more loci than this\ + \ value.\n\nOtherwise no alignments will be output, and the read will be counted\ + \ as \"mapped to too many loci\" in the Log.final.out ." + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outFilterMismatchNmax" + description: "alignment will be output only if it has no more mismatches than\ + \ this value." + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--outFilterMismatchNoverLmax" + description: "alignment will be output only if its ratio of mismatches to *mapped*\ + \ length is less than or equal to this value." + info: null + example: + - 0.3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--outFilterMismatchNoverReadLmax" + description: "alignment will be output only if its ratio of mismatches to *read*\ + \ length is less than or equal to this value." + info: null + example: + - 1.0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outFilterScoreMin" + description: "alignment will be output only if its score is higher than or equal\ + \ to this value." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--outFilterScoreMinOverLread" + description: "same as outFilterScoreMin, but normalized to read length (sum of\ + \ mates' lengths for paired-end reads)" + info: null + example: + - 0.66 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outFilterMatchNmin" + description: "alignment will be output only if the number of matched bases is\ + \ higher than or equal to this value." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--outFilterMatchNminOverLread" + description: "sam as outFilterMatchNmin, but normalized to the read length (sum\ + \ of mates' lengths for paired-end reads)." + info: null + example: + - 0.66 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outFilterIntronMotifs" + description: "filter alignment using their motifs\n\n- None \ + \ ... no filtering\n- RemoveNoncanonical ... filter out\ + \ alignments that contain non-canonical junctions\n- RemoveNoncanonicalUnannotated\ + \ ... filter out alignments that contain non-canonical unannotated junctions\ + \ when using annotated splice junctions database. The annotated non-canonical\ + \ junctions will be kept." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outFilterIntronStrands" + description: "filter alignments\n\n- RemoveInconsistentStrands ... remove\ + \ alignments that have junctions with inconsistent strands\n- None \ + \ ... no filtering" + info: null + example: + - "RemoveInconsistentStrands" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output splice junctions (SJ.out.tab)" + arguments: + - type: "string" + name: "--outSJtype" + description: "type of splice junction output\n\n- Standard ... standard SJ.out.tab\ + \ output\n- None ... no splice junction output" + info: null + example: + - "Standard" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output Filtering: Splice Junctions" + arguments: + - type: "string" + name: "--outSJfilterReads" + description: "which reads to consider for collapsed splice junctions output\n\n\ + - All ... all reads, unique- and multi-mappers\n- Unique ... uniquely mapping\ + \ reads only" + info: null + example: + - "All" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outSJfilterOverhangMin" + description: "minimum overhang length for splice junctions on both sides for:\ + \ (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif,\ + \ (4) AT/AC and GT/AT motif. -1 means no output for that motif\n\ndoes not apply\ + \ to annotated junctions" + info: null + example: + - 30 + - 12 + - 12 + - 12 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSJfilterCountUniqueMin" + description: "minimum uniquely mapping read count per junction for: (1) non-canonical\ + \ motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and\ + \ GT/AT motif. -1 means no output for that motif\n\nJunctions are output if\ + \ one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin conditions are\ + \ satisfied\ndoes not apply to annotated junctions" + info: null + example: + - 3 + - 1 + - 1 + - 1 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSJfilterCountTotalMin" + description: "minimum total (multi-mapping+unique) read count per junction for:\ + \ (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif,\ + \ (4) AT/AC and GT/AT motif. -1 means no output for that motif\n\nJunctions\ + \ are output if one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin\ + \ conditions are satisfied\ndoes not apply to annotated junctions" + info: null + example: + - 3 + - 1 + - 1 + - 1 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSJfilterDistToOtherSJmin" + description: "minimum allowed distance to other junctions' donor/acceptor\n\n\ + does not apply to annotated junctions" + info: null + example: + - 10 + - 0 + - 5 + - 10 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSJfilterIntronMaxVsReadN" + description: "maximum gap allowed for junctions supported by 1,2,3,,,N reads\n\ + \ni.e. by default junctions supported by 1 read can have gaps <=50000b, by 2\ + \ reads: <=100000b, by 3 reads: <=200000. by >=4 reads any gap <=alignIntronMax\n\ + does not apply to annotated junctions" + info: null + example: + - 50000 + - 100000 + - 200000 + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Scoring" + arguments: + - type: "integer" + name: "--scoreGap" + description: "splice junction penalty (independent on intron motif)" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreGapNoncan" + description: "non-canonical junction penalty (in addition to scoreGap)" + info: null + example: + - -8 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreGapGCAG" + description: "GC/AG and CT/GC junction penalty (in addition to scoreGap)" + info: null + example: + - -4 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreGapATAC" + description: "AT/AC and GT/AT junction penalty (in addition to scoreGap)" + info: null + example: + - -8 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreGenomicLengthLog2scale" + description: "extra score logarithmically scaled with genomic length of the alignment:\ + \ scoreGenomicLengthLog2scale*log2(genomicLength)" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreDelOpen" + description: "deletion open penalty" + info: null + example: + - -2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreDelBase" + description: "deletion extension penalty per base (in addition to scoreDelOpen)" + info: null + example: + - -2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreInsOpen" + description: "insertion open penalty" + info: null + example: + - -2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreInsBase" + description: "insertion extension penalty per base (in addition to scoreInsOpen)" + info: null + example: + - -2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreStitchSJshift" + description: "maximum score reduction while searching for SJ boundaries in the\ + \ stitching step" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Alignments and Seeding" + arguments: + - type: "integer" + name: "--seedSearchStartLmax" + description: "defines the search start point through the read - the read is split\ + \ into pieces no longer than this value" + info: null + example: + - 50 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--seedSearchStartLmaxOverLread" + description: "seedSearchStartLmax normalized to read length (sum of mates' lengths\ + \ for paired-end reads)" + info: null + example: + - 1.0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedSearchLmax" + description: "defines the maximum length of the seeds, if =0 seed length is not\ + \ limited" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedMultimapNmax" + description: "only pieces that map fewer than this value are utilized in the stitching\ + \ procedure" + info: null + example: + - 10000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedPerReadNmax" + description: "max number of seeds per read" + info: null + example: + - 1000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedPerWindowNmax" + description: "max number of seeds per window" + info: null + example: + - 50 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedNoneLociPerWindow" + description: "max number of one seed loci per window" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedSplitMin" + description: "min length of the seed sequences split by Ns or mate gap" + info: null + example: + - 12 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedMapMin" + description: "min length of seeds to be mapped" + info: null + example: + - 5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignIntronMin" + description: "minimum intron size, genomic gap is considered intron if its length>=alignIntronMin,\ + \ otherwise it is considered Deletion" + info: null + example: + - 21 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignIntronMax" + description: "maximum intron size, if 0, max intron size will be determined by\ + \ (2^winBinNbits)*winAnchorDistNbins" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignMatesGapMax" + description: "maximum gap between two mates, if 0, max intron gap will be determined\ + \ by (2^winBinNbits)*winAnchorDistNbins" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignSJoverhangMin" + description: "minimum overhang (i.e. block size) for spliced alignments" + info: null + example: + - 5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignSJstitchMismatchNmax" + description: "maximum number of mismatches for stitching of the splice junctions\ + \ (-1: no limit).\n\n(1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3)\ + \ GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif." + info: null + example: + - 0 + - -1 + - 0 + - 0 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--alignSJDBoverhangMin" + description: "minimum overhang (i.e. block size) for annotated (sjdb) spliced\ + \ alignments" + info: null + example: + - 3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignSplicedMateMapLmin" + description: "minimum mapped length for a read mate that is spliced" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--alignSplicedMateMapLminOverLmate" + description: "alignSplicedMateMapLmin normalized to mate length" + info: null + example: + - 0.66 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignWindowsPerReadNmax" + description: "max number of windows per read" + info: null + example: + - 10000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignTranscriptsPerWindowNmax" + description: "max number of transcripts per window" + info: null + example: + - 100 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignTranscriptsPerReadNmax" + description: "max number of different alignments per read to consider" + info: null + example: + - 10000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--alignEndsType" + description: "type of read ends alignment\n\n- Local ... standard\ + \ local alignment with soft-clipping allowed\n- EndToEnd ... force\ + \ end-to-end read alignment, do not soft-clip\n- Extend5pOfRead1 ... fully\ + \ extend only the 5p of the read1, all other ends: local alignment\n- Extend5pOfReads12\ + \ ... fully extend only the 5p of the both read1 and read2, all other ends:\ + \ local alignment" + info: null + example: + - "Local" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--alignEndsProtrude" + description: "allow protrusion of alignment ends, i.e. start (end) of the +strand\ + \ mate downstream of the start (end) of the -strand mate\n\n1st word: int: maximum\ + \ number of protrusion bases allowed\n2nd word: string:\n- \ + \ ConcordantPair ... report alignments with non-zero protrusion as concordant\ + \ pairs\n- DiscordantPair ... report alignments with non-zero\ + \ protrusion as discordant pairs" + info: null + example: + - "0 ConcordantPair" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--alignSoftClipAtReferenceEnds" + description: "allow the soft-clipping of the alignments past the end of the chromosomes\n\ + \n- Yes ... allow\n- No ... prohibit, useful for compatibility with Cufflinks" + info: null + example: + - "Yes" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--alignInsertionFlush" + description: "how to flush ambiguous insertion positions\n\n- None ... insertions\ + \ are not flushed\n- Right ... insertions are flushed to the right" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Paired-End reads" + arguments: + - type: "integer" + name: "--peOverlapNbasesMin" + description: "minimum number of overlapping bases to trigger mates merging and\ + \ realignment. Specify >0 value to switch on the \"merginf of overlapping mates\"\ + \ algorithm." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--peOverlapMMp" + description: "maximum proportion of mismatched bases in the overlap area" + info: null + example: + - 0.01 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Windows, Anchors, Binning" + arguments: + - type: "integer" + name: "--winAnchorMultimapNmax" + description: "max number of loci anchors are allowed to map to" + info: null + example: + - 50 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--winBinNbits" + description: "=log2(winBin), where winBin is the size of the bin for the windows/clustering,\ + \ each window will occupy an integer number of bins." + info: null + example: + - 16 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--winAnchorDistNbins" + description: "max number of bins between two anchors that allows aggregation of\ + \ anchors into one window" + info: null + example: + - 9 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--winFlankNbins" + description: "log2(winFlank), where win Flank is the size of the left and right\ + \ flanking regions for each window" + info: null + example: + - 4 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--winReadCoverageRelativeMin" + description: "minimum relative coverage of the read sequence by the seeds in a\ + \ window, for STARlong algorithm only." + info: null + example: + - 0.5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--winReadCoverageBasesMin" + description: "minimum number of bases covered by the seeds in a window , for STARlong\ + \ algorithm only." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Chimeric Alignments" + arguments: + - type: "string" + name: "--chimOutType" + description: "type of chimeric output\n\n- Junctions ... Chimeric.out.junction\n\ + - SeparateSAMold ... output old SAM into separate Chimeric.out.sam file\n-\ + \ WithinBAM ... output into main aligned BAM files (Aligned.*.bam)\n-\ + \ WithinBAM HardClip ... (default) hard-clipping in the CIGAR for supplemental\ + \ chimeric alignments (default if no 2nd word is present)\n- WithinBAM SoftClip\ + \ ... soft-clipping in the CIGAR for supplemental chimeric alignments" + info: null + example: + - "Junctions" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--chimSegmentMin" + description: "minimum length of chimeric segment length, if ==0, no chimeric output" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimScoreMin" + description: "minimum total (summed) score of the chimeric segments" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimScoreDropMax" + description: "max drop (difference) of chimeric score (the sum of scores of all\ + \ chimeric segments) from the read length" + info: null + example: + - 20 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimScoreSeparation" + description: "minimum difference (separation) between the best chimeric score\ + \ and the next one" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimScoreJunctionNonGTAG" + description: "penalty for a non-GT/AG chimeric junction" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimJunctionOverhangMin" + description: "minimum overhang for a chimeric junction" + info: null + example: + - 20 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimSegmentReadGapMax" + description: "maximum gap in the read sequence between chimeric segments" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--chimFilter" + description: "different filters for chimeric alignments\n\n- None ... no filtering\n\ + - banGenomicN ... Ns are not allowed in the genome sequence around the chimeric\ + \ junction" + info: null + example: + - "banGenomicN" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--chimMainSegmentMultNmax" + description: "maximum number of multi-alignments for the main chimeric segment.\ + \ =1 will prohibit multimapping main segments." + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimMultimapNmax" + description: "maximum number of chimeric multi-alignments\n\n- 0 ... use the old\ + \ scheme for chimeric detection which only considered unique alignments" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimMultimapScoreRange" + description: "the score range for multi-mapping chimeras below the best chimeric\ + \ score. Only works with --chimMultimapNmax > 1" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimNonchimScoreDropMin" + description: "to trigger chimeric detection, the drop in the best non-chimeric\ + \ alignment score with respect to the read length has to be greater than this\ + \ value" + info: null + example: + - 20 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimOutJunctionFormat" + description: "formatting type for the Chimeric.out.junction file\n\n- 0 ... no\ + \ comment lines/headers\n- 1 ... comment lines at the end of the file: command\ + \ line and Nreads: total, unique/multi-mapping" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Quantification of Annotations" + arguments: + - type: "string" + name: "--quantMode" + description: "types of quantification requested\n\n- - ... none\n\ + - TranscriptomeSAM ... output SAM/BAM alignments to transcriptome into a separate\ + \ file\n- GeneCounts ... count reads per gene" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--quantTranscriptomeBAMcompression" + description: "-2 to 10 transcriptome BAM compression level\n\n- -2 ... no BAM\ + \ output\n- -1 ... default compression (6?)\n- 0 ... no compression\n- 10\ + \ ... maximum compression" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--quantTranscriptomeSAMoutput" + description: "alignment filtering for TranscriptomeSAM output\n\n- BanSingleEnd_BanIndels_ExtendSoftclip\ + \ ... prohibit indels and single-end alignments, extend softclips - compatible\ + \ with RSEM\n- BanSingleEnd ... prohibit single-end alignments,\ + \ allow indels and softclips\n- BanSingleEnd_ExtendSoftclip ... prohibit single-end\ + \ alignments, extend softclips, allow indels" + info: null + example: + - "BanSingleEnd_BanIndels_ExtendSoftclip" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "2-pass Mapping" + arguments: + - type: "string" + name: "--twopassMode" + description: "2-pass mapping mode.\n\n- None ... 1-pass mapping\n- Basic\ + \ ... basic 2-pass mapping, with all 1st pass junctions inserted into\ + \ the genome indices on the fly" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--twopass1readsN" + description: "number of reads to process for the 1st step. Use very large number\ + \ (or default -1) to map all reads in the first step." + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "WASP parameters" + arguments: + - type: "string" + name: "--waspOutputMode" + description: "WASP allele-specific output type. This is re-implementation of the\ + \ original WASP mappability filtering by Bryce van de Geijn, Graham McVicker,\ + \ Yoav Gilad & Jonathan K Pritchard. Please cite the original WASP paper: Nature\ + \ Methods 12, 1061-1063 (2015), https://www.nature.com/articles/nmeth.3582 .\n\ + \n- SAMtag ... add WASP tags to the alignments that pass WASP filtering" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + alternatives: + - "--readFilesIn" + description: "The single-end or paired-end R1 FastQ files to be processed." + info: null + example: + - "mysample_S1_L001_R1_001.fastq.gz" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--input_r2" + description: "The paired-end R2 FastQ files to be processed. Only required if\ + \ --input is a paired-end R1 file." + info: null + example: + - "mysample_S1_L001_R2_001.fastq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--aligned_reads" + description: "The output file containing the aligned reads." + info: null + example: + - "aligned_reads.bam" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reads_per_gene" + description: "The output file containing the number of reads per gene." + info: null + example: + - "reads_per_gene.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unmapped" + description: "The output file containing the unmapped reads." + info: null + example: + - "unmapped.fastq" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unmapped_r2" + description: "The output file containing the unmapped R2 reads." + info: null + example: + - "unmapped_r2.fastq" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--chimeric_junctions" + description: "The output file containing the chimeric junctions." + info: null + example: + - "chimeric_junctions.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--log" + description: "The output file containing the log of the alignment process." + info: null + example: + - "log.txt" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--splice_junctions" + description: "The output file containing the splice junctions." + info: null + example: + - "splice_junctions.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "python_script" + path: "script.py" + is_executable: true +description: "Aligns reads to a reference genome using STAR.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "align" +- "fasta" +- "genome" +license: "MIT" +references: + doi: + - "10.1093/bioinformatics/bts635" +links: + repository: "https://github.com/alexdobin/STAR" + documentation: "https://github.com/alexdobin/STAR/blob/master/doc/STARmanual.pdf" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "python:3.12-slim" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "apt" + packages: + - "procps" + - "gzip" + - "bzip2" + interactive: false + - type: "docker" + run: + - "apt-get update && \\\n apt-get install -y --no-install-recommends ${PACKAGES}\ + \ && \\\n cd /tmp && \\\n wget --no-check-certificate https://github.com/alexdobin/STAR/archive/refs/tags/${STAR_VERSION}.zip\ + \ && \\\n unzip ${STAR_VERSION}.zip && \\\n cd STAR-${STAR_VERSION}/source\ + \ && \\\n make STARstatic CXXFLAGS_SIMD=-std=c++11 && \\\n cp STAR /usr/local/bin\ + \ && \\\n cd / && \\\n rm -rf /tmp/STAR-${STAR_VERSION} /tmp/${STAR_VERSION}.zip\ + \ && \\\n apt-get --purge autoremove -y ${PACKAGES} && \\\n apt-get clean\n" + env: + - "STAR_VERSION 2.7.11b" + - "PACKAGES gcc g++ make wget zlib1g-dev unzip xxd" + - type: "docker" + run: + - "STAR --version | sed 's#\\(.*\\)#star: \"\\1\"#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/star/star_align_reads/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/star/star_align_reads" + executable: "target/executable/star/star_align_reads/star_align_reads" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/star/star_align_reads/star_align_reads b/target/executable/star/star_align_reads/star_align_reads new file mode 100755 index 00000000..c385a9e7 --- /dev/null +++ b/target/executable/star/star_align_reads/star_align_reads @@ -0,0 +1,5387 @@ +#!/usr/bin/env bash + +# star_align_reads main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="star_align_reads" +VIASH_META_FUNCTIONALITY_NAME="star_align_reads" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "star_align_reads main" + echo "" + echo "Aligns reads to a reference genome using STAR." + echo "" + echo "Run Parameters:" + echo " --runRNGseed" + echo " type: integer" + echo " example: 777" + echo " random number generator seed." + echo "" + echo "Genome Parameters:" + echo " --genomeDir" + echo " type: file, required parameter, file must exist" + echo " example: ./GenomeDir" + echo " path to the directory where genome files are stored (for --runMode" + echo " alignReads) or will be generated (for --runMode generateGenome)" + echo "" + echo " --genomeLoad" + echo " type: string" + echo " example: NoSharedMemory" + echo " mode of shared memory usage for the genome files. Only used with" + echo " --runMode alignReads." + echo " - LoadAndKeep ... load genome into shared and keep it in memory" + echo " after run" + echo " - LoadAndRemove ... load genome into shared but remove it after run" + echo " - LoadAndExit ... load genome into shared memory and exit, keeping" + echo " the genome in memory for future runs" + echo " - Remove ... do not map anything, just remove loaded genome" + echo " from memory" + echo " - NoSharedMemory ... do not use shared memory, each job will have its" + echo " own private copy of the genome" + echo "" + echo " --genomeFastaFiles" + echo " type: file, multiple values allowed, file must exist" + echo " path(s) to the fasta files with the genome sequences, separated by" + echo " spaces. These files should be plain text FASTA files, they *cannot* be" + echo " zipped." + echo " Required for the genome generation (--runMode genomeGenerate). Can also" + echo " be used in the mapping (--runMode alignReads) to add extra (new)" + echo " sequences to the genome (e.g. spike-ins)." + echo "" + echo " --genomeFileSizes" + echo " type: integer, multiple values allowed" + echo " example: 0" + echo " genome files exact sizes in bytes. Typically, this should not be defined" + echo " by the user." + echo "" + echo " --genomeTransformOutput" + echo " type: string, multiple values allowed" + echo " which output to transform back to original genome" + echo " - SAM ... SAM/BAM alignments" + echo " - SJ ... splice junctions (SJ.out.tab)" + echo " - Quant ... quantifications (from --quantMode option)" + echo " - None ... no transformation of the output" + echo "" + echo " --genomeChrSetMitochondrial" + echo " type: string, multiple values allowed" + echo " example: chrM;M;MT" + echo " names of the mitochondrial chromosomes. Presently only used for STARsolo" + echo " statistics output/" + echo "" + echo "Splice Junctions Database:" + echo " --sjdbFileChrStartEnd" + echo " type: string, multiple values allowed" + echo " path to the files with genomic coordinates (chr start end" + echo " strand) for the splice junction introns. Multiple files can be" + echo " supplied and will be concatenated." + echo "" + echo " --sjdbGTFfile" + echo " type: file, file must exist" + echo " path to the GTF file with annotations" + echo "" + echo " --sjdbGTFchrPrefix" + echo " type: string" + echo " prefix for chromosome names in a GTF file (e.g. 'chr' for using ENSMEBL" + echo " annotations with UCSC genomes)" + echo "" + echo " --sjdbGTFfeatureExon" + echo " type: string" + echo " example: exon" + echo " feature type in GTF file to be used as exons for building transcripts" + echo "" + echo " --sjdbGTFtagExonParentTranscript" + echo " type: string" + echo " example: transcript_id" + echo " GTF attribute name for parent transcript ID (default \"transcript_id\"" + echo " works for GTF files)" + echo "" + echo " --sjdbGTFtagExonParentGene" + echo " type: string" + echo " example: gene_id" + echo " GTF attribute name for parent gene ID (default \"gene_id\" works for GTF" + echo " files)" + echo "" + echo " --sjdbGTFtagExonParentGeneName" + echo " type: string, multiple values allowed" + echo " example: gene_name" + echo " GTF attribute name for parent gene name" + echo "" + echo " --sjdbGTFtagExonParentGeneType" + echo " type: string, multiple values allowed" + echo " example: gene_type;gene_biotype" + echo " GTF attribute name for parent gene type" + echo "" + echo " --sjdbOverhang" + echo " type: integer" + echo " example: 100" + echo " length of the donor/acceptor sequence on each side of the junctions," + echo " ideally = (mate_length - 1)" + echo "" + echo " --sjdbScore" + echo " type: integer" + echo " example: 2" + echo " extra alignment score for alignments that cross database junctions" + echo "" + echo " --sjdbInsertSave" + echo " type: string" + echo " example: Basic" + echo " which files to save when sjdb junctions are inserted on the fly at the" + echo " mapping step" + echo " - Basic ... only small junction / transcript files" + echo " - All ... all files including big Genome, SA and SAindex - this will" + echo " create a complete genome directory" + echo "" + echo "Variation parameters:" + echo " --varVCFfile" + echo " type: string" + echo " path to the VCF file that contains variation data. The 10th column" + echo " should contain the genotype information, e.g. 0/1" + echo "" + echo "Read Parameters:" + echo " --readFilesType" + echo " type: string" + echo " example: Fastx" + echo " format of input read files" + echo " - Fastx ... FASTA or FASTQ" + echo " - SAM SE ... SAM or BAM single-end reads; for BAM use" + echo " --readFilesCommand samtools view" + echo " - SAM PE ... SAM or BAM paired-end reads; for BAM use" + echo " --readFilesCommand samtools view" + echo "" + echo " --readFilesSAMattrKeep" + echo " type: string, multiple values allowed" + echo " example: All" + echo " for --readFilesType SAM SE/PE, which SAM tags to keep in the output BAM," + echo " e.g.: --readFilesSAMtagsKeep RG PL" + echo " - All ... keep all tags" + echo " - None ... do not keep any tags" + echo "" + echo " --readFilesManifest" + echo " type: file, file must exist" + echo " path to the \"manifest\" file with the names of read files. The manifest" + echo " file should contain 3 tab-separated columns:" + echo " paired-end reads: read1_file_name \$tab\$ read2_file_name \$tab\$" + echo " read_group_line." + echo " single-end reads: read1_file_name \$tab\$ - \$tab\$" + echo " read_group_line." + echo " Spaces, but not tabs are allowed in file names." + echo " If read_group_line does not start with ID:, it can only contain one ID" + echo " field, and ID: will be added to it." + echo " If read_group_line starts with ID:, it can contain several fields" + echo " separated by \$tab\$, and all fields will be be copied verbatim into SAM" + echo " @RG header line." + echo "" + echo " --readFilesPrefix" + echo " type: string" + echo " prefix for the read files names, i.e. it will be added in front of the" + echo " strings in --readFilesIn" + echo "" + echo " --readFilesCommand" + echo " type: string, multiple values allowed" + echo " command line to execute for each of the input file. This command should" + echo " generate FASTA or FASTQ text and send it to stdout" + echo " For example: zcat - to uncompress .gz files, bzcat - to uncompress .bz2" + echo " files, etc." + echo "" + echo " --readMapNumber" + echo " type: integer" + echo " example: -1" + echo " number of reads to map from the beginning of the file" + echo " -1: map all reads" + echo "" + echo " --readMatesLengthsIn" + echo " type: string" + echo " example: NotEqual" + echo " Equal/NotEqual - lengths of names,sequences,qualities for both mates are" + echo " the same / not the same. NotEqual is safe in all situations." + echo "" + echo " --readNameSeparator" + echo " type: string, multiple values allowed" + echo " example: /" + echo " character(s) separating the part of the read names that will be trimmed" + echo " in output (read name after space is always trimmed)" + echo "" + echo " --readQualityScoreBase" + echo " type: integer" + echo " example: 33" + echo " number to be subtracted from the ASCII code to get Phred quality score" + echo "" + echo "Read Clipping:" + echo " --clipAdapterType" + echo " type: string" + echo " example: Hamming" + echo " adapter clipping type" + echo " - Hamming ... adapter clipping based on Hamming distance, with the" + echo " number of mismatches controlled by --clip5pAdapterMMp" + echo " - CellRanger4 ... 5p and 3p adapter clipping similar to CellRanger4." + echo " Utilizes Opal package by Martin Sosic: https://github.com/Martinsos/opal" + echo " - None ... no adapter clipping, all other clip* parameters are" + echo " disregarded" + echo "" + echo " --clip3pNbases" + echo " type: integer, multiple values allowed" + echo " example: 0" + echo " number(s) of bases to clip from 3p of each mate. If one value is given," + echo " it will be assumed the same for both mates." + echo "" + echo " --clip3pAdapterSeq" + echo " type: string, multiple values allowed" + echo " adapter sequences to clip from 3p of each mate. If one value is given," + echo " it will be assumed the same for both mates." + echo " - polyA ... polyA sequence with the length equal to read length" + echo "" + echo " --clip3pAdapterMMp" + echo " type: double, multiple values allowed" + echo " example: 0.1" + echo " max proportion of mismatches for 3p adapter clipping for each mate. If" + echo " one value is given, it will be assumed the same for both mates." + echo "" + echo " --clip3pAfterAdapterNbases" + echo " type: integer, multiple values allowed" + echo " example: 0" + echo " number of bases to clip from 3p of each mate after the adapter clipping." + echo " If one value is given, it will be assumed the same for both mates." + echo "" + echo " --clip5pNbases" + echo " type: integer, multiple values allowed" + echo " example: 0" + echo " number(s) of bases to clip from 5p of each mate. If one value is given," + echo " it will be assumed the same for both mates." + echo "" + echo "Limits:" + echo " --limitGenomeGenerateRAM" + echo " type: long" + echo " example: 31000000000" + echo " maximum available RAM (bytes) for genome generation" + echo "" + echo " --limitIObufferSize" + echo " type: long, multiple values allowed" + echo " example: 30000000;50000000" + echo " max available buffers size (bytes) for input/output, per thread" + echo "" + echo " --limitOutSAMoneReadBytes" + echo " type: long" + echo " example: 100000" + echo " max size of the SAM record (bytes) for one read. Recommended value:" + echo " >(2*(LengthMate1+LengthMate2+100)*outFilterMultimapNmax" + echo "" + echo " --limitOutSJoneRead" + echo " type: integer" + echo " example: 1000" + echo " max number of junctions for one read (including all multi-mappers)" + echo "" + echo " --limitOutSJcollapsed" + echo " type: integer" + echo " example: 1000000" + echo " max number of collapsed junctions" + echo "" + echo " --limitBAMsortRAM" + echo " type: long" + echo " example: 0" + echo " maximum available RAM (bytes) for sorting BAM. If =0, it will be set to" + echo " the genome index size. 0 value can only be used with --genomeLoad" + echo " NoSharedMemory option." + echo "" + echo " --limitSjdbInsertNsj" + echo " type: integer" + echo " example: 1000000" + echo " maximum number of junctions to be inserted to the genome on the fly at" + echo " the mapping stage, including those from annotations and those detected" + echo " in the 1st step of the 2-pass run" + echo "" + echo " --limitNreadsSoft" + echo " type: integer" + echo " example: -1" + echo " soft limit on the number of reads" + echo "" + echo "Output: general:" + echo " --outTmpKeep" + echo " type: string" + echo " whether to keep the temporary files after STAR runs is finished" + echo " - None ... remove all temporary files" + echo " - All ... keep all files" + echo "" + echo " --outStd" + echo " type: string" + echo " example: Log" + echo " which output will be directed to stdout (standard out)" + echo " - Log ... log messages" + echo " - SAM ... alignments in SAM format (which normally" + echo " are output to Aligned.out.sam file), normal standard output will go into" + echo " Log.std.out" + echo " - BAM_Unsorted ... alignments in BAM format, unsorted." + echo " Requires --outSAMtype BAM Unsorted" + echo " - BAM_SortedByCoordinate ... alignments in BAM format, sorted by" + echo " coordinate. Requires --outSAMtype BAM SortedByCoordinate" + echo " - BAM_Quant ... alignments to transcriptome in BAM format," + echo " unsorted. Requires --quantMode TranscriptomeSAM" + echo "" + echo " --outReadsUnmapped" + echo " type: string" + echo " output of unmapped and partially mapped (i.e. mapped only one mate of a" + echo " paired end read) reads in separate file(s)." + echo " - None ... no output" + echo " - Fastx ... output in separate fasta/fastq files, Unmapped.out.mate1/2" + echo "" + echo " --outQSconversionAdd" + echo " type: integer" + echo " example: 0" + echo " add this number to the quality score (e.g. to convert from Illumina to" + echo " Sanger, use -31)" + echo "" + echo " --outMultimapperOrder" + echo " type: string" + echo " example: Old_2.4" + echo " order of multimapping alignments in the output files" + echo " - Old_2.4 ... quasi-random order used before 2.5.0" + echo " - Random ... random order of alignments for each" + echo " multi-mapper. Read mates (pairs) are always adjacent, all alignment for" + echo " each read stay together. This option will become default in the future" + echo " releases." + echo "" + echo "Output: SAM and BAM:" + echo " --outSAMtype" + echo " type: string, multiple values allowed" + echo " example: SAM" + echo " type of SAM/BAM output" + echo " 1st word:" + echo " - BAM ... output BAM without sorting" + echo " - SAM ... output SAM without sorting" + echo " - None ... no SAM/BAM output" + echo " 2nd, 3rd:" + echo " - Unsorted ... standard unsorted" + echo " - SortedByCoordinate ... sorted by coordinate. This option will allocate" + echo " extra memory for sorting which can be specified by --limitBAMsortRAM." + echo "" + echo " --outSAMmode" + echo " type: string" + echo " example: Full" + echo " mode of SAM output" + echo " - None ... no SAM output" + echo " - Full ... full SAM output" + echo " - NoQS ... full SAM but without quality scores" + echo "" + echo " --outSAMstrandField" + echo " type: string" + echo " Cufflinks-like strand field flag" + echo " - None ... not used" + echo " - intronMotif ... strand derived from the intron motif. This option" + echo " changes the output alignments: reads with inconsistent and/or" + echo " non-canonical introns are filtered out." + echo "" + echo " --outSAMattributes" + echo " type: string, multiple values allowed" + echo " example: Standard" + echo " a string of desired SAM attributes, in the order desired for the output" + echo " SAM. Tags can be listed in any combination/order." + echo " ***Presets:" + echo " - None ... no attributes" + echo " - Standard ... NH HI AS nM" + echo " - All ... NH HI AS nM NM MD jM jI MC ch" + echo " ***Alignment:" + echo " - NH ... number of loci the reads maps to: =1 for unique" + echo " mappers, >1 for multimappers. Standard SAM tag." + echo " - HI ... multiple alignment index, starts with" + echo " --outSAMattrIHstart (=1 by default). Standard SAM tag." + echo " - AS ... local alignment score, +1/-1 for matches/mismateches," + echo " score* penalties for indels and gaps. For PE reads, total score for two" + echo " mates. Stadnard SAM tag." + echo " - nM ... number of mismatches. For PE reads, sum over two" + echo " mates." + echo " - NM ... edit distance to the reference (number of mismatched +" + echo " inserted + deleted bases) for each mate. Standard SAM tag." + echo " - MD ... string encoding mismatched and deleted reference bases" + echo " (see standard SAM specifications). Standard SAM tag." + echo " - jM ... intron motifs for all junctions (i.e. N in CIGAR): 0:" + echo " non-canonical; 1: GT/AG, 2: CT/AC, 3: GC/AG, 4: CT/GC, 5: AT/AC, 6:" + echo " GT/AT. If splice junctions database is used, and a junction is" + echo " annotated, 20 is added to its motif value." + echo " - jI ... start and end of introns for all junctions (1-based)." + echo " - XS ... alignment strand according to --outSAMstrandField." + echo " - MC ... mate's CIGAR string. Standard SAM tag." + echo " - ch ... marks all segment of all chimeric alingments for" + echo " --chimOutType WithinBAM output." + echo " - cN ... number of bases clipped from the read ends: 5' and 3'" + echo " ***Variation:" + echo " - vA ... variant allele" + echo " - vG ... genomic coordinate of the variant overlapped by the" + echo " read." + echo " - vW ... 1 - alignment passes WASP filtering; 2,3,4,5,6,7 -" + echo " alignment does not pass WASP filtering. Requires --waspOutputMode" + echo " SAMtag." + echo " - ha ... haplotype (1/2) when mapping to the diploid genome." + echo " Requires genome generated with --genomeTransformType Diploid ." + echo " ***STARsolo:" + echo " - CR CY UR UY ... sequences and quality scores of cell barcodes and UMIs" + echo " for the solo* demultiplexing." + echo " - GX GN ... gene ID and gene name for unique-gene reads." + echo " - gx gn ... gene IDs and gene names for unique- and multi-gene" + echo " reads." + echo " - CB UB ... error-corrected cell barcodes and UMIs for solo*" + echo " demultiplexing. Requires --outSAMtype BAM SortedByCoordinate." + echo " - sM ... assessment of CB and UMI." + echo " - sS ... sequence of the entire barcode (CB,UMI,adapter)." + echo " - sQ ... quality of the entire barcode." + echo " - sF ... type of feature overlap and number of features for" + echo " each alignment" + echo " ***Unsupported/undocumented:" + echo " - rB ... alignment block read/genomic coordinates." + echo " - vR ... read coordinate of the variant." + echo "" + echo " --outSAMattrIHstart" + echo " type: integer" + echo " example: 1" + echo " start value for the IH attribute. 0 may be required by some downstream" + echo " software, such as Cufflinks or StringTie." + echo "" + echo " --outSAMunmapped" + echo " type: string, multiple values allowed" + echo " output of unmapped reads in the SAM format" + echo " 1st word:" + echo " - None ... no output" + echo " - Within ... output unmapped reads within the main SAM file (i.e." + echo " Aligned.out.sam)" + echo " 2nd word:" + echo " - KeepPairs ... record unmapped mate for each alignment, and, in case of" + echo " unsorted output, keep it adjacent to its mapped mate. Only affects" + echo " multi-mapping reads." + echo "" + echo " --outSAMorder" + echo " type: string" + echo " example: Paired" + echo " type of sorting for the SAM output" + echo " Paired: one mate after the other for all paired alignments" + echo " PairedKeepInputOrder: one mate after the other for all paired" + echo " alignments, the order is kept the same as in the input FASTQ files" + echo "" + echo " --outSAMprimaryFlag" + echo " type: string" + echo " example: OneBestScore" + echo " which alignments are considered primary - all others will be marked with" + echo " 0x100 bit in the FLAG" + echo " - OneBestScore ... only one alignment with the best score is primary" + echo " - AllBestScore ... all alignments with the best score are primary" + echo "" + echo " --outSAMreadID" + echo " type: string" + echo " example: Standard" + echo " read ID record type" + echo " - Standard ... first word (until space) from the FASTx read ID line," + echo " removing /1,/2 from the end" + echo " - Number ... read number (index) in the FASTx file" + echo "" + echo " --outSAMmapqUnique" + echo " type: integer" + echo " example: 255" + echo " 0 to 255: the MAPQ value for unique mappers" + echo "" + echo " --outSAMflagOR" + echo " type: integer" + echo " example: 0" + echo " 0 to 65535: sam FLAG will be bitwise OR'd with this value, i.e." + echo " FLAG=FLAG | outSAMflagOR. This is applied after all flags have been set" + echo " by STAR, and after outSAMflagAND. Can be used to set specific bits that" + echo " are not set otherwise." + echo "" + echo " --outSAMflagAND" + echo " type: integer" + echo " example: 65535" + echo " 0 to 65535: sam FLAG will be bitwise AND'd with this value, i.e." + echo " FLAG=FLAG & outSAMflagOR. This is applied after all flags have been set" + echo " by STAR, but before outSAMflagOR. Can be used to unset specific bits" + echo " that are not set otherwise." + echo "" + echo " --outSAMattrRGline" + echo " type: string, multiple values allowed" + echo " SAM/BAM read group line. The first word contains the read group" + echo " identifier and must start with \"ID:\", e.g. --outSAMattrRGline ID:xxx" + echo " CN:yy \"DS:z z z\"." + echo " xxx will be added as RG tag to each output alignment. Any spaces in the" + echo " tag values have to be double quoted." + echo " Comma separated RG lines correspons to different (comma separated) input" + echo " files in --readFilesIn. Commas have to be surrounded by spaces, e.g." + echo " --outSAMattrRGline ID:xxx , ID:zzz \"DS:z z\" , ID:yyy DS:yyyy" + echo "" + echo " --outSAMheaderHD" + echo " type: string, multiple values allowed" + echo " @HD (header) line of the SAM header" + echo "" + echo " --outSAMheaderPG" + echo " type: string, multiple values allowed" + echo " extra @PG (software) line of the SAM header (in addition to STAR)" + echo "" + echo " --outSAMheaderCommentFile" + echo " type: string" + echo " path to the file with @CO (comment) lines of the SAM header" + echo "" + echo " --outSAMfilter" + echo " type: string, multiple values allowed" + echo " filter the output into main SAM/BAM files" + echo " - KeepOnlyAddedReferences ... only keep the reads for which all" + echo " alignments are to the extra reference sequences added with" + echo " --genomeFastaFiles at the mapping stage." + echo " - KeepAllAddedReferences ... keep all alignments to the extra reference" + echo " sequences added with --genomeFastaFiles at the mapping stage." + echo "" + echo " --outSAMmultNmax" + echo " type: integer" + echo " example: -1" + echo " max number of multiple alignments for a read that will be output to the" + echo " SAM/BAM files. Note that if this value is not equal to -1, the top" + echo " scoring alignment will be output first" + echo " - -1 ... all alignments (up to --outFilterMultimapNmax) will be output" + echo "" + echo " --outSAMtlen" + echo " type: integer" + echo " example: 1" + echo " calculation method for the TLEN field in the SAM/BAM files" + echo " - 1 ... leftmost base of the (+)strand mate to rightmost base of the" + echo " (-)mate. (+)sign for the (+)strand mate" + echo " - 2 ... leftmost base of any mate to rightmost base of any mate. (+)sign" + echo " for the mate with the leftmost base. This is different from 1 for" + echo " overlapping mates with protruding ends" + echo "" + echo " --outBAMcompression" + echo " type: integer" + echo " example: 1" + echo " -1 to 10 BAM compression level, -1=default compression (6?), 0=no" + echo " compression, 10=maximum compression" + echo "" + echo " --outBAMsortingThreadN" + echo " type: integer" + echo " example: 0" + echo " >=0: number of threads for BAM sorting. 0 will default to" + echo " min(6,--runThreadN)." + echo "" + echo " --outBAMsortingBinsN" + echo " type: integer" + echo " example: 50" + echo " >0: number of genome bins for coordinate-sorting" + echo "" + echo "BAM processing:" + echo " --bamRemoveDuplicatesType" + echo " type: string" + echo " mark duplicates in the BAM file, for now only works with (i) sorted BAM" + echo " fed with inputBAMfile, and (ii) for paired-end alignments only" + echo " - - ... no duplicate removal/marking" + echo " - UniqueIdentical ... mark all multimappers, and duplicate" + echo " unique mappers. The coordinates, FLAG, CIGAR must be identical" + echo " - UniqueIdenticalNotMulti ... mark duplicate unique mappers but not" + echo " multimappers." + echo "" + echo " --bamRemoveDuplicatesMate2basesN" + echo " type: integer" + echo " example: 0" + echo " number of bases from the 5' of mate 2 to use in collapsing (e.g. for" + echo " RAMPAGE)" + echo "" + echo "Output Wiggle:" + echo " --outWigType" + echo " type: string, multiple values allowed" + echo " type of signal output, e.g. \"bedGraph\" OR \"bedGraph read1_5p\". Requires" + echo " sorted BAM: --outSAMtype BAM SortedByCoordinate ." + echo " 1st word:" + echo " - None ... no signal output" + echo " - bedGraph ... bedGraph format" + echo " - wiggle ... wiggle format" + echo " 2nd word:" + echo " - read1_5p ... signal from only 5' of the 1st read, useful for" + echo " CAGE/RAMPAGE etc" + echo " - read2 ... signal from only 2nd read" + echo "" + echo " --outWigStrand" + echo " type: string" + echo " example: Stranded" + echo " strandedness of wiggle/bedGraph output" + echo " - Stranded ... separate strands, str1 and str2" + echo " - Unstranded ... collapsed strands" + echo "" + echo " --outWigReferencesPrefix" + echo " type: string" + echo " prefix matching reference names to include in the output wiggle file," + echo " e.g. \"chr\", default \"-\" - include all references" + echo "" + echo " --outWigNorm" + echo " type: string" + echo " example: RPM" + echo " type of normalization for the signal" + echo " - RPM ... reads per million of mapped reads" + echo " - None ... no normalization, \"raw\" counts" + echo "" + echo "Output Filtering:" + echo " --outFilterType" + echo " type: string" + echo " example: Normal" + echo " type of filtering" + echo " - Normal ... standard filtering using only current alignment" + echo " - BySJout ... keep only those reads that contain junctions that passed" + echo " filtering into SJ.out.tab" + echo "" + echo " --outFilterMultimapScoreRange" + echo " type: integer" + echo " example: 1" + echo " the score range below the maximum score for multimapping alignments" + echo "" + echo " --outFilterMultimapNmax" + echo " type: integer" + echo " example: 10" + echo " maximum number of loci the read is allowed to map to. Alignments (all of" + echo " them) will be output only if the read maps to no more loci than this" + echo " value." + echo " Otherwise no alignments will be output, and the read will be counted as" + echo " \"mapped to too many loci\" in the Log.final.out ." + echo "" + echo " --outFilterMismatchNmax" + echo " type: integer" + echo " example: 10" + echo " alignment will be output only if it has no more mismatches than this" + echo " value." + echo "" + echo " --outFilterMismatchNoverLmax" + echo " type: double" + echo " example: 0.3" + echo " alignment will be output only if its ratio of mismatches to *mapped*" + echo " length is less than or equal to this value." + echo "" + echo " --outFilterMismatchNoverReadLmax" + echo " type: double" + echo " example: 1.0" + echo " alignment will be output only if its ratio of mismatches to *read*" + echo " length is less than or equal to this value." + echo "" + echo " --outFilterScoreMin" + echo " type: integer" + echo " example: 0" + echo " alignment will be output only if its score is higher than or equal to" + echo " this value." + echo "" + echo " --outFilterScoreMinOverLread" + echo " type: double" + echo " example: 0.66" + echo " same as outFilterScoreMin, but normalized to read length (sum of mates'" + echo " lengths for paired-end reads)" + echo "" + echo " --outFilterMatchNmin" + echo " type: integer" + echo " example: 0" + echo " alignment will be output only if the number of matched bases is higher" + echo " than or equal to this value." + echo "" + echo " --outFilterMatchNminOverLread" + echo " type: double" + echo " example: 0.66" + echo " sam as outFilterMatchNmin, but normalized to the read length (sum of" + echo " mates' lengths for paired-end reads)." + echo "" + echo " --outFilterIntronMotifs" + echo " type: string" + echo " filter alignment using their motifs" + echo " - None ... no filtering" + echo " - RemoveNoncanonical ... filter out alignments that contain" + echo " non-canonical junctions" + echo " - RemoveNoncanonicalUnannotated ... filter out alignments that contain" + echo " non-canonical unannotated junctions when using annotated splice" + echo " junctions database. The annotated non-canonical junctions will be kept." + echo "" + echo " --outFilterIntronStrands" + echo " type: string" + echo " example: RemoveInconsistentStrands" + echo " filter alignments" + echo " - RemoveInconsistentStrands ... remove alignments that have" + echo " junctions with inconsistent strands" + echo " - None ... no filtering" + echo "" + echo "Output splice junctions (SJ.out.tab):" + echo " --outSJtype" + echo " type: string" + echo " example: Standard" + echo " type of splice junction output" + echo " - Standard ... standard SJ.out.tab output" + echo " - None ... no splice junction output" + echo "" + echo "Output Filtering: Splice Junctions:" + echo " --outSJfilterReads" + echo " type: string" + echo " example: All" + echo " which reads to consider for collapsed splice junctions output" + echo " - All ... all reads, unique- and multi-mappers" + echo " - Unique ... uniquely mapping reads only" + echo "" + echo " --outSJfilterOverhangMin" + echo " type: integer, multiple values allowed" + echo " example: 30;12;12;12" + echo " minimum overhang length for splice junctions on both sides for: (1)" + echo " non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC" + echo " motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif" + echo " does not apply to annotated junctions" + echo "" + echo " --outSJfilterCountUniqueMin" + echo " type: integer, multiple values allowed" + echo " example: 3;1;1;1" + echo " minimum uniquely mapping read count per junction for: (1) non-canonical" + echo " motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC" + echo " and GT/AT motif. -1 means no output for that motif" + echo " Junctions are output if one of outSJfilterCountUniqueMin OR" + echo " outSJfilterCountTotalMin conditions are satisfied" + echo " does not apply to annotated junctions" + echo "" + echo " --outSJfilterCountTotalMin" + echo " type: integer, multiple values allowed" + echo " example: 3;1;1;1" + echo " minimum total (multi-mapping+unique) read count per junction for: (1)" + echo " non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC" + echo " motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif" + echo " Junctions are output if one of outSJfilterCountUniqueMin OR" + echo " outSJfilterCountTotalMin conditions are satisfied" + echo " does not apply to annotated junctions" + echo "" + echo " --outSJfilterDistToOtherSJmin" + echo " type: integer, multiple values allowed" + echo " example: 10;0;5;10" + echo " minimum allowed distance to other junctions' donor/acceptor" + echo " does not apply to annotated junctions" + echo "" + echo " --outSJfilterIntronMaxVsReadN" + echo " type: integer, multiple values allowed" + echo " example: 50000;100000;200000" + echo " maximum gap allowed for junctions supported by 1,2,3,,,N reads" + echo " i.e. by default junctions supported by 1 read can have gaps <=50000b, by" + echo " 2 reads: <=100000b, by 3 reads: <=200000. by >=4 reads any gap" + echo " <=alignIntronMax" + echo " does not apply to annotated junctions" + echo "" + echo "Scoring:" + echo " --scoreGap" + echo " type: integer" + echo " example: 0" + echo " splice junction penalty (independent on intron motif)" + echo "" + echo " --scoreGapNoncan" + echo " type: integer" + echo " example: -8" + echo " non-canonical junction penalty (in addition to scoreGap)" + echo "" + echo " --scoreGapGCAG" + echo " type: integer" + echo " example: -4" + echo " GC/AG and CT/GC junction penalty (in addition to scoreGap)" + echo "" + echo " --scoreGapATAC" + echo " type: integer" + echo " example: -8" + echo " AT/AC and GT/AT junction penalty (in addition to scoreGap)" + echo "" + echo " --scoreGenomicLengthLog2scale" + echo " type: integer" + echo " example: 0" + echo " extra score logarithmically scaled with genomic length of the alignment:" + echo " scoreGenomicLengthLog2scale*log2(genomicLength)" + echo "" + echo " --scoreDelOpen" + echo " type: integer" + echo " example: -2" + echo " deletion open penalty" + echo "" + echo " --scoreDelBase" + echo " type: integer" + echo " example: -2" + echo " deletion extension penalty per base (in addition to scoreDelOpen)" + echo "" + echo " --scoreInsOpen" + echo " type: integer" + echo " example: -2" + echo " insertion open penalty" + echo "" + echo " --scoreInsBase" + echo " type: integer" + echo " example: -2" + echo " insertion extension penalty per base (in addition to scoreInsOpen)" + echo "" + echo " --scoreStitchSJshift" + echo " type: integer" + echo " example: 1" + echo " maximum score reduction while searching for SJ boundaries in the" + echo " stitching step" + echo "" + echo "Alignments and Seeding:" + echo " --seedSearchStartLmax" + echo " type: integer" + echo " example: 50" + echo " defines the search start point through the read - the read is split into" + echo " pieces no longer than this value" + echo "" + echo " --seedSearchStartLmaxOverLread" + echo " type: double" + echo " example: 1.0" + echo " seedSearchStartLmax normalized to read length (sum of mates' lengths for" + echo " paired-end reads)" + echo "" + echo " --seedSearchLmax" + echo " type: integer" + echo " example: 0" + echo " defines the maximum length of the seeds, if =0 seed length is not" + echo " limited" + echo "" + echo " --seedMultimapNmax" + echo " type: integer" + echo " example: 10000" + echo " only pieces that map fewer than this value are utilized in the stitching" + echo " procedure" + echo "" + echo " --seedPerReadNmax" + echo " type: integer" + echo " example: 1000" + echo " max number of seeds per read" + echo "" + echo " --seedPerWindowNmax" + echo " type: integer" + echo " example: 50" + echo " max number of seeds per window" + echo "" + echo " --seedNoneLociPerWindow" + echo " type: integer" + echo " example: 10" + echo " max number of one seed loci per window" + echo "" + echo " --seedSplitMin" + echo " type: integer" + echo " example: 12" + echo " min length of the seed sequences split by Ns or mate gap" + echo "" + echo " --seedMapMin" + echo " type: integer" + echo " example: 5" + echo " min length of seeds to be mapped" + echo "" + echo " --alignIntronMin" + echo " type: integer" + echo " example: 21" + echo " minimum intron size, genomic gap is considered intron if its" + echo " length>=alignIntronMin, otherwise it is considered Deletion" + echo "" + echo " --alignIntronMax" + echo " type: integer" + echo " example: 0" + echo " maximum intron size, if 0, max intron size will be determined by" + echo " (2^winBinNbits)*winAnchorDistNbins" + echo "" + echo " --alignMatesGapMax" + echo " type: integer" + echo " example: 0" + echo " maximum gap between two mates, if 0, max intron gap will be determined" + echo " by (2^winBinNbits)*winAnchorDistNbins" + echo "" + echo " --alignSJoverhangMin" + echo " type: integer" + echo " example: 5" + echo " minimum overhang (i.e. block size) for spliced alignments" + echo "" + echo " --alignSJstitchMismatchNmax" + echo " type: integer, multiple values allowed" + echo " example: 0;-1;0;0" + echo " maximum number of mismatches for stitching of the splice junctions (-1:" + echo " no limit)." + echo " (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC" + echo " motif, (4) AT/AC and GT/AT motif." + echo "" + echo " --alignSJDBoverhangMin" + echo " type: integer" + echo " example: 3" + echo " minimum overhang (i.e. block size) for annotated (sjdb) spliced" + echo " alignments" + echo "" + echo " --alignSplicedMateMapLmin" + echo " type: integer" + echo " example: 0" + echo " minimum mapped length for a read mate that is spliced" + echo "" + echo " --alignSplicedMateMapLminOverLmate" + echo " type: double" + echo " example: 0.66" + echo " alignSplicedMateMapLmin normalized to mate length" + echo "" + echo " --alignWindowsPerReadNmax" + echo " type: integer" + echo " example: 10000" + echo " max number of windows per read" + echo "" + echo " --alignTranscriptsPerWindowNmax" + echo " type: integer" + echo " example: 100" + echo " max number of transcripts per window" + echo "" + echo " --alignTranscriptsPerReadNmax" + echo " type: integer" + echo " example: 10000" + echo " max number of different alignments per read to consider" + echo "" + echo " --alignEndsType" + echo " type: string" + echo " example: Local" + echo " type of read ends alignment" + echo " - Local ... standard local alignment with soft-clipping" + echo " allowed" + echo " - EndToEnd ... force end-to-end read alignment, do not" + echo " soft-clip" + echo " - Extend5pOfRead1 ... fully extend only the 5p of the read1, all other" + echo " ends: local alignment" + echo " - Extend5pOfReads12 ... fully extend only the 5p of the both read1 and" + echo " read2, all other ends: local alignment" + echo "" + echo " --alignEndsProtrude" + echo " type: string" + echo " example: 0 ConcordantPair" + echo " allow protrusion of alignment ends, i.e. start (end) of the +strand mate" + echo " downstream of the start (end) of the -strand mate" + echo " 1st word: int: maximum number of protrusion bases allowed" + echo " 2nd word: string:" + echo " - ConcordantPair ... report alignments with non-zero" + echo " protrusion as concordant pairs" + echo " - DiscordantPair ... report alignments with non-zero" + echo " protrusion as discordant pairs" + echo "" + echo " --alignSoftClipAtReferenceEnds" + echo " type: string" + echo " example: Yes" + echo " allow the soft-clipping of the alignments past the end of the" + echo " chromosomes" + echo " - Yes ... allow" + echo " - No ... prohibit, useful for compatibility with Cufflinks" + echo "" + echo " --alignInsertionFlush" + echo " type: string" + echo " how to flush ambiguous insertion positions" + echo " - None ... insertions are not flushed" + echo " - Right ... insertions are flushed to the right" + echo "" + echo "Paired-End reads:" + echo " --peOverlapNbasesMin" + echo " type: integer" + echo " example: 0" + echo " minimum number of overlapping bases to trigger mates merging and" + echo " realignment. Specify >0 value to switch on the \"merginf of overlapping" + echo " mates\" algorithm." + echo "" + echo " --peOverlapMMp" + echo " type: double" + echo " example: 0.01" + echo " maximum proportion of mismatched bases in the overlap area" + echo "" + echo "Windows, Anchors, Binning:" + echo " --winAnchorMultimapNmax" + echo " type: integer" + echo " example: 50" + echo " max number of loci anchors are allowed to map to" + echo "" + echo " --winBinNbits" + echo " type: integer" + echo " example: 16" + echo " =log2(winBin), where winBin is the size of the bin for the" + echo " windows/clustering, each window will occupy an integer number of bins." + echo "" + echo " --winAnchorDistNbins" + echo " type: integer" + echo " example: 9" + echo " max number of bins between two anchors that allows aggregation of" + echo " anchors into one window" + echo "" + echo " --winFlankNbins" + echo " type: integer" + echo " example: 4" + echo " log2(winFlank), where win Flank is the size of the left and right" + echo " flanking regions for each window" + echo "" + echo " --winReadCoverageRelativeMin" + echo " type: double" + echo " example: 0.5" + echo " minimum relative coverage of the read sequence by the seeds in a window," + echo " for STARlong algorithm only." + echo "" + echo " --winReadCoverageBasesMin" + echo " type: integer" + echo " example: 0" + echo " minimum number of bases covered by the seeds in a window , for STARlong" + echo " algorithm only." + echo "" + echo "Chimeric Alignments:" + echo " --chimOutType" + echo " type: string, multiple values allowed" + echo " example: Junctions" + echo " type of chimeric output" + echo " - Junctions ... Chimeric.out.junction" + echo " - SeparateSAMold ... output old SAM into separate Chimeric.out.sam file" + echo " - WithinBAM ... output into main aligned BAM files (Aligned.*.bam)" + echo " - WithinBAM HardClip ... (default) hard-clipping in the CIGAR for" + echo " supplemental chimeric alignments (default if no 2nd word is present)" + echo " - WithinBAM SoftClip ... soft-clipping in the CIGAR for supplemental" + echo " chimeric alignments" + echo "" + echo " --chimSegmentMin" + echo " type: integer" + echo " example: 0" + echo " minimum length of chimeric segment length, if ==0, no chimeric output" + echo "" + echo " --chimScoreMin" + echo " type: integer" + echo " example: 0" + echo " minimum total (summed) score of the chimeric segments" + echo "" + echo " --chimScoreDropMax" + echo " type: integer" + echo " example: 20" + echo " max drop (difference) of chimeric score (the sum of scores of all" + echo " chimeric segments) from the read length" + echo "" + echo " --chimScoreSeparation" + echo " type: integer" + echo " example: 10" + echo " minimum difference (separation) between the best chimeric score and the" + echo " next one" + echo "" + echo " --chimScoreJunctionNonGTAG" + echo " type: integer" + echo " example: -1" + echo " penalty for a non-GT/AG chimeric junction" + echo "" + echo " --chimJunctionOverhangMin" + echo " type: integer" + echo " example: 20" + echo " minimum overhang for a chimeric junction" + echo "" + echo " --chimSegmentReadGapMax" + echo " type: integer" + echo " example: 0" + echo " maximum gap in the read sequence between chimeric segments" + echo "" + echo " --chimFilter" + echo " type: string, multiple values allowed" + echo " example: banGenomicN" + echo " different filters for chimeric alignments" + echo " - None ... no filtering" + echo " - banGenomicN ... Ns are not allowed in the genome sequence around the" + echo " chimeric junction" + echo "" + echo " --chimMainSegmentMultNmax" + echo " type: integer" + echo " example: 10" + echo " maximum number of multi-alignments for the main chimeric segment. =1" + echo " will prohibit multimapping main segments." + echo "" + echo " --chimMultimapNmax" + echo " type: integer" + echo " example: 0" + echo " maximum number of chimeric multi-alignments" + echo " - 0 ... use the old scheme for chimeric detection which only considered" + echo " unique alignments" + echo "" + echo " --chimMultimapScoreRange" + echo " type: integer" + echo " example: 1" + echo " the score range for multi-mapping chimeras below the best chimeric" + echo " score. Only works with --chimMultimapNmax > 1" + echo "" + echo " --chimNonchimScoreDropMin" + echo " type: integer" + echo " example: 20" + echo " to trigger chimeric detection, the drop in the best non-chimeric" + echo " alignment score with respect to the read length has to be greater than" + echo " this value" + echo "" + echo " --chimOutJunctionFormat" + echo " type: integer" + echo " example: 0" + echo " formatting type for the Chimeric.out.junction file" + echo " - 0 ... no comment lines/headers" + echo " - 1 ... comment lines at the end of the file: command line and Nreads:" + echo " total, unique/multi-mapping" + echo "" + echo "Quantification of Annotations:" + echo " --quantMode" + echo " type: string, multiple values allowed" + echo " types of quantification requested" + echo " - - ... none" + echo " - TranscriptomeSAM ... output SAM/BAM alignments to transcriptome into a" + echo " separate file" + echo " - GeneCounts ... count reads per gene" + echo "" + echo " --quantTranscriptomeBAMcompression" + echo " type: integer" + echo " example: 1" + echo " -2 to 10 transcriptome BAM compression level" + echo " - -2 ... no BAM output" + echo " - -1 ... default compression (6?)" + echo " - 0 ... no compression" + echo " - 10 ... maximum compression" + echo "" + echo " --quantTranscriptomeSAMoutput" + echo " type: string" + echo " example: BanSingleEnd_BanIndels_ExtendSoftclip" + echo " alignment filtering for TranscriptomeSAM output" + echo " - BanSingleEnd_BanIndels_ExtendSoftclip ... prohibit indels and" + echo " single-end alignments, extend softclips - compatible with RSEM" + echo " - BanSingleEnd ... prohibit single-end alignments, allow" + echo " indels and softclips" + echo " - BanSingleEnd_ExtendSoftclip ... prohibit single-end alignments, extend" + echo " softclips, allow indels" + echo "" + echo "2-pass Mapping:" + echo " --twopassMode" + echo " type: string" + echo " 2-pass mapping mode." + echo " - None ... 1-pass mapping" + echo " - Basic ... basic 2-pass mapping, with all 1st pass junctions" + echo " inserted into the genome indices on the fly" + echo "" + echo " --twopass1readsN" + echo " type: integer" + echo " example: -1" + echo " number of reads to process for the 1st step. Use very large number (or" + echo " default -1) to map all reads in the first step." + echo "" + echo "WASP parameters:" + echo " --waspOutputMode" + echo " type: string" + echo " WASP allele-specific output type. This is re-implementation of the" + echo " original WASP mappability filtering by Bryce van de Geijn, Graham" + echo " McVicker, Yoav Gilad & Jonathan K Pritchard. Please cite the original" + echo " WASP paper: Nature Methods 12, 1061-1063 (2015)," + echo " https://www.nature.com/articles/nmeth.3582 ." + echo " - SAMtag ... add WASP tags to the alignments that pass WASP" + echo " filtering" + echo "" + echo "Inputs:" + echo " --readFilesIn, --input" + echo " type: file, required parameter, multiple values allowed, file must exist" + echo " example: mysample_S1_L001_R1_001.fastq.gz" + echo " The single-end or paired-end R1 FastQ files to be processed." + echo "" + echo " --input_r2" + echo " type: file, multiple values allowed, file must exist" + echo " example: mysample_S1_L001_R2_001.fastq.gz" + echo " The paired-end R2 FastQ files to be processed. Only required if --input" + echo " is a paired-end R1 file." + echo "" + echo "Outputs:" + echo " --aligned_reads" + echo " type: file, required parameter, output, file must exist" + echo " example: aligned_reads.bam" + echo " The output file containing the aligned reads." + echo "" + echo " --reads_per_gene" + echo " type: file, output, file must exist" + echo " example: reads_per_gene.tsv" + echo " The output file containing the number of reads per gene." + echo "" + echo " --unmapped" + echo " type: file, output, file must exist" + echo " example: unmapped.fastq" + echo " The output file containing the unmapped reads." + echo "" + echo " --unmapped_r2" + echo " type: file, output, file must exist" + echo " example: unmapped_r2.fastq" + echo " The output file containing the unmapped R2 reads." + echo "" + echo " --chimeric_junctions" + echo " type: file, output, file must exist" + echo " example: chimeric_junctions.tsv" + echo " The output file containing the chimeric junctions." + echo "" + echo " --log" + echo " type: file, output, file must exist" + echo " example: log.txt" + echo " The output file containing the log of the alignment process." + echo "" + echo " --splice_junctions" + echo " type: file, output, file must exist" + echo " example: splice_junctions.tsv" + echo " The output file containing the splice junctions." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM python:3.12-slim +ENTRYPOINT [] +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y procps gzip bzip2 && \ + rm -rf /var/lib/apt/lists/* + +ENV STAR_VERSION 2.7.11b +ENV PACKAGES gcc g++ make wget zlib1g-dev unzip xxd +RUN apt-get update && \ + apt-get install -y --no-install-recommends ${PACKAGES} && \ + cd /tmp && \ + wget --no-check-certificate https://github.com/alexdobin/STAR/archive/refs/tags/${STAR_VERSION}.zip && \ + unzip ${STAR_VERSION}.zip && \ + cd STAR-${STAR_VERSION}/source && \ + make STARstatic CXXFLAGS_SIMD=-std=c++11 && \ + cp STAR /usr/local/bin && \ + cd / && \ + rm -rf /tmp/STAR-${STAR_VERSION} /tmp/${STAR_VERSION}.zip && \ + apt-get --purge autoremove -y ${PACKAGES} && \ + apt-get clean + +RUN STAR --version | sed 's#\(.*\)#star: "\1"#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component star star_align_reads" +LABEL org.opencontainers.image.created="2024-06-24T08:36:45Z" +LABEL org.opencontainers.image.source="https://github.com/alexdobin/STAR" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "star_align_reads main" + exit + ;; + --runRNGseed) + [ -n "$VIASH_PAR_RUNRNGSEED" ] && ViashError Bad arguments for option \'--runRNGseed\': \'$VIASH_PAR_RUNRNGSEED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RUNRNGSEED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --runRNGseed. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --runRNGseed=*) + [ -n "$VIASH_PAR_RUNRNGSEED" ] && ViashError Bad arguments for option \'--runRNGseed=*\': \'$VIASH_PAR_RUNRNGSEED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_RUNRNGSEED=$(ViashRemoveFlags "$1") + shift 1 + ;; + --genomeDir) + [ -n "$VIASH_PAR_GENOMEDIR" ] && ViashError Bad arguments for option \'--genomeDir\': \'$VIASH_PAR_GENOMEDIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMEDIR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeDir. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeDir=*) + [ -n "$VIASH_PAR_GENOMEDIR" ] && ViashError Bad arguments for option \'--genomeDir=*\': \'$VIASH_PAR_GENOMEDIR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMEDIR=$(ViashRemoveFlags "$1") + shift 1 + ;; + --genomeLoad) + [ -n "$VIASH_PAR_GENOMELOAD" ] && ViashError Bad arguments for option \'--genomeLoad\': \'$VIASH_PAR_GENOMELOAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMELOAD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeLoad. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeLoad=*) + [ -n "$VIASH_PAR_GENOMELOAD" ] && ViashError Bad arguments for option \'--genomeLoad=*\': \'$VIASH_PAR_GENOMELOAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMELOAD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --genomeFastaFiles) + if [ -z "$VIASH_PAR_GENOMEFASTAFILES" ]; then + VIASH_PAR_GENOMEFASTAFILES="$2" + else + VIASH_PAR_GENOMEFASTAFILES="$VIASH_PAR_GENOMEFASTAFILES;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeFastaFiles. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeFastaFiles=*) + if [ -z "$VIASH_PAR_GENOMEFASTAFILES" ]; then + VIASH_PAR_GENOMEFASTAFILES=$(ViashRemoveFlags "$1") + else + VIASH_PAR_GENOMEFASTAFILES="$VIASH_PAR_GENOMEFASTAFILES;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --genomeFileSizes) + if [ -z "$VIASH_PAR_GENOMEFILESIZES" ]; then + VIASH_PAR_GENOMEFILESIZES="$2" + else + VIASH_PAR_GENOMEFILESIZES="$VIASH_PAR_GENOMEFILESIZES;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeFileSizes. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeFileSizes=*) + if [ -z "$VIASH_PAR_GENOMEFILESIZES" ]; then + VIASH_PAR_GENOMEFILESIZES=$(ViashRemoveFlags "$1") + else + VIASH_PAR_GENOMEFILESIZES="$VIASH_PAR_GENOMEFILESIZES;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --genomeTransformOutput) + if [ -z "$VIASH_PAR_GENOMETRANSFORMOUTPUT" ]; then + VIASH_PAR_GENOMETRANSFORMOUTPUT="$2" + else + VIASH_PAR_GENOMETRANSFORMOUTPUT="$VIASH_PAR_GENOMETRANSFORMOUTPUT;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeTransformOutput. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeTransformOutput=*) + if [ -z "$VIASH_PAR_GENOMETRANSFORMOUTPUT" ]; then + VIASH_PAR_GENOMETRANSFORMOUTPUT=$(ViashRemoveFlags "$1") + else + VIASH_PAR_GENOMETRANSFORMOUTPUT="$VIASH_PAR_GENOMETRANSFORMOUTPUT;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --genomeChrSetMitochondrial) + if [ -z "$VIASH_PAR_GENOMECHRSETMITOCHONDRIAL" ]; then + VIASH_PAR_GENOMECHRSETMITOCHONDRIAL="$2" + else + VIASH_PAR_GENOMECHRSETMITOCHONDRIAL="$VIASH_PAR_GENOMECHRSETMITOCHONDRIAL;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeChrSetMitochondrial. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeChrSetMitochondrial=*) + if [ -z "$VIASH_PAR_GENOMECHRSETMITOCHONDRIAL" ]; then + VIASH_PAR_GENOMECHRSETMITOCHONDRIAL=$(ViashRemoveFlags "$1") + else + VIASH_PAR_GENOMECHRSETMITOCHONDRIAL="$VIASH_PAR_GENOMECHRSETMITOCHONDRIAL;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --sjdbFileChrStartEnd) + if [ -z "$VIASH_PAR_SJDBFILECHRSTARTEND" ]; then + VIASH_PAR_SJDBFILECHRSTARTEND="$2" + else + VIASH_PAR_SJDBFILECHRSTARTEND="$VIASH_PAR_SJDBFILECHRSTARTEND;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbFileChrStartEnd. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbFileChrStartEnd=*) + if [ -z "$VIASH_PAR_SJDBFILECHRSTARTEND" ]; then + VIASH_PAR_SJDBFILECHRSTARTEND=$(ViashRemoveFlags "$1") + else + VIASH_PAR_SJDBFILECHRSTARTEND="$VIASH_PAR_SJDBFILECHRSTARTEND;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --sjdbGTFfile) + [ -n "$VIASH_PAR_SJDBGTFFILE" ] && ViashError Bad arguments for option \'--sjdbGTFfile\': \'$VIASH_PAR_SJDBGTFFILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFFILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFfile. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFfile=*) + [ -n "$VIASH_PAR_SJDBGTFFILE" ] && ViashError Bad arguments for option \'--sjdbGTFfile=*\': \'$VIASH_PAR_SJDBGTFFILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFFILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbGTFchrPrefix) + [ -n "$VIASH_PAR_SJDBGTFCHRPREFIX" ] && ViashError Bad arguments for option \'--sjdbGTFchrPrefix\': \'$VIASH_PAR_SJDBGTFCHRPREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFCHRPREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFchrPrefix. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFchrPrefix=*) + [ -n "$VIASH_PAR_SJDBGTFCHRPREFIX" ] && ViashError Bad arguments for option \'--sjdbGTFchrPrefix=*\': \'$VIASH_PAR_SJDBGTFCHRPREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFCHRPREFIX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbGTFfeatureExon) + [ -n "$VIASH_PAR_SJDBGTFFEATUREEXON" ] && ViashError Bad arguments for option \'--sjdbGTFfeatureExon\': \'$VIASH_PAR_SJDBGTFFEATUREEXON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFFEATUREEXON="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFfeatureExon. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFfeatureExon=*) + [ -n "$VIASH_PAR_SJDBGTFFEATUREEXON" ] && ViashError Bad arguments for option \'--sjdbGTFfeatureExon=*\': \'$VIASH_PAR_SJDBGTFFEATUREEXON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFFEATUREEXON=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbGTFtagExonParentTranscript) + [ -n "$VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT" ] && ViashError Bad arguments for option \'--sjdbGTFtagExonParentTranscript\': \'$VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFtagExonParentTranscript. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFtagExonParentTranscript=*) + [ -n "$VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT" ] && ViashError Bad arguments for option \'--sjdbGTFtagExonParentTranscript=*\': \'$VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbGTFtagExonParentGene) + [ -n "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENE" ] && ViashError Bad arguments for option \'--sjdbGTFtagExonParentGene\': \'$VIASH_PAR_SJDBGTFTAGEXONPARENTGENE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFTAGEXONPARENTGENE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFtagExonParentGene. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFtagExonParentGene=*) + [ -n "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENE" ] && ViashError Bad arguments for option \'--sjdbGTFtagExonParentGene=*\': \'$VIASH_PAR_SJDBGTFTAGEXONPARENTGENE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFTAGEXONPARENTGENE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbGTFtagExonParentGeneName) + if [ -z "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME" ]; then + VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME="$2" + else + VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME="$VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFtagExonParentGeneName. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFtagExonParentGeneName=*) + if [ -z "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME" ]; then + VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME=$(ViashRemoveFlags "$1") + else + VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME="$VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --sjdbGTFtagExonParentGeneType) + if [ -z "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE" ]; then + VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE="$2" + else + VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE="$VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFtagExonParentGeneType. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFtagExonParentGeneType=*) + if [ -z "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE" ]; then + VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE="$VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --sjdbOverhang) + [ -n "$VIASH_PAR_SJDBOVERHANG" ] && ViashError Bad arguments for option \'--sjdbOverhang\': \'$VIASH_PAR_SJDBOVERHANG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBOVERHANG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbOverhang. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbOverhang=*) + [ -n "$VIASH_PAR_SJDBOVERHANG" ] && ViashError Bad arguments for option \'--sjdbOverhang=*\': \'$VIASH_PAR_SJDBOVERHANG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBOVERHANG=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbScore) + [ -n "$VIASH_PAR_SJDBSCORE" ] && ViashError Bad arguments for option \'--sjdbScore\': \'$VIASH_PAR_SJDBSCORE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBSCORE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbScore. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbScore=*) + [ -n "$VIASH_PAR_SJDBSCORE" ] && ViashError Bad arguments for option \'--sjdbScore=*\': \'$VIASH_PAR_SJDBSCORE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBSCORE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbInsertSave) + [ -n "$VIASH_PAR_SJDBINSERTSAVE" ] && ViashError Bad arguments for option \'--sjdbInsertSave\': \'$VIASH_PAR_SJDBINSERTSAVE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBINSERTSAVE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbInsertSave. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbInsertSave=*) + [ -n "$VIASH_PAR_SJDBINSERTSAVE" ] && ViashError Bad arguments for option \'--sjdbInsertSave=*\': \'$VIASH_PAR_SJDBINSERTSAVE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBINSERTSAVE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --varVCFfile) + [ -n "$VIASH_PAR_VARVCFFILE" ] && ViashError Bad arguments for option \'--varVCFfile\': \'$VIASH_PAR_VARVCFFILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_VARVCFFILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --varVCFfile. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --varVCFfile=*) + [ -n "$VIASH_PAR_VARVCFFILE" ] && ViashError Bad arguments for option \'--varVCFfile=*\': \'$VIASH_PAR_VARVCFFILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_VARVCFFILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --readFilesType) + [ -n "$VIASH_PAR_READFILESTYPE" ] && ViashError Bad arguments for option \'--readFilesType\': \'$VIASH_PAR_READFILESTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READFILESTYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --readFilesType. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --readFilesType=*) + [ -n "$VIASH_PAR_READFILESTYPE" ] && ViashError Bad arguments for option \'--readFilesType=*\': \'$VIASH_PAR_READFILESTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READFILESTYPE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --readFilesSAMattrKeep) + if [ -z "$VIASH_PAR_READFILESSAMATTRKEEP" ]; then + VIASH_PAR_READFILESSAMATTRKEEP="$2" + else + VIASH_PAR_READFILESSAMATTRKEEP="$VIASH_PAR_READFILESSAMATTRKEEP;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --readFilesSAMattrKeep. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --readFilesSAMattrKeep=*) + if [ -z "$VIASH_PAR_READFILESSAMATTRKEEP" ]; then + VIASH_PAR_READFILESSAMATTRKEEP=$(ViashRemoveFlags "$1") + else + VIASH_PAR_READFILESSAMATTRKEEP="$VIASH_PAR_READFILESSAMATTRKEEP;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --readFilesManifest) + [ -n "$VIASH_PAR_READFILESMANIFEST" ] && ViashError Bad arguments for option \'--readFilesManifest\': \'$VIASH_PAR_READFILESMANIFEST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READFILESMANIFEST="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --readFilesManifest. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --readFilesManifest=*) + [ -n "$VIASH_PAR_READFILESMANIFEST" ] && ViashError Bad arguments for option \'--readFilesManifest=*\': \'$VIASH_PAR_READFILESMANIFEST\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READFILESMANIFEST=$(ViashRemoveFlags "$1") + shift 1 + ;; + --readFilesPrefix) + [ -n "$VIASH_PAR_READFILESPREFIX" ] && ViashError Bad arguments for option \'--readFilesPrefix\': \'$VIASH_PAR_READFILESPREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READFILESPREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --readFilesPrefix. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --readFilesPrefix=*) + [ -n "$VIASH_PAR_READFILESPREFIX" ] && ViashError Bad arguments for option \'--readFilesPrefix=*\': \'$VIASH_PAR_READFILESPREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READFILESPREFIX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --readFilesCommand) + if [ -z "$VIASH_PAR_READFILESCOMMAND" ]; then + VIASH_PAR_READFILESCOMMAND="$2" + else + VIASH_PAR_READFILESCOMMAND="$VIASH_PAR_READFILESCOMMAND;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --readFilesCommand. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --readFilesCommand=*) + if [ -z "$VIASH_PAR_READFILESCOMMAND" ]; then + VIASH_PAR_READFILESCOMMAND=$(ViashRemoveFlags "$1") + else + VIASH_PAR_READFILESCOMMAND="$VIASH_PAR_READFILESCOMMAND;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --readMapNumber) + [ -n "$VIASH_PAR_READMAPNUMBER" ] && ViashError Bad arguments for option \'--readMapNumber\': \'$VIASH_PAR_READMAPNUMBER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READMAPNUMBER="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --readMapNumber. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --readMapNumber=*) + [ -n "$VIASH_PAR_READMAPNUMBER" ] && ViashError Bad arguments for option \'--readMapNumber=*\': \'$VIASH_PAR_READMAPNUMBER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READMAPNUMBER=$(ViashRemoveFlags "$1") + shift 1 + ;; + --readMatesLengthsIn) + [ -n "$VIASH_PAR_READMATESLENGTHSIN" ] && ViashError Bad arguments for option \'--readMatesLengthsIn\': \'$VIASH_PAR_READMATESLENGTHSIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READMATESLENGTHSIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --readMatesLengthsIn. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --readMatesLengthsIn=*) + [ -n "$VIASH_PAR_READMATESLENGTHSIN" ] && ViashError Bad arguments for option \'--readMatesLengthsIn=*\': \'$VIASH_PAR_READMATESLENGTHSIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READMATESLENGTHSIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --readNameSeparator) + if [ -z "$VIASH_PAR_READNAMESEPARATOR" ]; then + VIASH_PAR_READNAMESEPARATOR="$2" + else + VIASH_PAR_READNAMESEPARATOR="$VIASH_PAR_READNAMESEPARATOR;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --readNameSeparator. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --readNameSeparator=*) + if [ -z "$VIASH_PAR_READNAMESEPARATOR" ]; then + VIASH_PAR_READNAMESEPARATOR=$(ViashRemoveFlags "$1") + else + VIASH_PAR_READNAMESEPARATOR="$VIASH_PAR_READNAMESEPARATOR;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --readQualityScoreBase) + [ -n "$VIASH_PAR_READQUALITYSCOREBASE" ] && ViashError Bad arguments for option \'--readQualityScoreBase\': \'$VIASH_PAR_READQUALITYSCOREBASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READQUALITYSCOREBASE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --readQualityScoreBase. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --readQualityScoreBase=*) + [ -n "$VIASH_PAR_READQUALITYSCOREBASE" ] && ViashError Bad arguments for option \'--readQualityScoreBase=*\': \'$VIASH_PAR_READQUALITYSCOREBASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READQUALITYSCOREBASE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --clipAdapterType) + [ -n "$VIASH_PAR_CLIPADAPTERTYPE" ] && ViashError Bad arguments for option \'--clipAdapterType\': \'$VIASH_PAR_CLIPADAPTERTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CLIPADAPTERTYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --clipAdapterType. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --clipAdapterType=*) + [ -n "$VIASH_PAR_CLIPADAPTERTYPE" ] && ViashError Bad arguments for option \'--clipAdapterType=*\': \'$VIASH_PAR_CLIPADAPTERTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CLIPADAPTERTYPE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --clip3pNbases) + if [ -z "$VIASH_PAR_CLIP3PNBASES" ]; then + VIASH_PAR_CLIP3PNBASES="$2" + else + VIASH_PAR_CLIP3PNBASES="$VIASH_PAR_CLIP3PNBASES;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --clip3pNbases. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --clip3pNbases=*) + if [ -z "$VIASH_PAR_CLIP3PNBASES" ]; then + VIASH_PAR_CLIP3PNBASES=$(ViashRemoveFlags "$1") + else + VIASH_PAR_CLIP3PNBASES="$VIASH_PAR_CLIP3PNBASES;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --clip3pAdapterSeq) + if [ -z "$VIASH_PAR_CLIP3PADAPTERSEQ" ]; then + VIASH_PAR_CLIP3PADAPTERSEQ="$2" + else + VIASH_PAR_CLIP3PADAPTERSEQ="$VIASH_PAR_CLIP3PADAPTERSEQ;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --clip3pAdapterSeq. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --clip3pAdapterSeq=*) + if [ -z "$VIASH_PAR_CLIP3PADAPTERSEQ" ]; then + VIASH_PAR_CLIP3PADAPTERSEQ=$(ViashRemoveFlags "$1") + else + VIASH_PAR_CLIP3PADAPTERSEQ="$VIASH_PAR_CLIP3PADAPTERSEQ;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --clip3pAdapterMMp) + if [ -z "$VIASH_PAR_CLIP3PADAPTERMMP" ]; then + VIASH_PAR_CLIP3PADAPTERMMP="$2" + else + VIASH_PAR_CLIP3PADAPTERMMP="$VIASH_PAR_CLIP3PADAPTERMMP;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --clip3pAdapterMMp. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --clip3pAdapterMMp=*) + if [ -z "$VIASH_PAR_CLIP3PADAPTERMMP" ]; then + VIASH_PAR_CLIP3PADAPTERMMP=$(ViashRemoveFlags "$1") + else + VIASH_PAR_CLIP3PADAPTERMMP="$VIASH_PAR_CLIP3PADAPTERMMP;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --clip3pAfterAdapterNbases) + if [ -z "$VIASH_PAR_CLIP3PAFTERADAPTERNBASES" ]; then + VIASH_PAR_CLIP3PAFTERADAPTERNBASES="$2" + else + VIASH_PAR_CLIP3PAFTERADAPTERNBASES="$VIASH_PAR_CLIP3PAFTERADAPTERNBASES;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --clip3pAfterAdapterNbases. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --clip3pAfterAdapterNbases=*) + if [ -z "$VIASH_PAR_CLIP3PAFTERADAPTERNBASES" ]; then + VIASH_PAR_CLIP3PAFTERADAPTERNBASES=$(ViashRemoveFlags "$1") + else + VIASH_PAR_CLIP3PAFTERADAPTERNBASES="$VIASH_PAR_CLIP3PAFTERADAPTERNBASES;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --clip5pNbases) + if [ -z "$VIASH_PAR_CLIP5PNBASES" ]; then + VIASH_PAR_CLIP5PNBASES="$2" + else + VIASH_PAR_CLIP5PNBASES="$VIASH_PAR_CLIP5PNBASES;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --clip5pNbases. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --clip5pNbases=*) + if [ -z "$VIASH_PAR_CLIP5PNBASES" ]; then + VIASH_PAR_CLIP5PNBASES=$(ViashRemoveFlags "$1") + else + VIASH_PAR_CLIP5PNBASES="$VIASH_PAR_CLIP5PNBASES;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --limitGenomeGenerateRAM) + [ -n "$VIASH_PAR_LIMITGENOMEGENERATERAM" ] && ViashError Bad arguments for option \'--limitGenomeGenerateRAM\': \'$VIASH_PAR_LIMITGENOMEGENERATERAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITGENOMEGENERATERAM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --limitGenomeGenerateRAM. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --limitGenomeGenerateRAM=*) + [ -n "$VIASH_PAR_LIMITGENOMEGENERATERAM" ] && ViashError Bad arguments for option \'--limitGenomeGenerateRAM=*\': \'$VIASH_PAR_LIMITGENOMEGENERATERAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITGENOMEGENERATERAM=$(ViashRemoveFlags "$1") + shift 1 + ;; + --limitIObufferSize) + if [ -z "$VIASH_PAR_LIMITIOBUFFERSIZE" ]; then + VIASH_PAR_LIMITIOBUFFERSIZE="$2" + else + VIASH_PAR_LIMITIOBUFFERSIZE="$VIASH_PAR_LIMITIOBUFFERSIZE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --limitIObufferSize. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --limitIObufferSize=*) + if [ -z "$VIASH_PAR_LIMITIOBUFFERSIZE" ]; then + VIASH_PAR_LIMITIOBUFFERSIZE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_LIMITIOBUFFERSIZE="$VIASH_PAR_LIMITIOBUFFERSIZE;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --limitOutSAMoneReadBytes) + [ -n "$VIASH_PAR_LIMITOUTSAMONEREADBYTES" ] && ViashError Bad arguments for option \'--limitOutSAMoneReadBytes\': \'$VIASH_PAR_LIMITOUTSAMONEREADBYTES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITOUTSAMONEREADBYTES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --limitOutSAMoneReadBytes. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --limitOutSAMoneReadBytes=*) + [ -n "$VIASH_PAR_LIMITOUTSAMONEREADBYTES" ] && ViashError Bad arguments for option \'--limitOutSAMoneReadBytes=*\': \'$VIASH_PAR_LIMITOUTSAMONEREADBYTES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITOUTSAMONEREADBYTES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --limitOutSJoneRead) + [ -n "$VIASH_PAR_LIMITOUTSJONEREAD" ] && ViashError Bad arguments for option \'--limitOutSJoneRead\': \'$VIASH_PAR_LIMITOUTSJONEREAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITOUTSJONEREAD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --limitOutSJoneRead. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --limitOutSJoneRead=*) + [ -n "$VIASH_PAR_LIMITOUTSJONEREAD" ] && ViashError Bad arguments for option \'--limitOutSJoneRead=*\': \'$VIASH_PAR_LIMITOUTSJONEREAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITOUTSJONEREAD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --limitOutSJcollapsed) + [ -n "$VIASH_PAR_LIMITOUTSJCOLLAPSED" ] && ViashError Bad arguments for option \'--limitOutSJcollapsed\': \'$VIASH_PAR_LIMITOUTSJCOLLAPSED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITOUTSJCOLLAPSED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --limitOutSJcollapsed. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --limitOutSJcollapsed=*) + [ -n "$VIASH_PAR_LIMITOUTSJCOLLAPSED" ] && ViashError Bad arguments for option \'--limitOutSJcollapsed=*\': \'$VIASH_PAR_LIMITOUTSJCOLLAPSED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITOUTSJCOLLAPSED=$(ViashRemoveFlags "$1") + shift 1 + ;; + --limitBAMsortRAM) + [ -n "$VIASH_PAR_LIMITBAMSORTRAM" ] && ViashError Bad arguments for option \'--limitBAMsortRAM\': \'$VIASH_PAR_LIMITBAMSORTRAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITBAMSORTRAM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --limitBAMsortRAM. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --limitBAMsortRAM=*) + [ -n "$VIASH_PAR_LIMITBAMSORTRAM" ] && ViashError Bad arguments for option \'--limitBAMsortRAM=*\': \'$VIASH_PAR_LIMITBAMSORTRAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITBAMSORTRAM=$(ViashRemoveFlags "$1") + shift 1 + ;; + --limitSjdbInsertNsj) + [ -n "$VIASH_PAR_LIMITSJDBINSERTNSJ" ] && ViashError Bad arguments for option \'--limitSjdbInsertNsj\': \'$VIASH_PAR_LIMITSJDBINSERTNSJ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITSJDBINSERTNSJ="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --limitSjdbInsertNsj. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --limitSjdbInsertNsj=*) + [ -n "$VIASH_PAR_LIMITSJDBINSERTNSJ" ] && ViashError Bad arguments for option \'--limitSjdbInsertNsj=*\': \'$VIASH_PAR_LIMITSJDBINSERTNSJ\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITSJDBINSERTNSJ=$(ViashRemoveFlags "$1") + shift 1 + ;; + --limitNreadsSoft) + [ -n "$VIASH_PAR_LIMITNREADSSOFT" ] && ViashError Bad arguments for option \'--limitNreadsSoft\': \'$VIASH_PAR_LIMITNREADSSOFT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITNREADSSOFT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --limitNreadsSoft. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --limitNreadsSoft=*) + [ -n "$VIASH_PAR_LIMITNREADSSOFT" ] && ViashError Bad arguments for option \'--limitNreadsSoft=*\': \'$VIASH_PAR_LIMITNREADSSOFT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITNREADSSOFT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outTmpKeep) + [ -n "$VIASH_PAR_OUTTMPKEEP" ] && ViashError Bad arguments for option \'--outTmpKeep\': \'$VIASH_PAR_OUTTMPKEEP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTTMPKEEP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outTmpKeep. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outTmpKeep=*) + [ -n "$VIASH_PAR_OUTTMPKEEP" ] && ViashError Bad arguments for option \'--outTmpKeep=*\': \'$VIASH_PAR_OUTTMPKEEP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTTMPKEEP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outStd) + [ -n "$VIASH_PAR_OUTSTD" ] && ViashError Bad arguments for option \'--outStd\': \'$VIASH_PAR_OUTSTD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSTD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outStd. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outStd=*) + [ -n "$VIASH_PAR_OUTSTD" ] && ViashError Bad arguments for option \'--outStd=*\': \'$VIASH_PAR_OUTSTD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSTD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outReadsUnmapped) + [ -n "$VIASH_PAR_OUTREADSUNMAPPED" ] && ViashError Bad arguments for option \'--outReadsUnmapped\': \'$VIASH_PAR_OUTREADSUNMAPPED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTREADSUNMAPPED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outReadsUnmapped. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outReadsUnmapped=*) + [ -n "$VIASH_PAR_OUTREADSUNMAPPED" ] && ViashError Bad arguments for option \'--outReadsUnmapped=*\': \'$VIASH_PAR_OUTREADSUNMAPPED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTREADSUNMAPPED=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outQSconversionAdd) + [ -n "$VIASH_PAR_OUTQSCONVERSIONADD" ] && ViashError Bad arguments for option \'--outQSconversionAdd\': \'$VIASH_PAR_OUTQSCONVERSIONADD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTQSCONVERSIONADD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outQSconversionAdd. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outQSconversionAdd=*) + [ -n "$VIASH_PAR_OUTQSCONVERSIONADD" ] && ViashError Bad arguments for option \'--outQSconversionAdd=*\': \'$VIASH_PAR_OUTQSCONVERSIONADD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTQSCONVERSIONADD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outMultimapperOrder) + [ -n "$VIASH_PAR_OUTMULTIMAPPERORDER" ] && ViashError Bad arguments for option \'--outMultimapperOrder\': \'$VIASH_PAR_OUTMULTIMAPPERORDER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTMULTIMAPPERORDER="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outMultimapperOrder. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outMultimapperOrder=*) + [ -n "$VIASH_PAR_OUTMULTIMAPPERORDER" ] && ViashError Bad arguments for option \'--outMultimapperOrder=*\': \'$VIASH_PAR_OUTMULTIMAPPERORDER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTMULTIMAPPERORDER=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMtype) + if [ -z "$VIASH_PAR_OUTSAMTYPE" ]; then + VIASH_PAR_OUTSAMTYPE="$2" + else + VIASH_PAR_OUTSAMTYPE="$VIASH_PAR_OUTSAMTYPE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMtype. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMtype=*) + if [ -z "$VIASH_PAR_OUTSAMTYPE" ]; then + VIASH_PAR_OUTSAMTYPE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSAMTYPE="$VIASH_PAR_OUTSAMTYPE;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outSAMmode) + [ -n "$VIASH_PAR_OUTSAMMODE" ] && ViashError Bad arguments for option \'--outSAMmode\': \'$VIASH_PAR_OUTSAMMODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMMODE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMmode. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMmode=*) + [ -n "$VIASH_PAR_OUTSAMMODE" ] && ViashError Bad arguments for option \'--outSAMmode=*\': \'$VIASH_PAR_OUTSAMMODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMMODE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMstrandField) + [ -n "$VIASH_PAR_OUTSAMSTRANDFIELD" ] && ViashError Bad arguments for option \'--outSAMstrandField\': \'$VIASH_PAR_OUTSAMSTRANDFIELD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMSTRANDFIELD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMstrandField. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMstrandField=*) + [ -n "$VIASH_PAR_OUTSAMSTRANDFIELD" ] && ViashError Bad arguments for option \'--outSAMstrandField=*\': \'$VIASH_PAR_OUTSAMSTRANDFIELD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMSTRANDFIELD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMattributes) + if [ -z "$VIASH_PAR_OUTSAMATTRIBUTES" ]; then + VIASH_PAR_OUTSAMATTRIBUTES="$2" + else + VIASH_PAR_OUTSAMATTRIBUTES="$VIASH_PAR_OUTSAMATTRIBUTES;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMattributes. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMattributes=*) + if [ -z "$VIASH_PAR_OUTSAMATTRIBUTES" ]; then + VIASH_PAR_OUTSAMATTRIBUTES=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSAMATTRIBUTES="$VIASH_PAR_OUTSAMATTRIBUTES;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outSAMattrIHstart) + [ -n "$VIASH_PAR_OUTSAMATTRIHSTART" ] && ViashError Bad arguments for option \'--outSAMattrIHstart\': \'$VIASH_PAR_OUTSAMATTRIHSTART\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMATTRIHSTART="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMattrIHstart. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMattrIHstart=*) + [ -n "$VIASH_PAR_OUTSAMATTRIHSTART" ] && ViashError Bad arguments for option \'--outSAMattrIHstart=*\': \'$VIASH_PAR_OUTSAMATTRIHSTART\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMATTRIHSTART=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMunmapped) + if [ -z "$VIASH_PAR_OUTSAMUNMAPPED" ]; then + VIASH_PAR_OUTSAMUNMAPPED="$2" + else + VIASH_PAR_OUTSAMUNMAPPED="$VIASH_PAR_OUTSAMUNMAPPED;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMunmapped. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMunmapped=*) + if [ -z "$VIASH_PAR_OUTSAMUNMAPPED" ]; then + VIASH_PAR_OUTSAMUNMAPPED=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSAMUNMAPPED="$VIASH_PAR_OUTSAMUNMAPPED;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outSAMorder) + [ -n "$VIASH_PAR_OUTSAMORDER" ] && ViashError Bad arguments for option \'--outSAMorder\': \'$VIASH_PAR_OUTSAMORDER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMORDER="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMorder. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMorder=*) + [ -n "$VIASH_PAR_OUTSAMORDER" ] && ViashError Bad arguments for option \'--outSAMorder=*\': \'$VIASH_PAR_OUTSAMORDER\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMORDER=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMprimaryFlag) + [ -n "$VIASH_PAR_OUTSAMPRIMARYFLAG" ] && ViashError Bad arguments for option \'--outSAMprimaryFlag\': \'$VIASH_PAR_OUTSAMPRIMARYFLAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMPRIMARYFLAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMprimaryFlag. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMprimaryFlag=*) + [ -n "$VIASH_PAR_OUTSAMPRIMARYFLAG" ] && ViashError Bad arguments for option \'--outSAMprimaryFlag=*\': \'$VIASH_PAR_OUTSAMPRIMARYFLAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMPRIMARYFLAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMreadID) + [ -n "$VIASH_PAR_OUTSAMREADID" ] && ViashError Bad arguments for option \'--outSAMreadID\': \'$VIASH_PAR_OUTSAMREADID\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMREADID="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMreadID. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMreadID=*) + [ -n "$VIASH_PAR_OUTSAMREADID" ] && ViashError Bad arguments for option \'--outSAMreadID=*\': \'$VIASH_PAR_OUTSAMREADID\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMREADID=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMmapqUnique) + [ -n "$VIASH_PAR_OUTSAMMAPQUNIQUE" ] && ViashError Bad arguments for option \'--outSAMmapqUnique\': \'$VIASH_PAR_OUTSAMMAPQUNIQUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMMAPQUNIQUE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMmapqUnique. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMmapqUnique=*) + [ -n "$VIASH_PAR_OUTSAMMAPQUNIQUE" ] && ViashError Bad arguments for option \'--outSAMmapqUnique=*\': \'$VIASH_PAR_OUTSAMMAPQUNIQUE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMMAPQUNIQUE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMflagOR) + [ -n "$VIASH_PAR_OUTSAMFLAGOR" ] && ViashError Bad arguments for option \'--outSAMflagOR\': \'$VIASH_PAR_OUTSAMFLAGOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMFLAGOR="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMflagOR. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMflagOR=*) + [ -n "$VIASH_PAR_OUTSAMFLAGOR" ] && ViashError Bad arguments for option \'--outSAMflagOR=*\': \'$VIASH_PAR_OUTSAMFLAGOR\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMFLAGOR=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMflagAND) + [ -n "$VIASH_PAR_OUTSAMFLAGAND" ] && ViashError Bad arguments for option \'--outSAMflagAND\': \'$VIASH_PAR_OUTSAMFLAGAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMFLAGAND="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMflagAND. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMflagAND=*) + [ -n "$VIASH_PAR_OUTSAMFLAGAND" ] && ViashError Bad arguments for option \'--outSAMflagAND=*\': \'$VIASH_PAR_OUTSAMFLAGAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMFLAGAND=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMattrRGline) + if [ -z "$VIASH_PAR_OUTSAMATTRRGLINE" ]; then + VIASH_PAR_OUTSAMATTRRGLINE="$2" + else + VIASH_PAR_OUTSAMATTRRGLINE="$VIASH_PAR_OUTSAMATTRRGLINE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMattrRGline. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMattrRGline=*) + if [ -z "$VIASH_PAR_OUTSAMATTRRGLINE" ]; then + VIASH_PAR_OUTSAMATTRRGLINE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSAMATTRRGLINE="$VIASH_PAR_OUTSAMATTRRGLINE;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outSAMheaderHD) + if [ -z "$VIASH_PAR_OUTSAMHEADERHD" ]; then + VIASH_PAR_OUTSAMHEADERHD="$2" + else + VIASH_PAR_OUTSAMHEADERHD="$VIASH_PAR_OUTSAMHEADERHD;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMheaderHD. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMheaderHD=*) + if [ -z "$VIASH_PAR_OUTSAMHEADERHD" ]; then + VIASH_PAR_OUTSAMHEADERHD=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSAMHEADERHD="$VIASH_PAR_OUTSAMHEADERHD;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outSAMheaderPG) + if [ -z "$VIASH_PAR_OUTSAMHEADERPG" ]; then + VIASH_PAR_OUTSAMHEADERPG="$2" + else + VIASH_PAR_OUTSAMHEADERPG="$VIASH_PAR_OUTSAMHEADERPG;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMheaderPG. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMheaderPG=*) + if [ -z "$VIASH_PAR_OUTSAMHEADERPG" ]; then + VIASH_PAR_OUTSAMHEADERPG=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSAMHEADERPG="$VIASH_PAR_OUTSAMHEADERPG;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outSAMheaderCommentFile) + [ -n "$VIASH_PAR_OUTSAMHEADERCOMMENTFILE" ] && ViashError Bad arguments for option \'--outSAMheaderCommentFile\': \'$VIASH_PAR_OUTSAMHEADERCOMMENTFILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMHEADERCOMMENTFILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMheaderCommentFile. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMheaderCommentFile=*) + [ -n "$VIASH_PAR_OUTSAMHEADERCOMMENTFILE" ] && ViashError Bad arguments for option \'--outSAMheaderCommentFile=*\': \'$VIASH_PAR_OUTSAMHEADERCOMMENTFILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMHEADERCOMMENTFILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMfilter) + if [ -z "$VIASH_PAR_OUTSAMFILTER" ]; then + VIASH_PAR_OUTSAMFILTER="$2" + else + VIASH_PAR_OUTSAMFILTER="$VIASH_PAR_OUTSAMFILTER;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMfilter. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMfilter=*) + if [ -z "$VIASH_PAR_OUTSAMFILTER" ]; then + VIASH_PAR_OUTSAMFILTER=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSAMFILTER="$VIASH_PAR_OUTSAMFILTER;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outSAMmultNmax) + [ -n "$VIASH_PAR_OUTSAMMULTNMAX" ] && ViashError Bad arguments for option \'--outSAMmultNmax\': \'$VIASH_PAR_OUTSAMMULTNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMMULTNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMmultNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMmultNmax=*) + [ -n "$VIASH_PAR_OUTSAMMULTNMAX" ] && ViashError Bad arguments for option \'--outSAMmultNmax=*\': \'$VIASH_PAR_OUTSAMMULTNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMMULTNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSAMtlen) + [ -n "$VIASH_PAR_OUTSAMTLEN" ] && ViashError Bad arguments for option \'--outSAMtlen\': \'$VIASH_PAR_OUTSAMTLEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMTLEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSAMtlen. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSAMtlen=*) + [ -n "$VIASH_PAR_OUTSAMTLEN" ] && ViashError Bad arguments for option \'--outSAMtlen=*\': \'$VIASH_PAR_OUTSAMTLEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSAMTLEN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outBAMcompression) + [ -n "$VIASH_PAR_OUTBAMCOMPRESSION" ] && ViashError Bad arguments for option \'--outBAMcompression\': \'$VIASH_PAR_OUTBAMCOMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTBAMCOMPRESSION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outBAMcompression. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outBAMcompression=*) + [ -n "$VIASH_PAR_OUTBAMCOMPRESSION" ] && ViashError Bad arguments for option \'--outBAMcompression=*\': \'$VIASH_PAR_OUTBAMCOMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTBAMCOMPRESSION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outBAMsortingThreadN) + [ -n "$VIASH_PAR_OUTBAMSORTINGTHREADN" ] && ViashError Bad arguments for option \'--outBAMsortingThreadN\': \'$VIASH_PAR_OUTBAMSORTINGTHREADN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTBAMSORTINGTHREADN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outBAMsortingThreadN. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outBAMsortingThreadN=*) + [ -n "$VIASH_PAR_OUTBAMSORTINGTHREADN" ] && ViashError Bad arguments for option \'--outBAMsortingThreadN=*\': \'$VIASH_PAR_OUTBAMSORTINGTHREADN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTBAMSORTINGTHREADN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outBAMsortingBinsN) + [ -n "$VIASH_PAR_OUTBAMSORTINGBINSN" ] && ViashError Bad arguments for option \'--outBAMsortingBinsN\': \'$VIASH_PAR_OUTBAMSORTINGBINSN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTBAMSORTINGBINSN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outBAMsortingBinsN. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outBAMsortingBinsN=*) + [ -n "$VIASH_PAR_OUTBAMSORTINGBINSN" ] && ViashError Bad arguments for option \'--outBAMsortingBinsN=*\': \'$VIASH_PAR_OUTBAMSORTINGBINSN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTBAMSORTINGBINSN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bamRemoveDuplicatesType) + [ -n "$VIASH_PAR_BAMREMOVEDUPLICATESTYPE" ] && ViashError Bad arguments for option \'--bamRemoveDuplicatesType\': \'$VIASH_PAR_BAMREMOVEDUPLICATESTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAMREMOVEDUPLICATESTYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bamRemoveDuplicatesType. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bamRemoveDuplicatesType=*) + [ -n "$VIASH_PAR_BAMREMOVEDUPLICATESTYPE" ] && ViashError Bad arguments for option \'--bamRemoveDuplicatesType=*\': \'$VIASH_PAR_BAMREMOVEDUPLICATESTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAMREMOVEDUPLICATESTYPE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --bamRemoveDuplicatesMate2basesN) + [ -n "$VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN" ] && ViashError Bad arguments for option \'--bamRemoveDuplicatesMate2basesN\': \'$VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --bamRemoveDuplicatesMate2basesN. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --bamRemoveDuplicatesMate2basesN=*) + [ -n "$VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN" ] && ViashError Bad arguments for option \'--bamRemoveDuplicatesMate2basesN=*\': \'$VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outWigType) + if [ -z "$VIASH_PAR_OUTWIGTYPE" ]; then + VIASH_PAR_OUTWIGTYPE="$2" + else + VIASH_PAR_OUTWIGTYPE="$VIASH_PAR_OUTWIGTYPE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outWigType. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outWigType=*) + if [ -z "$VIASH_PAR_OUTWIGTYPE" ]; then + VIASH_PAR_OUTWIGTYPE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTWIGTYPE="$VIASH_PAR_OUTWIGTYPE;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outWigStrand) + [ -n "$VIASH_PAR_OUTWIGSTRAND" ] && ViashError Bad arguments for option \'--outWigStrand\': \'$VIASH_PAR_OUTWIGSTRAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTWIGSTRAND="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outWigStrand. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outWigStrand=*) + [ -n "$VIASH_PAR_OUTWIGSTRAND" ] && ViashError Bad arguments for option \'--outWigStrand=*\': \'$VIASH_PAR_OUTWIGSTRAND\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTWIGSTRAND=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outWigReferencesPrefix) + [ -n "$VIASH_PAR_OUTWIGREFERENCESPREFIX" ] && ViashError Bad arguments for option \'--outWigReferencesPrefix\': \'$VIASH_PAR_OUTWIGREFERENCESPREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTWIGREFERENCESPREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outWigReferencesPrefix. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outWigReferencesPrefix=*) + [ -n "$VIASH_PAR_OUTWIGREFERENCESPREFIX" ] && ViashError Bad arguments for option \'--outWigReferencesPrefix=*\': \'$VIASH_PAR_OUTWIGREFERENCESPREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTWIGREFERENCESPREFIX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outWigNorm) + [ -n "$VIASH_PAR_OUTWIGNORM" ] && ViashError Bad arguments for option \'--outWigNorm\': \'$VIASH_PAR_OUTWIGNORM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTWIGNORM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outWigNorm. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outWigNorm=*) + [ -n "$VIASH_PAR_OUTWIGNORM" ] && ViashError Bad arguments for option \'--outWigNorm=*\': \'$VIASH_PAR_OUTWIGNORM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTWIGNORM=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterType) + [ -n "$VIASH_PAR_OUTFILTERTYPE" ] && ViashError Bad arguments for option \'--outFilterType\': \'$VIASH_PAR_OUTFILTERTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERTYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterType. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterType=*) + [ -n "$VIASH_PAR_OUTFILTERTYPE" ] && ViashError Bad arguments for option \'--outFilterType=*\': \'$VIASH_PAR_OUTFILTERTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERTYPE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterMultimapScoreRange) + [ -n "$VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE" ] && ViashError Bad arguments for option \'--outFilterMultimapScoreRange\': \'$VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterMultimapScoreRange. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterMultimapScoreRange=*) + [ -n "$VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE" ] && ViashError Bad arguments for option \'--outFilterMultimapScoreRange=*\': \'$VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterMultimapNmax) + [ -n "$VIASH_PAR_OUTFILTERMULTIMAPNMAX" ] && ViashError Bad arguments for option \'--outFilterMultimapNmax\': \'$VIASH_PAR_OUTFILTERMULTIMAPNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMULTIMAPNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterMultimapNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterMultimapNmax=*) + [ -n "$VIASH_PAR_OUTFILTERMULTIMAPNMAX" ] && ViashError Bad arguments for option \'--outFilterMultimapNmax=*\': \'$VIASH_PAR_OUTFILTERMULTIMAPNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMULTIMAPNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterMismatchNmax) + [ -n "$VIASH_PAR_OUTFILTERMISMATCHNMAX" ] && ViashError Bad arguments for option \'--outFilterMismatchNmax\': \'$VIASH_PAR_OUTFILTERMISMATCHNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMISMATCHNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterMismatchNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterMismatchNmax=*) + [ -n "$VIASH_PAR_OUTFILTERMISMATCHNMAX" ] && ViashError Bad arguments for option \'--outFilterMismatchNmax=*\': \'$VIASH_PAR_OUTFILTERMISMATCHNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMISMATCHNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterMismatchNoverLmax) + [ -n "$VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX" ] && ViashError Bad arguments for option \'--outFilterMismatchNoverLmax\': \'$VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterMismatchNoverLmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterMismatchNoverLmax=*) + [ -n "$VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX" ] && ViashError Bad arguments for option \'--outFilterMismatchNoverLmax=*\': \'$VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterMismatchNoverReadLmax) + [ -n "$VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX" ] && ViashError Bad arguments for option \'--outFilterMismatchNoverReadLmax\': \'$VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterMismatchNoverReadLmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterMismatchNoverReadLmax=*) + [ -n "$VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX" ] && ViashError Bad arguments for option \'--outFilterMismatchNoverReadLmax=*\': \'$VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterScoreMin) + [ -n "$VIASH_PAR_OUTFILTERSCOREMIN" ] && ViashError Bad arguments for option \'--outFilterScoreMin\': \'$VIASH_PAR_OUTFILTERSCOREMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERSCOREMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterScoreMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterScoreMin=*) + [ -n "$VIASH_PAR_OUTFILTERSCOREMIN" ] && ViashError Bad arguments for option \'--outFilterScoreMin=*\': \'$VIASH_PAR_OUTFILTERSCOREMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERSCOREMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterScoreMinOverLread) + [ -n "$VIASH_PAR_OUTFILTERSCOREMINOVERLREAD" ] && ViashError Bad arguments for option \'--outFilterScoreMinOverLread\': \'$VIASH_PAR_OUTFILTERSCOREMINOVERLREAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERSCOREMINOVERLREAD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterScoreMinOverLread. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterScoreMinOverLread=*) + [ -n "$VIASH_PAR_OUTFILTERSCOREMINOVERLREAD" ] && ViashError Bad arguments for option \'--outFilterScoreMinOverLread=*\': \'$VIASH_PAR_OUTFILTERSCOREMINOVERLREAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERSCOREMINOVERLREAD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterMatchNmin) + [ -n "$VIASH_PAR_OUTFILTERMATCHNMIN" ] && ViashError Bad arguments for option \'--outFilterMatchNmin\': \'$VIASH_PAR_OUTFILTERMATCHNMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMATCHNMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterMatchNmin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterMatchNmin=*) + [ -n "$VIASH_PAR_OUTFILTERMATCHNMIN" ] && ViashError Bad arguments for option \'--outFilterMatchNmin=*\': \'$VIASH_PAR_OUTFILTERMATCHNMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMATCHNMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterMatchNminOverLread) + [ -n "$VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD" ] && ViashError Bad arguments for option \'--outFilterMatchNminOverLread\': \'$VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterMatchNminOverLread. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterMatchNminOverLread=*) + [ -n "$VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD" ] && ViashError Bad arguments for option \'--outFilterMatchNminOverLread=*\': \'$VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterIntronMotifs) + [ -n "$VIASH_PAR_OUTFILTERINTRONMOTIFS" ] && ViashError Bad arguments for option \'--outFilterIntronMotifs\': \'$VIASH_PAR_OUTFILTERINTRONMOTIFS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERINTRONMOTIFS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterIntronMotifs. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterIntronMotifs=*) + [ -n "$VIASH_PAR_OUTFILTERINTRONMOTIFS" ] && ViashError Bad arguments for option \'--outFilterIntronMotifs=*\': \'$VIASH_PAR_OUTFILTERINTRONMOTIFS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERINTRONMOTIFS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outFilterIntronStrands) + [ -n "$VIASH_PAR_OUTFILTERINTRONSTRANDS" ] && ViashError Bad arguments for option \'--outFilterIntronStrands\': \'$VIASH_PAR_OUTFILTERINTRONSTRANDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERINTRONSTRANDS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outFilterIntronStrands. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outFilterIntronStrands=*) + [ -n "$VIASH_PAR_OUTFILTERINTRONSTRANDS" ] && ViashError Bad arguments for option \'--outFilterIntronStrands=*\': \'$VIASH_PAR_OUTFILTERINTRONSTRANDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTFILTERINTRONSTRANDS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSJtype) + [ -n "$VIASH_PAR_OUTSJTYPE" ] && ViashError Bad arguments for option \'--outSJtype\': \'$VIASH_PAR_OUTSJTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSJTYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSJtype. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSJtype=*) + [ -n "$VIASH_PAR_OUTSJTYPE" ] && ViashError Bad arguments for option \'--outSJtype=*\': \'$VIASH_PAR_OUTSJTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSJTYPE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSJfilterReads) + [ -n "$VIASH_PAR_OUTSJFILTERREADS" ] && ViashError Bad arguments for option \'--outSJfilterReads\': \'$VIASH_PAR_OUTSJFILTERREADS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSJFILTERREADS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSJfilterReads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSJfilterReads=*) + [ -n "$VIASH_PAR_OUTSJFILTERREADS" ] && ViashError Bad arguments for option \'--outSJfilterReads=*\': \'$VIASH_PAR_OUTSJFILTERREADS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_OUTSJFILTERREADS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --outSJfilterOverhangMin) + if [ -z "$VIASH_PAR_OUTSJFILTEROVERHANGMIN" ]; then + VIASH_PAR_OUTSJFILTEROVERHANGMIN="$2" + else + VIASH_PAR_OUTSJFILTEROVERHANGMIN="$VIASH_PAR_OUTSJFILTEROVERHANGMIN;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSJfilterOverhangMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSJfilterOverhangMin=*) + if [ -z "$VIASH_PAR_OUTSJFILTEROVERHANGMIN" ]; then + VIASH_PAR_OUTSJFILTEROVERHANGMIN=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSJFILTEROVERHANGMIN="$VIASH_PAR_OUTSJFILTEROVERHANGMIN;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outSJfilterCountUniqueMin) + if [ -z "$VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN" ]; then + VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN="$2" + else + VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN="$VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSJfilterCountUniqueMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSJfilterCountUniqueMin=*) + if [ -z "$VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN" ]; then + VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN="$VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outSJfilterCountTotalMin) + if [ -z "$VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN" ]; then + VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN="$2" + else + VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN="$VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSJfilterCountTotalMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSJfilterCountTotalMin=*) + if [ -z "$VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN" ]; then + VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN="$VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outSJfilterDistToOtherSJmin) + if [ -z "$VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN" ]; then + VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN="$2" + else + VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN="$VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSJfilterDistToOtherSJmin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSJfilterDistToOtherSJmin=*) + if [ -z "$VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN" ]; then + VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN="$VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --outSJfilterIntronMaxVsReadN) + if [ -z "$VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN" ]; then + VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN="$2" + else + VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN="$VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --outSJfilterIntronMaxVsReadN. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --outSJfilterIntronMaxVsReadN=*) + if [ -z "$VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN" ]; then + VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN=$(ViashRemoveFlags "$1") + else + VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN="$VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --scoreGap) + [ -n "$VIASH_PAR_SCOREGAP" ] && ViashError Bad arguments for option \'--scoreGap\': \'$VIASH_PAR_SCOREGAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREGAP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --scoreGap. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --scoreGap=*) + [ -n "$VIASH_PAR_SCOREGAP" ] && ViashError Bad arguments for option \'--scoreGap=*\': \'$VIASH_PAR_SCOREGAP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREGAP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --scoreGapNoncan) + [ -n "$VIASH_PAR_SCOREGAPNONCAN" ] && ViashError Bad arguments for option \'--scoreGapNoncan\': \'$VIASH_PAR_SCOREGAPNONCAN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREGAPNONCAN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --scoreGapNoncan. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --scoreGapNoncan=*) + [ -n "$VIASH_PAR_SCOREGAPNONCAN" ] && ViashError Bad arguments for option \'--scoreGapNoncan=*\': \'$VIASH_PAR_SCOREGAPNONCAN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREGAPNONCAN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --scoreGapGCAG) + [ -n "$VIASH_PAR_SCOREGAPGCAG" ] && ViashError Bad arguments for option \'--scoreGapGCAG\': \'$VIASH_PAR_SCOREGAPGCAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREGAPGCAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --scoreGapGCAG. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --scoreGapGCAG=*) + [ -n "$VIASH_PAR_SCOREGAPGCAG" ] && ViashError Bad arguments for option \'--scoreGapGCAG=*\': \'$VIASH_PAR_SCOREGAPGCAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREGAPGCAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + --scoreGapATAC) + [ -n "$VIASH_PAR_SCOREGAPATAC" ] && ViashError Bad arguments for option \'--scoreGapATAC\': \'$VIASH_PAR_SCOREGAPATAC\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREGAPATAC="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --scoreGapATAC. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --scoreGapATAC=*) + [ -n "$VIASH_PAR_SCOREGAPATAC" ] && ViashError Bad arguments for option \'--scoreGapATAC=*\': \'$VIASH_PAR_SCOREGAPATAC\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREGAPATAC=$(ViashRemoveFlags "$1") + shift 1 + ;; + --scoreGenomicLengthLog2scale) + [ -n "$VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE" ] && ViashError Bad arguments for option \'--scoreGenomicLengthLog2scale\': \'$VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --scoreGenomicLengthLog2scale. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --scoreGenomicLengthLog2scale=*) + [ -n "$VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE" ] && ViashError Bad arguments for option \'--scoreGenomicLengthLog2scale=*\': \'$VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --scoreDelOpen) + [ -n "$VIASH_PAR_SCOREDELOPEN" ] && ViashError Bad arguments for option \'--scoreDelOpen\': \'$VIASH_PAR_SCOREDELOPEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREDELOPEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --scoreDelOpen. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --scoreDelOpen=*) + [ -n "$VIASH_PAR_SCOREDELOPEN" ] && ViashError Bad arguments for option \'--scoreDelOpen=*\': \'$VIASH_PAR_SCOREDELOPEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREDELOPEN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --scoreDelBase) + [ -n "$VIASH_PAR_SCOREDELBASE" ] && ViashError Bad arguments for option \'--scoreDelBase\': \'$VIASH_PAR_SCOREDELBASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREDELBASE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --scoreDelBase. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --scoreDelBase=*) + [ -n "$VIASH_PAR_SCOREDELBASE" ] && ViashError Bad arguments for option \'--scoreDelBase=*\': \'$VIASH_PAR_SCOREDELBASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREDELBASE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --scoreInsOpen) + [ -n "$VIASH_PAR_SCOREINSOPEN" ] && ViashError Bad arguments for option \'--scoreInsOpen\': \'$VIASH_PAR_SCOREINSOPEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREINSOPEN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --scoreInsOpen. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --scoreInsOpen=*) + [ -n "$VIASH_PAR_SCOREINSOPEN" ] && ViashError Bad arguments for option \'--scoreInsOpen=*\': \'$VIASH_PAR_SCOREINSOPEN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREINSOPEN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --scoreInsBase) + [ -n "$VIASH_PAR_SCOREINSBASE" ] && ViashError Bad arguments for option \'--scoreInsBase\': \'$VIASH_PAR_SCOREINSBASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREINSBASE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --scoreInsBase. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --scoreInsBase=*) + [ -n "$VIASH_PAR_SCOREINSBASE" ] && ViashError Bad arguments for option \'--scoreInsBase=*\': \'$VIASH_PAR_SCOREINSBASE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCOREINSBASE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --scoreStitchSJshift) + [ -n "$VIASH_PAR_SCORESTITCHSJSHIFT" ] && ViashError Bad arguments for option \'--scoreStitchSJshift\': \'$VIASH_PAR_SCORESTITCHSJSHIFT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCORESTITCHSJSHIFT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --scoreStitchSJshift. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --scoreStitchSJshift=*) + [ -n "$VIASH_PAR_SCORESTITCHSJSHIFT" ] && ViashError Bad arguments for option \'--scoreStitchSJshift=*\': \'$VIASH_PAR_SCORESTITCHSJSHIFT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SCORESTITCHSJSHIFT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --seedSearchStartLmax) + [ -n "$VIASH_PAR_SEEDSEARCHSTARTLMAX" ] && ViashError Bad arguments for option \'--seedSearchStartLmax\': \'$VIASH_PAR_SEEDSEARCHSTARTLMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDSEARCHSTARTLMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --seedSearchStartLmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --seedSearchStartLmax=*) + [ -n "$VIASH_PAR_SEEDSEARCHSTARTLMAX" ] && ViashError Bad arguments for option \'--seedSearchStartLmax=*\': \'$VIASH_PAR_SEEDSEARCHSTARTLMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDSEARCHSTARTLMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --seedSearchStartLmaxOverLread) + [ -n "$VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD" ] && ViashError Bad arguments for option \'--seedSearchStartLmaxOverLread\': \'$VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --seedSearchStartLmaxOverLread. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --seedSearchStartLmaxOverLread=*) + [ -n "$VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD" ] && ViashError Bad arguments for option \'--seedSearchStartLmaxOverLread=*\': \'$VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD=$(ViashRemoveFlags "$1") + shift 1 + ;; + --seedSearchLmax) + [ -n "$VIASH_PAR_SEEDSEARCHLMAX" ] && ViashError Bad arguments for option \'--seedSearchLmax\': \'$VIASH_PAR_SEEDSEARCHLMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDSEARCHLMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --seedSearchLmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --seedSearchLmax=*) + [ -n "$VIASH_PAR_SEEDSEARCHLMAX" ] && ViashError Bad arguments for option \'--seedSearchLmax=*\': \'$VIASH_PAR_SEEDSEARCHLMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDSEARCHLMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --seedMultimapNmax) + [ -n "$VIASH_PAR_SEEDMULTIMAPNMAX" ] && ViashError Bad arguments for option \'--seedMultimapNmax\': \'$VIASH_PAR_SEEDMULTIMAPNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDMULTIMAPNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --seedMultimapNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --seedMultimapNmax=*) + [ -n "$VIASH_PAR_SEEDMULTIMAPNMAX" ] && ViashError Bad arguments for option \'--seedMultimapNmax=*\': \'$VIASH_PAR_SEEDMULTIMAPNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDMULTIMAPNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --seedPerReadNmax) + [ -n "$VIASH_PAR_SEEDPERREADNMAX" ] && ViashError Bad arguments for option \'--seedPerReadNmax\': \'$VIASH_PAR_SEEDPERREADNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDPERREADNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --seedPerReadNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --seedPerReadNmax=*) + [ -n "$VIASH_PAR_SEEDPERREADNMAX" ] && ViashError Bad arguments for option \'--seedPerReadNmax=*\': \'$VIASH_PAR_SEEDPERREADNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDPERREADNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --seedPerWindowNmax) + [ -n "$VIASH_PAR_SEEDPERWINDOWNMAX" ] && ViashError Bad arguments for option \'--seedPerWindowNmax\': \'$VIASH_PAR_SEEDPERWINDOWNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDPERWINDOWNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --seedPerWindowNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --seedPerWindowNmax=*) + [ -n "$VIASH_PAR_SEEDPERWINDOWNMAX" ] && ViashError Bad arguments for option \'--seedPerWindowNmax=*\': \'$VIASH_PAR_SEEDPERWINDOWNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDPERWINDOWNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --seedNoneLociPerWindow) + [ -n "$VIASH_PAR_SEEDNONELOCIPERWINDOW" ] && ViashError Bad arguments for option \'--seedNoneLociPerWindow\': \'$VIASH_PAR_SEEDNONELOCIPERWINDOW\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDNONELOCIPERWINDOW="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --seedNoneLociPerWindow. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --seedNoneLociPerWindow=*) + [ -n "$VIASH_PAR_SEEDNONELOCIPERWINDOW" ] && ViashError Bad arguments for option \'--seedNoneLociPerWindow=*\': \'$VIASH_PAR_SEEDNONELOCIPERWINDOW\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDNONELOCIPERWINDOW=$(ViashRemoveFlags "$1") + shift 1 + ;; + --seedSplitMin) + [ -n "$VIASH_PAR_SEEDSPLITMIN" ] && ViashError Bad arguments for option \'--seedSplitMin\': \'$VIASH_PAR_SEEDSPLITMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDSPLITMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --seedSplitMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --seedSplitMin=*) + [ -n "$VIASH_PAR_SEEDSPLITMIN" ] && ViashError Bad arguments for option \'--seedSplitMin=*\': \'$VIASH_PAR_SEEDSPLITMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDSPLITMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --seedMapMin) + [ -n "$VIASH_PAR_SEEDMAPMIN" ] && ViashError Bad arguments for option \'--seedMapMin\': \'$VIASH_PAR_SEEDMAPMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDMAPMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --seedMapMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --seedMapMin=*) + [ -n "$VIASH_PAR_SEEDMAPMIN" ] && ViashError Bad arguments for option \'--seedMapMin=*\': \'$VIASH_PAR_SEEDMAPMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SEEDMAPMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignIntronMin) + [ -n "$VIASH_PAR_ALIGNINTRONMIN" ] && ViashError Bad arguments for option \'--alignIntronMin\': \'$VIASH_PAR_ALIGNINTRONMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNINTRONMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignIntronMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignIntronMin=*) + [ -n "$VIASH_PAR_ALIGNINTRONMIN" ] && ViashError Bad arguments for option \'--alignIntronMin=*\': \'$VIASH_PAR_ALIGNINTRONMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNINTRONMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignIntronMax) + [ -n "$VIASH_PAR_ALIGNINTRONMAX" ] && ViashError Bad arguments for option \'--alignIntronMax\': \'$VIASH_PAR_ALIGNINTRONMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNINTRONMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignIntronMax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignIntronMax=*) + [ -n "$VIASH_PAR_ALIGNINTRONMAX" ] && ViashError Bad arguments for option \'--alignIntronMax=*\': \'$VIASH_PAR_ALIGNINTRONMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNINTRONMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignMatesGapMax) + [ -n "$VIASH_PAR_ALIGNMATESGAPMAX" ] && ViashError Bad arguments for option \'--alignMatesGapMax\': \'$VIASH_PAR_ALIGNMATESGAPMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNMATESGAPMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignMatesGapMax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignMatesGapMax=*) + [ -n "$VIASH_PAR_ALIGNMATESGAPMAX" ] && ViashError Bad arguments for option \'--alignMatesGapMax=*\': \'$VIASH_PAR_ALIGNMATESGAPMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNMATESGAPMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignSJoverhangMin) + [ -n "$VIASH_PAR_ALIGNSJOVERHANGMIN" ] && ViashError Bad arguments for option \'--alignSJoverhangMin\': \'$VIASH_PAR_ALIGNSJOVERHANGMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNSJOVERHANGMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignSJoverhangMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignSJoverhangMin=*) + [ -n "$VIASH_PAR_ALIGNSJOVERHANGMIN" ] && ViashError Bad arguments for option \'--alignSJoverhangMin=*\': \'$VIASH_PAR_ALIGNSJOVERHANGMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNSJOVERHANGMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignSJstitchMismatchNmax) + if [ -z "$VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX" ]; then + VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX="$2" + else + VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX="$VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignSJstitchMismatchNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignSJstitchMismatchNmax=*) + if [ -z "$VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX" ]; then + VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX=$(ViashRemoveFlags "$1") + else + VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX="$VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --alignSJDBoverhangMin) + [ -n "$VIASH_PAR_ALIGNSJDBOVERHANGMIN" ] && ViashError Bad arguments for option \'--alignSJDBoverhangMin\': \'$VIASH_PAR_ALIGNSJDBOVERHANGMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNSJDBOVERHANGMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignSJDBoverhangMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignSJDBoverhangMin=*) + [ -n "$VIASH_PAR_ALIGNSJDBOVERHANGMIN" ] && ViashError Bad arguments for option \'--alignSJDBoverhangMin=*\': \'$VIASH_PAR_ALIGNSJDBOVERHANGMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNSJDBOVERHANGMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignSplicedMateMapLmin) + [ -n "$VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN" ] && ViashError Bad arguments for option \'--alignSplicedMateMapLmin\': \'$VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignSplicedMateMapLmin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignSplicedMateMapLmin=*) + [ -n "$VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN" ] && ViashError Bad arguments for option \'--alignSplicedMateMapLmin=*\': \'$VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignSplicedMateMapLminOverLmate) + [ -n "$VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE" ] && ViashError Bad arguments for option \'--alignSplicedMateMapLminOverLmate\': \'$VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignSplicedMateMapLminOverLmate. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignSplicedMateMapLminOverLmate=*) + [ -n "$VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE" ] && ViashError Bad arguments for option \'--alignSplicedMateMapLminOverLmate=*\': \'$VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignWindowsPerReadNmax) + [ -n "$VIASH_PAR_ALIGNWINDOWSPERREADNMAX" ] && ViashError Bad arguments for option \'--alignWindowsPerReadNmax\': \'$VIASH_PAR_ALIGNWINDOWSPERREADNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNWINDOWSPERREADNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignWindowsPerReadNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignWindowsPerReadNmax=*) + [ -n "$VIASH_PAR_ALIGNWINDOWSPERREADNMAX" ] && ViashError Bad arguments for option \'--alignWindowsPerReadNmax=*\': \'$VIASH_PAR_ALIGNWINDOWSPERREADNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNWINDOWSPERREADNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignTranscriptsPerWindowNmax) + [ -n "$VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX" ] && ViashError Bad arguments for option \'--alignTranscriptsPerWindowNmax\': \'$VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignTranscriptsPerWindowNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignTranscriptsPerWindowNmax=*) + [ -n "$VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX" ] && ViashError Bad arguments for option \'--alignTranscriptsPerWindowNmax=*\': \'$VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignTranscriptsPerReadNmax) + [ -n "$VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX" ] && ViashError Bad arguments for option \'--alignTranscriptsPerReadNmax\': \'$VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignTranscriptsPerReadNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignTranscriptsPerReadNmax=*) + [ -n "$VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX" ] && ViashError Bad arguments for option \'--alignTranscriptsPerReadNmax=*\': \'$VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignEndsType) + [ -n "$VIASH_PAR_ALIGNENDSTYPE" ] && ViashError Bad arguments for option \'--alignEndsType\': \'$VIASH_PAR_ALIGNENDSTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNENDSTYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignEndsType. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignEndsType=*) + [ -n "$VIASH_PAR_ALIGNENDSTYPE" ] && ViashError Bad arguments for option \'--alignEndsType=*\': \'$VIASH_PAR_ALIGNENDSTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNENDSTYPE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignEndsProtrude) + [ -n "$VIASH_PAR_ALIGNENDSPROTRUDE" ] && ViashError Bad arguments for option \'--alignEndsProtrude\': \'$VIASH_PAR_ALIGNENDSPROTRUDE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNENDSPROTRUDE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignEndsProtrude. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignEndsProtrude=*) + [ -n "$VIASH_PAR_ALIGNENDSPROTRUDE" ] && ViashError Bad arguments for option \'--alignEndsProtrude=*\': \'$VIASH_PAR_ALIGNENDSPROTRUDE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNENDSPROTRUDE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignSoftClipAtReferenceEnds) + [ -n "$VIASH_PAR_ALIGNSOFTCLIPATREFERENCEENDS" ] && ViashError Bad arguments for option \'--alignSoftClipAtReferenceEnds\': \'$VIASH_PAR_ALIGNSOFTCLIPATREFERENCEENDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNSOFTCLIPATREFERENCEENDS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignSoftClipAtReferenceEnds. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignSoftClipAtReferenceEnds=*) + [ -n "$VIASH_PAR_ALIGNSOFTCLIPATREFERENCEENDS" ] && ViashError Bad arguments for option \'--alignSoftClipAtReferenceEnds=*\': \'$VIASH_PAR_ALIGNSOFTCLIPATREFERENCEENDS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNSOFTCLIPATREFERENCEENDS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --alignInsertionFlush) + [ -n "$VIASH_PAR_ALIGNINSERTIONFLUSH" ] && ViashError Bad arguments for option \'--alignInsertionFlush\': \'$VIASH_PAR_ALIGNINSERTIONFLUSH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNINSERTIONFLUSH="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --alignInsertionFlush. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --alignInsertionFlush=*) + [ -n "$VIASH_PAR_ALIGNINSERTIONFLUSH" ] && ViashError Bad arguments for option \'--alignInsertionFlush=*\': \'$VIASH_PAR_ALIGNINSERTIONFLUSH\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNINSERTIONFLUSH=$(ViashRemoveFlags "$1") + shift 1 + ;; + --peOverlapNbasesMin) + [ -n "$VIASH_PAR_PEOVERLAPNBASESMIN" ] && ViashError Bad arguments for option \'--peOverlapNbasesMin\': \'$VIASH_PAR_PEOVERLAPNBASESMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PEOVERLAPNBASESMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --peOverlapNbasesMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --peOverlapNbasesMin=*) + [ -n "$VIASH_PAR_PEOVERLAPNBASESMIN" ] && ViashError Bad arguments for option \'--peOverlapNbasesMin=*\': \'$VIASH_PAR_PEOVERLAPNBASESMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PEOVERLAPNBASESMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --peOverlapMMp) + [ -n "$VIASH_PAR_PEOVERLAPMMP" ] && ViashError Bad arguments for option \'--peOverlapMMp\': \'$VIASH_PAR_PEOVERLAPMMP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PEOVERLAPMMP="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --peOverlapMMp. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --peOverlapMMp=*) + [ -n "$VIASH_PAR_PEOVERLAPMMP" ] && ViashError Bad arguments for option \'--peOverlapMMp=*\': \'$VIASH_PAR_PEOVERLAPMMP\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_PEOVERLAPMMP=$(ViashRemoveFlags "$1") + shift 1 + ;; + --winAnchorMultimapNmax) + [ -n "$VIASH_PAR_WINANCHORMULTIMAPNMAX" ] && ViashError Bad arguments for option \'--winAnchorMultimapNmax\': \'$VIASH_PAR_WINANCHORMULTIMAPNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINANCHORMULTIMAPNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --winAnchorMultimapNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --winAnchorMultimapNmax=*) + [ -n "$VIASH_PAR_WINANCHORMULTIMAPNMAX" ] && ViashError Bad arguments for option \'--winAnchorMultimapNmax=*\': \'$VIASH_PAR_WINANCHORMULTIMAPNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINANCHORMULTIMAPNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --winBinNbits) + [ -n "$VIASH_PAR_WINBINNBITS" ] && ViashError Bad arguments for option \'--winBinNbits\': \'$VIASH_PAR_WINBINNBITS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINBINNBITS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --winBinNbits. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --winBinNbits=*) + [ -n "$VIASH_PAR_WINBINNBITS" ] && ViashError Bad arguments for option \'--winBinNbits=*\': \'$VIASH_PAR_WINBINNBITS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINBINNBITS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --winAnchorDistNbins) + [ -n "$VIASH_PAR_WINANCHORDISTNBINS" ] && ViashError Bad arguments for option \'--winAnchorDistNbins\': \'$VIASH_PAR_WINANCHORDISTNBINS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINANCHORDISTNBINS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --winAnchorDistNbins. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --winAnchorDistNbins=*) + [ -n "$VIASH_PAR_WINANCHORDISTNBINS" ] && ViashError Bad arguments for option \'--winAnchorDistNbins=*\': \'$VIASH_PAR_WINANCHORDISTNBINS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINANCHORDISTNBINS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --winFlankNbins) + [ -n "$VIASH_PAR_WINFLANKNBINS" ] && ViashError Bad arguments for option \'--winFlankNbins\': \'$VIASH_PAR_WINFLANKNBINS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINFLANKNBINS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --winFlankNbins. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --winFlankNbins=*) + [ -n "$VIASH_PAR_WINFLANKNBINS" ] && ViashError Bad arguments for option \'--winFlankNbins=*\': \'$VIASH_PAR_WINFLANKNBINS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINFLANKNBINS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --winReadCoverageRelativeMin) + [ -n "$VIASH_PAR_WINREADCOVERAGERELATIVEMIN" ] && ViashError Bad arguments for option \'--winReadCoverageRelativeMin\': \'$VIASH_PAR_WINREADCOVERAGERELATIVEMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINREADCOVERAGERELATIVEMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --winReadCoverageRelativeMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --winReadCoverageRelativeMin=*) + [ -n "$VIASH_PAR_WINREADCOVERAGERELATIVEMIN" ] && ViashError Bad arguments for option \'--winReadCoverageRelativeMin=*\': \'$VIASH_PAR_WINREADCOVERAGERELATIVEMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINREADCOVERAGERELATIVEMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --winReadCoverageBasesMin) + [ -n "$VIASH_PAR_WINREADCOVERAGEBASESMIN" ] && ViashError Bad arguments for option \'--winReadCoverageBasesMin\': \'$VIASH_PAR_WINREADCOVERAGEBASESMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINREADCOVERAGEBASESMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --winReadCoverageBasesMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --winReadCoverageBasesMin=*) + [ -n "$VIASH_PAR_WINREADCOVERAGEBASESMIN" ] && ViashError Bad arguments for option \'--winReadCoverageBasesMin=*\': \'$VIASH_PAR_WINREADCOVERAGEBASESMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WINREADCOVERAGEBASESMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimOutType) + if [ -z "$VIASH_PAR_CHIMOUTTYPE" ]; then + VIASH_PAR_CHIMOUTTYPE="$2" + else + VIASH_PAR_CHIMOUTTYPE="$VIASH_PAR_CHIMOUTTYPE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimOutType. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimOutType=*) + if [ -z "$VIASH_PAR_CHIMOUTTYPE" ]; then + VIASH_PAR_CHIMOUTTYPE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_CHIMOUTTYPE="$VIASH_PAR_CHIMOUTTYPE;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --chimSegmentMin) + [ -n "$VIASH_PAR_CHIMSEGMENTMIN" ] && ViashError Bad arguments for option \'--chimSegmentMin\': \'$VIASH_PAR_CHIMSEGMENTMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSEGMENTMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimSegmentMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimSegmentMin=*) + [ -n "$VIASH_PAR_CHIMSEGMENTMIN" ] && ViashError Bad arguments for option \'--chimSegmentMin=*\': \'$VIASH_PAR_CHIMSEGMENTMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSEGMENTMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimScoreMin) + [ -n "$VIASH_PAR_CHIMSCOREMIN" ] && ViashError Bad arguments for option \'--chimScoreMin\': \'$VIASH_PAR_CHIMSCOREMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSCOREMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimScoreMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimScoreMin=*) + [ -n "$VIASH_PAR_CHIMSCOREMIN" ] && ViashError Bad arguments for option \'--chimScoreMin=*\': \'$VIASH_PAR_CHIMSCOREMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSCOREMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimScoreDropMax) + [ -n "$VIASH_PAR_CHIMSCOREDROPMAX" ] && ViashError Bad arguments for option \'--chimScoreDropMax\': \'$VIASH_PAR_CHIMSCOREDROPMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSCOREDROPMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimScoreDropMax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimScoreDropMax=*) + [ -n "$VIASH_PAR_CHIMSCOREDROPMAX" ] && ViashError Bad arguments for option \'--chimScoreDropMax=*\': \'$VIASH_PAR_CHIMSCOREDROPMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSCOREDROPMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimScoreSeparation) + [ -n "$VIASH_PAR_CHIMSCORESEPARATION" ] && ViashError Bad arguments for option \'--chimScoreSeparation\': \'$VIASH_PAR_CHIMSCORESEPARATION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSCORESEPARATION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimScoreSeparation. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimScoreSeparation=*) + [ -n "$VIASH_PAR_CHIMSCORESEPARATION" ] && ViashError Bad arguments for option \'--chimScoreSeparation=*\': \'$VIASH_PAR_CHIMSCORESEPARATION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSCORESEPARATION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimScoreJunctionNonGTAG) + [ -n "$VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG" ] && ViashError Bad arguments for option \'--chimScoreJunctionNonGTAG\': \'$VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimScoreJunctionNonGTAG. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimScoreJunctionNonGTAG=*) + [ -n "$VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG" ] && ViashError Bad arguments for option \'--chimScoreJunctionNonGTAG=*\': \'$VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimJunctionOverhangMin) + [ -n "$VIASH_PAR_CHIMJUNCTIONOVERHANGMIN" ] && ViashError Bad arguments for option \'--chimJunctionOverhangMin\': \'$VIASH_PAR_CHIMJUNCTIONOVERHANGMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMJUNCTIONOVERHANGMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimJunctionOverhangMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimJunctionOverhangMin=*) + [ -n "$VIASH_PAR_CHIMJUNCTIONOVERHANGMIN" ] && ViashError Bad arguments for option \'--chimJunctionOverhangMin=*\': \'$VIASH_PAR_CHIMJUNCTIONOVERHANGMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMJUNCTIONOVERHANGMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimSegmentReadGapMax) + [ -n "$VIASH_PAR_CHIMSEGMENTREADGAPMAX" ] && ViashError Bad arguments for option \'--chimSegmentReadGapMax\': \'$VIASH_PAR_CHIMSEGMENTREADGAPMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSEGMENTREADGAPMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimSegmentReadGapMax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimSegmentReadGapMax=*) + [ -n "$VIASH_PAR_CHIMSEGMENTREADGAPMAX" ] && ViashError Bad arguments for option \'--chimSegmentReadGapMax=*\': \'$VIASH_PAR_CHIMSEGMENTREADGAPMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMSEGMENTREADGAPMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimFilter) + if [ -z "$VIASH_PAR_CHIMFILTER" ]; then + VIASH_PAR_CHIMFILTER="$2" + else + VIASH_PAR_CHIMFILTER="$VIASH_PAR_CHIMFILTER;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimFilter. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimFilter=*) + if [ -z "$VIASH_PAR_CHIMFILTER" ]; then + VIASH_PAR_CHIMFILTER=$(ViashRemoveFlags "$1") + else + VIASH_PAR_CHIMFILTER="$VIASH_PAR_CHIMFILTER;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --chimMainSegmentMultNmax) + [ -n "$VIASH_PAR_CHIMMAINSEGMENTMULTNMAX" ] && ViashError Bad arguments for option \'--chimMainSegmentMultNmax\': \'$VIASH_PAR_CHIMMAINSEGMENTMULTNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMMAINSEGMENTMULTNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimMainSegmentMultNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimMainSegmentMultNmax=*) + [ -n "$VIASH_PAR_CHIMMAINSEGMENTMULTNMAX" ] && ViashError Bad arguments for option \'--chimMainSegmentMultNmax=*\': \'$VIASH_PAR_CHIMMAINSEGMENTMULTNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMMAINSEGMENTMULTNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimMultimapNmax) + [ -n "$VIASH_PAR_CHIMMULTIMAPNMAX" ] && ViashError Bad arguments for option \'--chimMultimapNmax\': \'$VIASH_PAR_CHIMMULTIMAPNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMMULTIMAPNMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimMultimapNmax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimMultimapNmax=*) + [ -n "$VIASH_PAR_CHIMMULTIMAPNMAX" ] && ViashError Bad arguments for option \'--chimMultimapNmax=*\': \'$VIASH_PAR_CHIMMULTIMAPNMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMMULTIMAPNMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimMultimapScoreRange) + [ -n "$VIASH_PAR_CHIMMULTIMAPSCORERANGE" ] && ViashError Bad arguments for option \'--chimMultimapScoreRange\': \'$VIASH_PAR_CHIMMULTIMAPSCORERANGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMMULTIMAPSCORERANGE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimMultimapScoreRange. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimMultimapScoreRange=*) + [ -n "$VIASH_PAR_CHIMMULTIMAPSCORERANGE" ] && ViashError Bad arguments for option \'--chimMultimapScoreRange=*\': \'$VIASH_PAR_CHIMMULTIMAPSCORERANGE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMMULTIMAPSCORERANGE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimNonchimScoreDropMin) + [ -n "$VIASH_PAR_CHIMNONCHIMSCOREDROPMIN" ] && ViashError Bad arguments for option \'--chimNonchimScoreDropMin\': \'$VIASH_PAR_CHIMNONCHIMSCOREDROPMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMNONCHIMSCOREDROPMIN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimNonchimScoreDropMin. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimNonchimScoreDropMin=*) + [ -n "$VIASH_PAR_CHIMNONCHIMSCOREDROPMIN" ] && ViashError Bad arguments for option \'--chimNonchimScoreDropMin=*\': \'$VIASH_PAR_CHIMNONCHIMSCOREDROPMIN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMNONCHIMSCOREDROPMIN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimOutJunctionFormat) + [ -n "$VIASH_PAR_CHIMOUTJUNCTIONFORMAT" ] && ViashError Bad arguments for option \'--chimOutJunctionFormat\': \'$VIASH_PAR_CHIMOUTJUNCTIONFORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMOUTJUNCTIONFORMAT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimOutJunctionFormat. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimOutJunctionFormat=*) + [ -n "$VIASH_PAR_CHIMOUTJUNCTIONFORMAT" ] && ViashError Bad arguments for option \'--chimOutJunctionFormat=*\': \'$VIASH_PAR_CHIMOUTJUNCTIONFORMAT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMOUTJUNCTIONFORMAT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --quantMode) + if [ -z "$VIASH_PAR_QUANTMODE" ]; then + VIASH_PAR_QUANTMODE="$2" + else + VIASH_PAR_QUANTMODE="$VIASH_PAR_QUANTMODE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --quantMode. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quantMode=*) + if [ -z "$VIASH_PAR_QUANTMODE" ]; then + VIASH_PAR_QUANTMODE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_QUANTMODE="$VIASH_PAR_QUANTMODE;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --quantTranscriptomeBAMcompression) + [ -n "$VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION" ] && ViashError Bad arguments for option \'--quantTranscriptomeBAMcompression\': \'$VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --quantTranscriptomeBAMcompression. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quantTranscriptomeBAMcompression=*) + [ -n "$VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION" ] && ViashError Bad arguments for option \'--quantTranscriptomeBAMcompression=*\': \'$VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION=$(ViashRemoveFlags "$1") + shift 1 + ;; + --quantTranscriptomeSAMoutput) + [ -n "$VIASH_PAR_QUANTTRANSCRIPTOMESAMOUTPUT" ] && ViashError Bad arguments for option \'--quantTranscriptomeSAMoutput\': \'$VIASH_PAR_QUANTTRANSCRIPTOMESAMOUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUANTTRANSCRIPTOMESAMOUTPUT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --quantTranscriptomeSAMoutput. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --quantTranscriptomeSAMoutput=*) + [ -n "$VIASH_PAR_QUANTTRANSCRIPTOMESAMOUTPUT" ] && ViashError Bad arguments for option \'--quantTranscriptomeSAMoutput=*\': \'$VIASH_PAR_QUANTTRANSCRIPTOMESAMOUTPUT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_QUANTTRANSCRIPTOMESAMOUTPUT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --twopassMode) + [ -n "$VIASH_PAR_TWOPASSMODE" ] && ViashError Bad arguments for option \'--twopassMode\': \'$VIASH_PAR_TWOPASSMODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TWOPASSMODE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --twopassMode. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --twopassMode=*) + [ -n "$VIASH_PAR_TWOPASSMODE" ] && ViashError Bad arguments for option \'--twopassMode=*\': \'$VIASH_PAR_TWOPASSMODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TWOPASSMODE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --twopass1readsN) + [ -n "$VIASH_PAR_TWOPASS1READSN" ] && ViashError Bad arguments for option \'--twopass1readsN\': \'$VIASH_PAR_TWOPASS1READSN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TWOPASS1READSN="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --twopass1readsN. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --twopass1readsN=*) + [ -n "$VIASH_PAR_TWOPASS1READSN" ] && ViashError Bad arguments for option \'--twopass1readsN=*\': \'$VIASH_PAR_TWOPASS1READSN\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_TWOPASS1READSN=$(ViashRemoveFlags "$1") + shift 1 + ;; + --waspOutputMode) + [ -n "$VIASH_PAR_WASPOUTPUTMODE" ] && ViashError Bad arguments for option \'--waspOutputMode\': \'$VIASH_PAR_WASPOUTPUTMODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WASPOUTPUTMODE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --waspOutputMode. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --waspOutputMode=*) + [ -n "$VIASH_PAR_WASPOUTPUTMODE" ] && ViashError Bad arguments for option \'--waspOutputMode=*\': \'$VIASH_PAR_WASPOUTPUTMODE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_WASPOUTPUTMODE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --input) + if [ -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT="$2" + else + VIASH_PAR_INPUT="$VIASH_PAR_INPUT;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input=*) + if [ -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT=$(ViashRemoveFlags "$1") + else + VIASH_PAR_INPUT="$VIASH_PAR_INPUT;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --readFilesIn) + if [ -z "$VIASH_PAR_INPUT" ]; then + VIASH_PAR_INPUT="$2" + else + VIASH_PAR_INPUT="$VIASH_PAR_INPUT;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --readFilesIn. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input_r2) + if [ -z "$VIASH_PAR_INPUT_R2" ]; then + VIASH_PAR_INPUT_R2="$2" + else + VIASH_PAR_INPUT_R2="$VIASH_PAR_INPUT_R2;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --input_r2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --input_r2=*) + if [ -z "$VIASH_PAR_INPUT_R2" ]; then + VIASH_PAR_INPUT_R2=$(ViashRemoveFlags "$1") + else + VIASH_PAR_INPUT_R2="$VIASH_PAR_INPUT_R2;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --aligned_reads) + [ -n "$VIASH_PAR_ALIGNED_READS" ] && ViashError Bad arguments for option \'--aligned_reads\': \'$VIASH_PAR_ALIGNED_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNED_READS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --aligned_reads. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --aligned_reads=*) + [ -n "$VIASH_PAR_ALIGNED_READS" ] && ViashError Bad arguments for option \'--aligned_reads=*\': \'$VIASH_PAR_ALIGNED_READS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_ALIGNED_READS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --reads_per_gene) + [ -n "$VIASH_PAR_READS_PER_GENE" ] && ViashError Bad arguments for option \'--reads_per_gene\': \'$VIASH_PAR_READS_PER_GENE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READS_PER_GENE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --reads_per_gene. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --reads_per_gene=*) + [ -n "$VIASH_PAR_READS_PER_GENE" ] && ViashError Bad arguments for option \'--reads_per_gene=*\': \'$VIASH_PAR_READS_PER_GENE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_READS_PER_GENE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --unmapped) + [ -n "$VIASH_PAR_UNMAPPED" ] && ViashError Bad arguments for option \'--unmapped\': \'$VIASH_PAR_UNMAPPED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNMAPPED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --unmapped. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unmapped=*) + [ -n "$VIASH_PAR_UNMAPPED" ] && ViashError Bad arguments for option \'--unmapped=*\': \'$VIASH_PAR_UNMAPPED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNMAPPED=$(ViashRemoveFlags "$1") + shift 1 + ;; + --unmapped_r2) + [ -n "$VIASH_PAR_UNMAPPED_R2" ] && ViashError Bad arguments for option \'--unmapped_r2\': \'$VIASH_PAR_UNMAPPED_R2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNMAPPED_R2="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --unmapped_r2. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --unmapped_r2=*) + [ -n "$VIASH_PAR_UNMAPPED_R2" ] && ViashError Bad arguments for option \'--unmapped_r2=*\': \'$VIASH_PAR_UNMAPPED_R2\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_UNMAPPED_R2=$(ViashRemoveFlags "$1") + shift 1 + ;; + --chimeric_junctions) + [ -n "$VIASH_PAR_CHIMERIC_JUNCTIONS" ] && ViashError Bad arguments for option \'--chimeric_junctions\': \'$VIASH_PAR_CHIMERIC_JUNCTIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMERIC_JUNCTIONS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --chimeric_junctions. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --chimeric_junctions=*) + [ -n "$VIASH_PAR_CHIMERIC_JUNCTIONS" ] && ViashError Bad arguments for option \'--chimeric_junctions=*\': \'$VIASH_PAR_CHIMERIC_JUNCTIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_CHIMERIC_JUNCTIONS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --log) + [ -n "$VIASH_PAR_LOG" ] && ViashError Bad arguments for option \'--log\': \'$VIASH_PAR_LOG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LOG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --log. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --log=*) + [ -n "$VIASH_PAR_LOG" ] && ViashError Bad arguments for option \'--log=*\': \'$VIASH_PAR_LOG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LOG=$(ViashRemoveFlags "$1") + shift 1 + ;; + --splice_junctions) + [ -n "$VIASH_PAR_SPLICE_JUNCTIONS" ] && ViashError Bad arguments for option \'--splice_junctions\': \'$VIASH_PAR_SPLICE_JUNCTIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLICE_JUNCTIONS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --splice_junctions. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --splice_junctions=*) + [ -n "$VIASH_PAR_SPLICE_JUNCTIONS" ] && ViashError Bad arguments for option \'--splice_junctions=*\': \'$VIASH_PAR_SPLICE_JUNCTIONS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SPLICE_JUNCTIONS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/star/star_align_reads:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_GENOMEDIR+x} ]; then + ViashError '--genomeDir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_INPUT+x} ]; then + ViashError '--input' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_ALIGNED_READS+x} ]; then + ViashError '--aligned_reads' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_GENOMEDIR" ] && [ ! -e "$VIASH_PAR_GENOMEDIR" ]; then + ViashError "Input file '$VIASH_PAR_GENOMEDIR' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_GENOMEFASTAFILES" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_GENOMEFASTAFILES; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi +if [ ! -z "$VIASH_PAR_SJDBGTFFILE" ] && [ ! -e "$VIASH_PAR_SJDBGTFFILE" ]; then + ViashError "Input file '$VIASH_PAR_SJDBGTFFILE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_READFILESMANIFEST" ] && [ ! -e "$VIASH_PAR_READFILESMANIFEST" ]; then + ViashError "Input file '$VIASH_PAR_READFILESMANIFEST' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_INPUT" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_INPUT; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi +if [ ! -z "$VIASH_PAR_INPUT_R2" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_INPUT_R2; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_RUNRNGSEED" ]]; then + if ! [[ "$VIASH_PAR_RUNRNGSEED" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--runRNGseed' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [ -n "$VIASH_PAR_GENOMEFILESIZES" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_GENOMEFILESIZES; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--genomeFileSizes' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [[ -n "$VIASH_PAR_SJDBOVERHANG" ]]; then + if ! [[ "$VIASH_PAR_SJDBOVERHANG" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--sjdbOverhang' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SJDBSCORE" ]]; then + if ! [[ "$VIASH_PAR_SJDBSCORE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--sjdbScore' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_READMAPNUMBER" ]]; then + if ! [[ "$VIASH_PAR_READMAPNUMBER" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--readMapNumber' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_READQUALITYSCOREBASE" ]]; then + if ! [[ "$VIASH_PAR_READQUALITYSCOREBASE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--readQualityScoreBase' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [ -n "$VIASH_PAR_CLIP3PNBASES" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_CLIP3PNBASES; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--clip3pNbases' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [ -n "$VIASH_PAR_CLIP3PADAPTERMMP" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_CLIP3PADAPTERMMP; do + if ! [[ "${val}" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--clip3pAdapterMMp' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [ -n "$VIASH_PAR_CLIP3PAFTERADAPTERNBASES" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_CLIP3PAFTERADAPTERNBASES; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--clip3pAfterAdapterNbases' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [ -n "$VIASH_PAR_CLIP5PNBASES" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_CLIP5PNBASES; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--clip5pNbases' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [[ -n "$VIASH_PAR_LIMITGENOMEGENERATERAM" ]]; then + if ! [[ "$VIASH_PAR_LIMITGENOMEGENERATERAM" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--limitGenomeGenerateRAM' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [ -n "$VIASH_PAR_LIMITIOBUFFERSIZE" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_LIMITIOBUFFERSIZE; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--limitIObufferSize' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [[ -n "$VIASH_PAR_LIMITOUTSAMONEREADBYTES" ]]; then + if ! [[ "$VIASH_PAR_LIMITOUTSAMONEREADBYTES" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--limitOutSAMoneReadBytes' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LIMITOUTSJONEREAD" ]]; then + if ! [[ "$VIASH_PAR_LIMITOUTSJONEREAD" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--limitOutSJoneRead' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LIMITOUTSJCOLLAPSED" ]]; then + if ! [[ "$VIASH_PAR_LIMITOUTSJCOLLAPSED" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--limitOutSJcollapsed' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LIMITBAMSORTRAM" ]]; then + if ! [[ "$VIASH_PAR_LIMITBAMSORTRAM" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--limitBAMsortRAM' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LIMITSJDBINSERTNSJ" ]]; then + if ! [[ "$VIASH_PAR_LIMITSJDBINSERTNSJ" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--limitSjdbInsertNsj' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LIMITNREADSSOFT" ]]; then + if ! [[ "$VIASH_PAR_LIMITNREADSSOFT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--limitNreadsSoft' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTQSCONVERSIONADD" ]]; then + if ! [[ "$VIASH_PAR_OUTQSCONVERSIONADD" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outQSconversionAdd' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTSAMATTRIHSTART" ]]; then + if ! [[ "$VIASH_PAR_OUTSAMATTRIHSTART" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outSAMattrIHstart' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTSAMMAPQUNIQUE" ]]; then + if ! [[ "$VIASH_PAR_OUTSAMMAPQUNIQUE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outSAMmapqUnique' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTSAMFLAGOR" ]]; then + if ! [[ "$VIASH_PAR_OUTSAMFLAGOR" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outSAMflagOR' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTSAMFLAGAND" ]]; then + if ! [[ "$VIASH_PAR_OUTSAMFLAGAND" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outSAMflagAND' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTSAMMULTNMAX" ]]; then + if ! [[ "$VIASH_PAR_OUTSAMMULTNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outSAMmultNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTSAMTLEN" ]]; then + if ! [[ "$VIASH_PAR_OUTSAMTLEN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outSAMtlen' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTBAMCOMPRESSION" ]]; then + if ! [[ "$VIASH_PAR_OUTBAMCOMPRESSION" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outBAMcompression' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTBAMSORTINGTHREADN" ]]; then + if ! [[ "$VIASH_PAR_OUTBAMSORTINGTHREADN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outBAMsortingThreadN' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTBAMSORTINGBINSN" ]]; then + if ! [[ "$VIASH_PAR_OUTBAMSORTINGBINSN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outBAMsortingBinsN' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN" ]]; then + if ! [[ "$VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--bamRemoveDuplicatesMate2basesN' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE" ]]; then + if ! [[ "$VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outFilterMultimapScoreRange' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTFILTERMULTIMAPNMAX" ]]; then + if ! [[ "$VIASH_PAR_OUTFILTERMULTIMAPNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outFilterMultimapNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTFILTERMISMATCHNMAX" ]]; then + if ! [[ "$VIASH_PAR_OUTFILTERMISMATCHNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outFilterMismatchNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX" ]]; then + if ! [[ "$VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--outFilterMismatchNoverLmax' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX" ]]; then + if ! [[ "$VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--outFilterMismatchNoverReadLmax' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTFILTERSCOREMIN" ]]; then + if ! [[ "$VIASH_PAR_OUTFILTERSCOREMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outFilterScoreMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTFILTERSCOREMINOVERLREAD" ]]; then + if ! [[ "$VIASH_PAR_OUTFILTERSCOREMINOVERLREAD" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--outFilterScoreMinOverLread' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTFILTERMATCHNMIN" ]]; then + if ! [[ "$VIASH_PAR_OUTFILTERMATCHNMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outFilterMatchNmin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD" ]]; then + if ! [[ "$VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--outFilterMatchNminOverLread' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [ -n "$VIASH_PAR_OUTSJFILTEROVERHANGMIN" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_OUTSJFILTEROVERHANGMIN; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outSJfilterOverhangMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [ -n "$VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outSJfilterCountUniqueMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [ -n "$VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outSJfilterCountTotalMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [ -n "$VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outSJfilterDistToOtherSJmin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [ -n "$VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--outSJfilterIntronMaxVsReadN' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [[ -n "$VIASH_PAR_SCOREGAP" ]]; then + if ! [[ "$VIASH_PAR_SCOREGAP" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--scoreGap' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCOREGAPNONCAN" ]]; then + if ! [[ "$VIASH_PAR_SCOREGAPNONCAN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--scoreGapNoncan' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCOREGAPGCAG" ]]; then + if ! [[ "$VIASH_PAR_SCOREGAPGCAG" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--scoreGapGCAG' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCOREGAPATAC" ]]; then + if ! [[ "$VIASH_PAR_SCOREGAPATAC" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--scoreGapATAC' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE" ]]; then + if ! [[ "$VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--scoreGenomicLengthLog2scale' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCOREDELOPEN" ]]; then + if ! [[ "$VIASH_PAR_SCOREDELOPEN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--scoreDelOpen' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCOREDELBASE" ]]; then + if ! [[ "$VIASH_PAR_SCOREDELBASE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--scoreDelBase' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCOREINSOPEN" ]]; then + if ! [[ "$VIASH_PAR_SCOREINSOPEN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--scoreInsOpen' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCOREINSBASE" ]]; then + if ! [[ "$VIASH_PAR_SCOREINSBASE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--scoreInsBase' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SCORESTITCHSJSHIFT" ]]; then + if ! [[ "$VIASH_PAR_SCORESTITCHSJSHIFT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--scoreStitchSJshift' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SEEDSEARCHSTARTLMAX" ]]; then + if ! [[ "$VIASH_PAR_SEEDSEARCHSTARTLMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--seedSearchStartLmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD" ]]; then + if ! [[ "$VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--seedSearchStartLmaxOverLread' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SEEDSEARCHLMAX" ]]; then + if ! [[ "$VIASH_PAR_SEEDSEARCHLMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--seedSearchLmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SEEDMULTIMAPNMAX" ]]; then + if ! [[ "$VIASH_PAR_SEEDMULTIMAPNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--seedMultimapNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SEEDPERREADNMAX" ]]; then + if ! [[ "$VIASH_PAR_SEEDPERREADNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--seedPerReadNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SEEDPERWINDOWNMAX" ]]; then + if ! [[ "$VIASH_PAR_SEEDPERWINDOWNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--seedPerWindowNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SEEDNONELOCIPERWINDOW" ]]; then + if ! [[ "$VIASH_PAR_SEEDNONELOCIPERWINDOW" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--seedNoneLociPerWindow' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SEEDSPLITMIN" ]]; then + if ! [[ "$VIASH_PAR_SEEDSPLITMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--seedSplitMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_SEEDMAPMIN" ]]; then + if ! [[ "$VIASH_PAR_SEEDMAPMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--seedMapMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ALIGNINTRONMIN" ]]; then + if ! [[ "$VIASH_PAR_ALIGNINTRONMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--alignIntronMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ALIGNINTRONMAX" ]]; then + if ! [[ "$VIASH_PAR_ALIGNINTRONMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--alignIntronMax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ALIGNMATESGAPMAX" ]]; then + if ! [[ "$VIASH_PAR_ALIGNMATESGAPMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--alignMatesGapMax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ALIGNSJOVERHANGMIN" ]]; then + if ! [[ "$VIASH_PAR_ALIGNSJOVERHANGMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--alignSJoverhangMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [ -n "$VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX" ]; then + IFS=';' + set -f + for val in $VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX; do + if ! [[ "${val}" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--alignSJstitchMismatchNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + done + set +f + unset IFS +fi + +if [[ -n "$VIASH_PAR_ALIGNSJDBOVERHANGMIN" ]]; then + if ! [[ "$VIASH_PAR_ALIGNSJDBOVERHANGMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--alignSJDBoverhangMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN" ]]; then + if ! [[ "$VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--alignSplicedMateMapLmin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE" ]]; then + if ! [[ "$VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--alignSplicedMateMapLminOverLmate' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ALIGNWINDOWSPERREADNMAX" ]]; then + if ! [[ "$VIASH_PAR_ALIGNWINDOWSPERREADNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--alignWindowsPerReadNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX" ]]; then + if ! [[ "$VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--alignTranscriptsPerWindowNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX" ]]; then + if ! [[ "$VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--alignTranscriptsPerReadNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PEOVERLAPNBASESMIN" ]]; then + if ! [[ "$VIASH_PAR_PEOVERLAPNBASESMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--peOverlapNbasesMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_PEOVERLAPMMP" ]]; then + if ! [[ "$VIASH_PAR_PEOVERLAPMMP" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--peOverlapMMp' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WINANCHORMULTIMAPNMAX" ]]; then + if ! [[ "$VIASH_PAR_WINANCHORMULTIMAPNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--winAnchorMultimapNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WINBINNBITS" ]]; then + if ! [[ "$VIASH_PAR_WINBINNBITS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--winBinNbits' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WINANCHORDISTNBINS" ]]; then + if ! [[ "$VIASH_PAR_WINANCHORDISTNBINS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--winAnchorDistNbins' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WINFLANKNBINS" ]]; then + if ! [[ "$VIASH_PAR_WINFLANKNBINS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--winFlankNbins' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WINREADCOVERAGERELATIVEMIN" ]]; then + if ! [[ "$VIASH_PAR_WINREADCOVERAGERELATIVEMIN" =~ ^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$ ]]; then + ViashError '--winReadCoverageRelativeMin' has to be a double. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_WINREADCOVERAGEBASESMIN" ]]; then + if ! [[ "$VIASH_PAR_WINREADCOVERAGEBASESMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--winReadCoverageBasesMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMSEGMENTMIN" ]]; then + if ! [[ "$VIASH_PAR_CHIMSEGMENTMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimSegmentMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMSCOREMIN" ]]; then + if ! [[ "$VIASH_PAR_CHIMSCOREMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimScoreMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMSCOREDROPMAX" ]]; then + if ! [[ "$VIASH_PAR_CHIMSCOREDROPMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimScoreDropMax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMSCORESEPARATION" ]]; then + if ! [[ "$VIASH_PAR_CHIMSCORESEPARATION" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimScoreSeparation' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG" ]]; then + if ! [[ "$VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimScoreJunctionNonGTAG' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMJUNCTIONOVERHANGMIN" ]]; then + if ! [[ "$VIASH_PAR_CHIMJUNCTIONOVERHANGMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimJunctionOverhangMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMSEGMENTREADGAPMAX" ]]; then + if ! [[ "$VIASH_PAR_CHIMSEGMENTREADGAPMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimSegmentReadGapMax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMMAINSEGMENTMULTNMAX" ]]; then + if ! [[ "$VIASH_PAR_CHIMMAINSEGMENTMULTNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimMainSegmentMultNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMMULTIMAPNMAX" ]]; then + if ! [[ "$VIASH_PAR_CHIMMULTIMAPNMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimMultimapNmax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMMULTIMAPSCORERANGE" ]]; then + if ! [[ "$VIASH_PAR_CHIMMULTIMAPSCORERANGE" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimMultimapScoreRange' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMNONCHIMSCOREDROPMIN" ]]; then + if ! [[ "$VIASH_PAR_CHIMNONCHIMSCOREDROPMIN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimNonchimScoreDropMin' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_CHIMOUTJUNCTIONFORMAT" ]]; then + if ! [[ "$VIASH_PAR_CHIMOUTJUNCTIONFORMAT" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--chimOutJunctionFormat' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION" ]]; then + if ! [[ "$VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--quantTranscriptomeBAMcompression' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_TWOPASS1READSN" ]]; then + if ! [[ "$VIASH_PAR_TWOPASS1READSN" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--twopass1readsN' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_ALIGNED_READS" ] && [ ! -d "$(dirname "$VIASH_PAR_ALIGNED_READS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_ALIGNED_READS")" +fi +if [ ! -z "$VIASH_PAR_READS_PER_GENE" ] && [ ! -d "$(dirname "$VIASH_PAR_READS_PER_GENE")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_READS_PER_GENE")" +fi +if [ ! -z "$VIASH_PAR_UNMAPPED" ] && [ ! -d "$(dirname "$VIASH_PAR_UNMAPPED")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_UNMAPPED")" +fi +if [ ! -z "$VIASH_PAR_UNMAPPED_R2" ] && [ ! -d "$(dirname "$VIASH_PAR_UNMAPPED_R2")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_UNMAPPED_R2")" +fi +if [ ! -z "$VIASH_PAR_CHIMERIC_JUNCTIONS" ] && [ ! -d "$(dirname "$VIASH_PAR_CHIMERIC_JUNCTIONS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_CHIMERIC_JUNCTIONS")" +fi +if [ ! -z "$VIASH_PAR_LOG" ] && [ ! -d "$(dirname "$VIASH_PAR_LOG")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_LOG")" +fi +if [ ! -z "$VIASH_PAR_SPLICE_JUNCTIONS" ] && [ ! -d "$(dirname "$VIASH_PAR_SPLICE_JUNCTIONS")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_SPLICE_JUNCTIONS")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_GENOMEDIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_GENOMEDIR")" ) + VIASH_PAR_GENOMEDIR=$(ViashDockerAutodetectMount "$VIASH_PAR_GENOMEDIR") +fi +if [ ! -z "$VIASH_PAR_GENOMEFASTAFILES" ]; then + VIASH_TEST_GENOMEFASTAFILES=() + IFS=';' + for var in $VIASH_PAR_GENOMEFASTAFILES; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_GENOMEFASTAFILES+=( "$var" ) + done + VIASH_PAR_GENOMEFASTAFILES=$(IFS=';' ; echo "${VIASH_TEST_GENOMEFASTAFILES[*]}") +fi +if [ ! -z "$VIASH_PAR_SJDBGTFFILE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SJDBGTFFILE")" ) + VIASH_PAR_SJDBGTFFILE=$(ViashDockerAutodetectMount "$VIASH_PAR_SJDBGTFFILE") +fi +if [ ! -z "$VIASH_PAR_READFILESMANIFEST" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_READFILESMANIFEST")" ) + VIASH_PAR_READFILESMANIFEST=$(ViashDockerAutodetectMount "$VIASH_PAR_READFILESMANIFEST") +fi +if [ ! -z "$VIASH_PAR_INPUT" ]; then + VIASH_TEST_INPUT=() + IFS=';' + for var in $VIASH_PAR_INPUT; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_INPUT+=( "$var" ) + done + VIASH_PAR_INPUT=$(IFS=';' ; echo "${VIASH_TEST_INPUT[*]}") +fi +if [ ! -z "$VIASH_PAR_INPUT_R2" ]; then + VIASH_TEST_INPUT_R2=() + IFS=';' + for var in $VIASH_PAR_INPUT_R2; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_INPUT_R2+=( "$var" ) + done + VIASH_PAR_INPUT_R2=$(IFS=';' ; echo "${VIASH_TEST_INPUT_R2[*]}") +fi +if [ ! -z "$VIASH_PAR_ALIGNED_READS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_ALIGNED_READS")" ) + VIASH_PAR_ALIGNED_READS=$(ViashDockerAutodetectMount "$VIASH_PAR_ALIGNED_READS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_ALIGNED_READS" ) +fi +if [ ! -z "$VIASH_PAR_READS_PER_GENE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_READS_PER_GENE")" ) + VIASH_PAR_READS_PER_GENE=$(ViashDockerAutodetectMount "$VIASH_PAR_READS_PER_GENE") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_READS_PER_GENE" ) +fi +if [ ! -z "$VIASH_PAR_UNMAPPED" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_UNMAPPED")" ) + VIASH_PAR_UNMAPPED=$(ViashDockerAutodetectMount "$VIASH_PAR_UNMAPPED") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_UNMAPPED" ) +fi +if [ ! -z "$VIASH_PAR_UNMAPPED_R2" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_UNMAPPED_R2")" ) + VIASH_PAR_UNMAPPED_R2=$(ViashDockerAutodetectMount "$VIASH_PAR_UNMAPPED_R2") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_UNMAPPED_R2" ) +fi +if [ ! -z "$VIASH_PAR_CHIMERIC_JUNCTIONS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_CHIMERIC_JUNCTIONS")" ) + VIASH_PAR_CHIMERIC_JUNCTIONS=$(ViashDockerAutodetectMount "$VIASH_PAR_CHIMERIC_JUNCTIONS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_CHIMERIC_JUNCTIONS" ) +fi +if [ ! -z "$VIASH_PAR_LOG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_LOG")" ) + VIASH_PAR_LOG=$(ViashDockerAutodetectMount "$VIASH_PAR_LOG") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_LOG" ) +fi +if [ ! -z "$VIASH_PAR_SPLICE_JUNCTIONS" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SPLICE_JUNCTIONS")" ) + VIASH_PAR_SPLICE_JUNCTIONS=$(ViashDockerAutodetectMount "$VIASH_PAR_SPLICE_JUNCTIONS") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_SPLICE_JUNCTIONS" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-star_align_reads-XXXXXX").py +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +import tempfile +import subprocess +import shutil +from pathlib import Path + +## VIASH START +# The following code has been auto-generated by Viash. +par = { + 'runRNGseed': $( if [ ! -z ${VIASH_PAR_RUNRNGSEED+x} ]; then echo "int(r'${VIASH_PAR_RUNRNGSEED//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'genomeDir': $( if [ ! -z ${VIASH_PAR_GENOMEDIR+x} ]; then echo "r'${VIASH_PAR_GENOMEDIR//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'genomeLoad': $( if [ ! -z ${VIASH_PAR_GENOMELOAD+x} ]; then echo "r'${VIASH_PAR_GENOMELOAD//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'genomeFastaFiles': $( if [ ! -z ${VIASH_PAR_GENOMEFASTAFILES+x} ]; then echo "r'${VIASH_PAR_GENOMEFASTAFILES//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'genomeFileSizes': $( if [ ! -z ${VIASH_PAR_GENOMEFILESIZES+x} ]; then echo "list(map(int, r'${VIASH_PAR_GENOMEFILESIZES//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'genomeTransformOutput': $( if [ ! -z ${VIASH_PAR_GENOMETRANSFORMOUTPUT+x} ]; then echo "r'${VIASH_PAR_GENOMETRANSFORMOUTPUT//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'genomeChrSetMitochondrial': $( if [ ! -z ${VIASH_PAR_GENOMECHRSETMITOCHONDRIAL+x} ]; then echo "r'${VIASH_PAR_GENOMECHRSETMITOCHONDRIAL//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'sjdbFileChrStartEnd': $( if [ ! -z ${VIASH_PAR_SJDBFILECHRSTARTEND+x} ]; then echo "r'${VIASH_PAR_SJDBFILECHRSTARTEND//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'sjdbGTFfile': $( if [ ! -z ${VIASH_PAR_SJDBGTFFILE+x} ]; then echo "r'${VIASH_PAR_SJDBGTFFILE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'sjdbGTFchrPrefix': $( if [ ! -z ${VIASH_PAR_SJDBGTFCHRPREFIX+x} ]; then echo "r'${VIASH_PAR_SJDBGTFCHRPREFIX//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'sjdbGTFfeatureExon': $( if [ ! -z ${VIASH_PAR_SJDBGTFFEATUREEXON+x} ]; then echo "r'${VIASH_PAR_SJDBGTFFEATUREEXON//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'sjdbGTFtagExonParentTranscript': $( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT+x} ]; then echo "r'${VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'sjdbGTFtagExonParentGene': $( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENE+x} ]; then echo "r'${VIASH_PAR_SJDBGTFTAGEXONPARENTGENE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'sjdbGTFtagExonParentGeneName': $( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME+x} ]; then echo "r'${VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'sjdbGTFtagExonParentGeneType': $( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE+x} ]; then echo "r'${VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'sjdbOverhang': $( if [ ! -z ${VIASH_PAR_SJDBOVERHANG+x} ]; then echo "int(r'${VIASH_PAR_SJDBOVERHANG//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'sjdbScore': $( if [ ! -z ${VIASH_PAR_SJDBSCORE+x} ]; then echo "int(r'${VIASH_PAR_SJDBSCORE//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'sjdbInsertSave': $( if [ ! -z ${VIASH_PAR_SJDBINSERTSAVE+x} ]; then echo "r'${VIASH_PAR_SJDBINSERTSAVE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'varVCFfile': $( if [ ! -z ${VIASH_PAR_VARVCFFILE+x} ]; then echo "r'${VIASH_PAR_VARVCFFILE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'readFilesType': $( if [ ! -z ${VIASH_PAR_READFILESTYPE+x} ]; then echo "r'${VIASH_PAR_READFILESTYPE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'readFilesSAMattrKeep': $( if [ ! -z ${VIASH_PAR_READFILESSAMATTRKEEP+x} ]; then echo "r'${VIASH_PAR_READFILESSAMATTRKEEP//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'readFilesManifest': $( if [ ! -z ${VIASH_PAR_READFILESMANIFEST+x} ]; then echo "r'${VIASH_PAR_READFILESMANIFEST//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'readFilesPrefix': $( if [ ! -z ${VIASH_PAR_READFILESPREFIX+x} ]; then echo "r'${VIASH_PAR_READFILESPREFIX//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'readFilesCommand': $( if [ ! -z ${VIASH_PAR_READFILESCOMMAND+x} ]; then echo "r'${VIASH_PAR_READFILESCOMMAND//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'readMapNumber': $( if [ ! -z ${VIASH_PAR_READMAPNUMBER+x} ]; then echo "int(r'${VIASH_PAR_READMAPNUMBER//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'readMatesLengthsIn': $( if [ ! -z ${VIASH_PAR_READMATESLENGTHSIN+x} ]; then echo "r'${VIASH_PAR_READMATESLENGTHSIN//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'readNameSeparator': $( if [ ! -z ${VIASH_PAR_READNAMESEPARATOR+x} ]; then echo "r'${VIASH_PAR_READNAMESEPARATOR//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'readQualityScoreBase': $( if [ ! -z ${VIASH_PAR_READQUALITYSCOREBASE+x} ]; then echo "int(r'${VIASH_PAR_READQUALITYSCOREBASE//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'clipAdapterType': $( if [ ! -z ${VIASH_PAR_CLIPADAPTERTYPE+x} ]; then echo "r'${VIASH_PAR_CLIPADAPTERTYPE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'clip3pNbases': $( if [ ! -z ${VIASH_PAR_CLIP3PNBASES+x} ]; then echo "list(map(int, r'${VIASH_PAR_CLIP3PNBASES//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'clip3pAdapterSeq': $( if [ ! -z ${VIASH_PAR_CLIP3PADAPTERSEQ+x} ]; then echo "r'${VIASH_PAR_CLIP3PADAPTERSEQ//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'clip3pAdapterMMp': $( if [ ! -z ${VIASH_PAR_CLIP3PADAPTERMMP+x} ]; then echo "list(map(float, r'${VIASH_PAR_CLIP3PADAPTERMMP//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'clip3pAfterAdapterNbases': $( if [ ! -z ${VIASH_PAR_CLIP3PAFTERADAPTERNBASES+x} ]; then echo "list(map(int, r'${VIASH_PAR_CLIP3PAFTERADAPTERNBASES//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'clip5pNbases': $( if [ ! -z ${VIASH_PAR_CLIP5PNBASES+x} ]; then echo "list(map(int, r'${VIASH_PAR_CLIP5PNBASES//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'limitGenomeGenerateRAM': $( if [ ! -z ${VIASH_PAR_LIMITGENOMEGENERATERAM+x} ]; then echo "int(r'${VIASH_PAR_LIMITGENOMEGENERATERAM//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'limitIObufferSize': $( if [ ! -z ${VIASH_PAR_LIMITIOBUFFERSIZE+x} ]; then echo "list(map(int, r'${VIASH_PAR_LIMITIOBUFFERSIZE//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'limitOutSAMoneReadBytes': $( if [ ! -z ${VIASH_PAR_LIMITOUTSAMONEREADBYTES+x} ]; then echo "int(r'${VIASH_PAR_LIMITOUTSAMONEREADBYTES//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'limitOutSJoneRead': $( if [ ! -z ${VIASH_PAR_LIMITOUTSJONEREAD+x} ]; then echo "int(r'${VIASH_PAR_LIMITOUTSJONEREAD//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'limitOutSJcollapsed': $( if [ ! -z ${VIASH_PAR_LIMITOUTSJCOLLAPSED+x} ]; then echo "int(r'${VIASH_PAR_LIMITOUTSJCOLLAPSED//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'limitBAMsortRAM': $( if [ ! -z ${VIASH_PAR_LIMITBAMSORTRAM+x} ]; then echo "int(r'${VIASH_PAR_LIMITBAMSORTRAM//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'limitSjdbInsertNsj': $( if [ ! -z ${VIASH_PAR_LIMITSJDBINSERTNSJ+x} ]; then echo "int(r'${VIASH_PAR_LIMITSJDBINSERTNSJ//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'limitNreadsSoft': $( if [ ! -z ${VIASH_PAR_LIMITNREADSSOFT+x} ]; then echo "int(r'${VIASH_PAR_LIMITNREADSSOFT//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outTmpKeep': $( if [ ! -z ${VIASH_PAR_OUTTMPKEEP+x} ]; then echo "r'${VIASH_PAR_OUTTMPKEEP//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outStd': $( if [ ! -z ${VIASH_PAR_OUTSTD+x} ]; then echo "r'${VIASH_PAR_OUTSTD//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outReadsUnmapped': $( if [ ! -z ${VIASH_PAR_OUTREADSUNMAPPED+x} ]; then echo "r'${VIASH_PAR_OUTREADSUNMAPPED//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outQSconversionAdd': $( if [ ! -z ${VIASH_PAR_OUTQSCONVERSIONADD+x} ]; then echo "int(r'${VIASH_PAR_OUTQSCONVERSIONADD//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outMultimapperOrder': $( if [ ! -z ${VIASH_PAR_OUTMULTIMAPPERORDER+x} ]; then echo "r'${VIASH_PAR_OUTMULTIMAPPERORDER//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outSAMtype': $( if [ ! -z ${VIASH_PAR_OUTSAMTYPE+x} ]; then echo "r'${VIASH_PAR_OUTSAMTYPE//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'outSAMmode': $( if [ ! -z ${VIASH_PAR_OUTSAMMODE+x} ]; then echo "r'${VIASH_PAR_OUTSAMMODE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outSAMstrandField': $( if [ ! -z ${VIASH_PAR_OUTSAMSTRANDFIELD+x} ]; then echo "r'${VIASH_PAR_OUTSAMSTRANDFIELD//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outSAMattributes': $( if [ ! -z ${VIASH_PAR_OUTSAMATTRIBUTES+x} ]; then echo "r'${VIASH_PAR_OUTSAMATTRIBUTES//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'outSAMattrIHstart': $( if [ ! -z ${VIASH_PAR_OUTSAMATTRIHSTART+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMATTRIHSTART//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outSAMunmapped': $( if [ ! -z ${VIASH_PAR_OUTSAMUNMAPPED+x} ]; then echo "r'${VIASH_PAR_OUTSAMUNMAPPED//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'outSAMorder': $( if [ ! -z ${VIASH_PAR_OUTSAMORDER+x} ]; then echo "r'${VIASH_PAR_OUTSAMORDER//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outSAMprimaryFlag': $( if [ ! -z ${VIASH_PAR_OUTSAMPRIMARYFLAG+x} ]; then echo "r'${VIASH_PAR_OUTSAMPRIMARYFLAG//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outSAMreadID': $( if [ ! -z ${VIASH_PAR_OUTSAMREADID+x} ]; then echo "r'${VIASH_PAR_OUTSAMREADID//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outSAMmapqUnique': $( if [ ! -z ${VIASH_PAR_OUTSAMMAPQUNIQUE+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMMAPQUNIQUE//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outSAMflagOR': $( if [ ! -z ${VIASH_PAR_OUTSAMFLAGOR+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMFLAGOR//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outSAMflagAND': $( if [ ! -z ${VIASH_PAR_OUTSAMFLAGAND+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMFLAGAND//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outSAMattrRGline': $( if [ ! -z ${VIASH_PAR_OUTSAMATTRRGLINE+x} ]; then echo "r'${VIASH_PAR_OUTSAMATTRRGLINE//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'outSAMheaderHD': $( if [ ! -z ${VIASH_PAR_OUTSAMHEADERHD+x} ]; then echo "r'${VIASH_PAR_OUTSAMHEADERHD//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'outSAMheaderPG': $( if [ ! -z ${VIASH_PAR_OUTSAMHEADERPG+x} ]; then echo "r'${VIASH_PAR_OUTSAMHEADERPG//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'outSAMheaderCommentFile': $( if [ ! -z ${VIASH_PAR_OUTSAMHEADERCOMMENTFILE+x} ]; then echo "r'${VIASH_PAR_OUTSAMHEADERCOMMENTFILE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outSAMfilter': $( if [ ! -z ${VIASH_PAR_OUTSAMFILTER+x} ]; then echo "r'${VIASH_PAR_OUTSAMFILTER//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'outSAMmultNmax': $( if [ ! -z ${VIASH_PAR_OUTSAMMULTNMAX+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMMULTNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outSAMtlen': $( if [ ! -z ${VIASH_PAR_OUTSAMTLEN+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMTLEN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outBAMcompression': $( if [ ! -z ${VIASH_PAR_OUTBAMCOMPRESSION+x} ]; then echo "int(r'${VIASH_PAR_OUTBAMCOMPRESSION//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outBAMsortingThreadN': $( if [ ! -z ${VIASH_PAR_OUTBAMSORTINGTHREADN+x} ]; then echo "int(r'${VIASH_PAR_OUTBAMSORTINGTHREADN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outBAMsortingBinsN': $( if [ ! -z ${VIASH_PAR_OUTBAMSORTINGBINSN+x} ]; then echo "int(r'${VIASH_PAR_OUTBAMSORTINGBINSN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'bamRemoveDuplicatesType': $( if [ ! -z ${VIASH_PAR_BAMREMOVEDUPLICATESTYPE+x} ]; then echo "r'${VIASH_PAR_BAMREMOVEDUPLICATESTYPE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'bamRemoveDuplicatesMate2basesN': $( if [ ! -z ${VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN+x} ]; then echo "int(r'${VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outWigType': $( if [ ! -z ${VIASH_PAR_OUTWIGTYPE+x} ]; then echo "r'${VIASH_PAR_OUTWIGTYPE//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'outWigStrand': $( if [ ! -z ${VIASH_PAR_OUTWIGSTRAND+x} ]; then echo "r'${VIASH_PAR_OUTWIGSTRAND//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outWigReferencesPrefix': $( if [ ! -z ${VIASH_PAR_OUTWIGREFERENCESPREFIX+x} ]; then echo "r'${VIASH_PAR_OUTWIGREFERENCESPREFIX//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outWigNorm': $( if [ ! -z ${VIASH_PAR_OUTWIGNORM+x} ]; then echo "r'${VIASH_PAR_OUTWIGNORM//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outFilterType': $( if [ ! -z ${VIASH_PAR_OUTFILTERTYPE+x} ]; then echo "r'${VIASH_PAR_OUTFILTERTYPE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outFilterMultimapScoreRange': $( if [ ! -z ${VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE+x} ]; then echo "int(r'${VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outFilterMultimapNmax': $( if [ ! -z ${VIASH_PAR_OUTFILTERMULTIMAPNMAX+x} ]; then echo "int(r'${VIASH_PAR_OUTFILTERMULTIMAPNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outFilterMismatchNmax': $( if [ ! -z ${VIASH_PAR_OUTFILTERMISMATCHNMAX+x} ]; then echo "int(r'${VIASH_PAR_OUTFILTERMISMATCHNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outFilterMismatchNoverLmax': $( if [ ! -z ${VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX+x} ]; then echo "float(r'${VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outFilterMismatchNoverReadLmax': $( if [ ! -z ${VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX+x} ]; then echo "float(r'${VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outFilterScoreMin': $( if [ ! -z ${VIASH_PAR_OUTFILTERSCOREMIN+x} ]; then echo "int(r'${VIASH_PAR_OUTFILTERSCOREMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outFilterScoreMinOverLread': $( if [ ! -z ${VIASH_PAR_OUTFILTERSCOREMINOVERLREAD+x} ]; then echo "float(r'${VIASH_PAR_OUTFILTERSCOREMINOVERLREAD//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outFilterMatchNmin': $( if [ ! -z ${VIASH_PAR_OUTFILTERMATCHNMIN+x} ]; then echo "int(r'${VIASH_PAR_OUTFILTERMATCHNMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outFilterMatchNminOverLread': $( if [ ! -z ${VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD+x} ]; then echo "float(r'${VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'outFilterIntronMotifs': $( if [ ! -z ${VIASH_PAR_OUTFILTERINTRONMOTIFS+x} ]; then echo "r'${VIASH_PAR_OUTFILTERINTRONMOTIFS//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outFilterIntronStrands': $( if [ ! -z ${VIASH_PAR_OUTFILTERINTRONSTRANDS+x} ]; then echo "r'${VIASH_PAR_OUTFILTERINTRONSTRANDS//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outSJtype': $( if [ ! -z ${VIASH_PAR_OUTSJTYPE+x} ]; then echo "r'${VIASH_PAR_OUTSJTYPE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outSJfilterReads': $( if [ ! -z ${VIASH_PAR_OUTSJFILTERREADS+x} ]; then echo "r'${VIASH_PAR_OUTSJFILTERREADS//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'outSJfilterOverhangMin': $( if [ ! -z ${VIASH_PAR_OUTSJFILTEROVERHANGMIN+x} ]; then echo "list(map(int, r'${VIASH_PAR_OUTSJFILTEROVERHANGMIN//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'outSJfilterCountUniqueMin': $( if [ ! -z ${VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN+x} ]; then echo "list(map(int, r'${VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'outSJfilterCountTotalMin': $( if [ ! -z ${VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN+x} ]; then echo "list(map(int, r'${VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'outSJfilterDistToOtherSJmin': $( if [ ! -z ${VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN+x} ]; then echo "list(map(int, r'${VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'outSJfilterIntronMaxVsReadN': $( if [ ! -z ${VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN+x} ]; then echo "list(map(int, r'${VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'scoreGap': $( if [ ! -z ${VIASH_PAR_SCOREGAP+x} ]; then echo "int(r'${VIASH_PAR_SCOREGAP//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'scoreGapNoncan': $( if [ ! -z ${VIASH_PAR_SCOREGAPNONCAN+x} ]; then echo "int(r'${VIASH_PAR_SCOREGAPNONCAN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'scoreGapGCAG': $( if [ ! -z ${VIASH_PAR_SCOREGAPGCAG+x} ]; then echo "int(r'${VIASH_PAR_SCOREGAPGCAG//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'scoreGapATAC': $( if [ ! -z ${VIASH_PAR_SCOREGAPATAC+x} ]; then echo "int(r'${VIASH_PAR_SCOREGAPATAC//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'scoreGenomicLengthLog2scale': $( if [ ! -z ${VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE+x} ]; then echo "int(r'${VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'scoreDelOpen': $( if [ ! -z ${VIASH_PAR_SCOREDELOPEN+x} ]; then echo "int(r'${VIASH_PAR_SCOREDELOPEN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'scoreDelBase': $( if [ ! -z ${VIASH_PAR_SCOREDELBASE+x} ]; then echo "int(r'${VIASH_PAR_SCOREDELBASE//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'scoreInsOpen': $( if [ ! -z ${VIASH_PAR_SCOREINSOPEN+x} ]; then echo "int(r'${VIASH_PAR_SCOREINSOPEN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'scoreInsBase': $( if [ ! -z ${VIASH_PAR_SCOREINSBASE+x} ]; then echo "int(r'${VIASH_PAR_SCOREINSBASE//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'scoreStitchSJshift': $( if [ ! -z ${VIASH_PAR_SCORESTITCHSJSHIFT+x} ]; then echo "int(r'${VIASH_PAR_SCORESTITCHSJSHIFT//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'seedSearchStartLmax': $( if [ ! -z ${VIASH_PAR_SEEDSEARCHSTARTLMAX+x} ]; then echo "int(r'${VIASH_PAR_SEEDSEARCHSTARTLMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'seedSearchStartLmaxOverLread': $( if [ ! -z ${VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD+x} ]; then echo "float(r'${VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'seedSearchLmax': $( if [ ! -z ${VIASH_PAR_SEEDSEARCHLMAX+x} ]; then echo "int(r'${VIASH_PAR_SEEDSEARCHLMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'seedMultimapNmax': $( if [ ! -z ${VIASH_PAR_SEEDMULTIMAPNMAX+x} ]; then echo "int(r'${VIASH_PAR_SEEDMULTIMAPNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'seedPerReadNmax': $( if [ ! -z ${VIASH_PAR_SEEDPERREADNMAX+x} ]; then echo "int(r'${VIASH_PAR_SEEDPERREADNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'seedPerWindowNmax': $( if [ ! -z ${VIASH_PAR_SEEDPERWINDOWNMAX+x} ]; then echo "int(r'${VIASH_PAR_SEEDPERWINDOWNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'seedNoneLociPerWindow': $( if [ ! -z ${VIASH_PAR_SEEDNONELOCIPERWINDOW+x} ]; then echo "int(r'${VIASH_PAR_SEEDNONELOCIPERWINDOW//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'seedSplitMin': $( if [ ! -z ${VIASH_PAR_SEEDSPLITMIN+x} ]; then echo "int(r'${VIASH_PAR_SEEDSPLITMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'seedMapMin': $( if [ ! -z ${VIASH_PAR_SEEDMAPMIN+x} ]; then echo "int(r'${VIASH_PAR_SEEDMAPMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'alignIntronMin': $( if [ ! -z ${VIASH_PAR_ALIGNINTRONMIN+x} ]; then echo "int(r'${VIASH_PAR_ALIGNINTRONMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'alignIntronMax': $( if [ ! -z ${VIASH_PAR_ALIGNINTRONMAX+x} ]; then echo "int(r'${VIASH_PAR_ALIGNINTRONMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'alignMatesGapMax': $( if [ ! -z ${VIASH_PAR_ALIGNMATESGAPMAX+x} ]; then echo "int(r'${VIASH_PAR_ALIGNMATESGAPMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'alignSJoverhangMin': $( if [ ! -z ${VIASH_PAR_ALIGNSJOVERHANGMIN+x} ]; then echo "int(r'${VIASH_PAR_ALIGNSJOVERHANGMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'alignSJstitchMismatchNmax': $( if [ ! -z ${VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX+x} ]; then echo "list(map(int, r'${VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX//\'/\'\"\'\"r\'}'.split(';')))"; else echo None; fi ), + 'alignSJDBoverhangMin': $( if [ ! -z ${VIASH_PAR_ALIGNSJDBOVERHANGMIN+x} ]; then echo "int(r'${VIASH_PAR_ALIGNSJDBOVERHANGMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'alignSplicedMateMapLmin': $( if [ ! -z ${VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN+x} ]; then echo "int(r'${VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'alignSplicedMateMapLminOverLmate': $( if [ ! -z ${VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE+x} ]; then echo "float(r'${VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'alignWindowsPerReadNmax': $( if [ ! -z ${VIASH_PAR_ALIGNWINDOWSPERREADNMAX+x} ]; then echo "int(r'${VIASH_PAR_ALIGNWINDOWSPERREADNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'alignTranscriptsPerWindowNmax': $( if [ ! -z ${VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX+x} ]; then echo "int(r'${VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'alignTranscriptsPerReadNmax': $( if [ ! -z ${VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX+x} ]; then echo "int(r'${VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'alignEndsType': $( if [ ! -z ${VIASH_PAR_ALIGNENDSTYPE+x} ]; then echo "r'${VIASH_PAR_ALIGNENDSTYPE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'alignEndsProtrude': $( if [ ! -z ${VIASH_PAR_ALIGNENDSPROTRUDE+x} ]; then echo "r'${VIASH_PAR_ALIGNENDSPROTRUDE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'alignSoftClipAtReferenceEnds': $( if [ ! -z ${VIASH_PAR_ALIGNSOFTCLIPATREFERENCEENDS+x} ]; then echo "r'${VIASH_PAR_ALIGNSOFTCLIPATREFERENCEENDS//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'alignInsertionFlush': $( if [ ! -z ${VIASH_PAR_ALIGNINSERTIONFLUSH+x} ]; then echo "r'${VIASH_PAR_ALIGNINSERTIONFLUSH//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'peOverlapNbasesMin': $( if [ ! -z ${VIASH_PAR_PEOVERLAPNBASESMIN+x} ]; then echo "int(r'${VIASH_PAR_PEOVERLAPNBASESMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'peOverlapMMp': $( if [ ! -z ${VIASH_PAR_PEOVERLAPMMP+x} ]; then echo "float(r'${VIASH_PAR_PEOVERLAPMMP//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'winAnchorMultimapNmax': $( if [ ! -z ${VIASH_PAR_WINANCHORMULTIMAPNMAX+x} ]; then echo "int(r'${VIASH_PAR_WINANCHORMULTIMAPNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'winBinNbits': $( if [ ! -z ${VIASH_PAR_WINBINNBITS+x} ]; then echo "int(r'${VIASH_PAR_WINBINNBITS//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'winAnchorDistNbins': $( if [ ! -z ${VIASH_PAR_WINANCHORDISTNBINS+x} ]; then echo "int(r'${VIASH_PAR_WINANCHORDISTNBINS//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'winFlankNbins': $( if [ ! -z ${VIASH_PAR_WINFLANKNBINS+x} ]; then echo "int(r'${VIASH_PAR_WINFLANKNBINS//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'winReadCoverageRelativeMin': $( if [ ! -z ${VIASH_PAR_WINREADCOVERAGERELATIVEMIN+x} ]; then echo "float(r'${VIASH_PAR_WINREADCOVERAGERELATIVEMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'winReadCoverageBasesMin': $( if [ ! -z ${VIASH_PAR_WINREADCOVERAGEBASESMIN+x} ]; then echo "int(r'${VIASH_PAR_WINREADCOVERAGEBASESMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimOutType': $( if [ ! -z ${VIASH_PAR_CHIMOUTTYPE+x} ]; then echo "r'${VIASH_PAR_CHIMOUTTYPE//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'chimSegmentMin': $( if [ ! -z ${VIASH_PAR_CHIMSEGMENTMIN+x} ]; then echo "int(r'${VIASH_PAR_CHIMSEGMENTMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimScoreMin': $( if [ ! -z ${VIASH_PAR_CHIMSCOREMIN+x} ]; then echo "int(r'${VIASH_PAR_CHIMSCOREMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimScoreDropMax': $( if [ ! -z ${VIASH_PAR_CHIMSCOREDROPMAX+x} ]; then echo "int(r'${VIASH_PAR_CHIMSCOREDROPMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimScoreSeparation': $( if [ ! -z ${VIASH_PAR_CHIMSCORESEPARATION+x} ]; then echo "int(r'${VIASH_PAR_CHIMSCORESEPARATION//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimScoreJunctionNonGTAG': $( if [ ! -z ${VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG+x} ]; then echo "int(r'${VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimJunctionOverhangMin': $( if [ ! -z ${VIASH_PAR_CHIMJUNCTIONOVERHANGMIN+x} ]; then echo "int(r'${VIASH_PAR_CHIMJUNCTIONOVERHANGMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimSegmentReadGapMax': $( if [ ! -z ${VIASH_PAR_CHIMSEGMENTREADGAPMAX+x} ]; then echo "int(r'${VIASH_PAR_CHIMSEGMENTREADGAPMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimFilter': $( if [ ! -z ${VIASH_PAR_CHIMFILTER+x} ]; then echo "r'${VIASH_PAR_CHIMFILTER//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'chimMainSegmentMultNmax': $( if [ ! -z ${VIASH_PAR_CHIMMAINSEGMENTMULTNMAX+x} ]; then echo "int(r'${VIASH_PAR_CHIMMAINSEGMENTMULTNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimMultimapNmax': $( if [ ! -z ${VIASH_PAR_CHIMMULTIMAPNMAX+x} ]; then echo "int(r'${VIASH_PAR_CHIMMULTIMAPNMAX//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimMultimapScoreRange': $( if [ ! -z ${VIASH_PAR_CHIMMULTIMAPSCORERANGE+x} ]; then echo "int(r'${VIASH_PAR_CHIMMULTIMAPSCORERANGE//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimNonchimScoreDropMin': $( if [ ! -z ${VIASH_PAR_CHIMNONCHIMSCOREDROPMIN+x} ]; then echo "int(r'${VIASH_PAR_CHIMNONCHIMSCOREDROPMIN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'chimOutJunctionFormat': $( if [ ! -z ${VIASH_PAR_CHIMOUTJUNCTIONFORMAT+x} ]; then echo "int(r'${VIASH_PAR_CHIMOUTJUNCTIONFORMAT//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'quantMode': $( if [ ! -z ${VIASH_PAR_QUANTMODE+x} ]; then echo "r'${VIASH_PAR_QUANTMODE//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'quantTranscriptomeBAMcompression': $( if [ ! -z ${VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION+x} ]; then echo "int(r'${VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'quantTranscriptomeSAMoutput': $( if [ ! -z ${VIASH_PAR_QUANTTRANSCRIPTOMESAMOUTPUT+x} ]; then echo "r'${VIASH_PAR_QUANTTRANSCRIPTOMESAMOUTPUT//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'twopassMode': $( if [ ! -z ${VIASH_PAR_TWOPASSMODE+x} ]; then echo "r'${VIASH_PAR_TWOPASSMODE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'twopass1readsN': $( if [ ! -z ${VIASH_PAR_TWOPASS1READSN+x} ]; then echo "int(r'${VIASH_PAR_TWOPASS1READSN//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'waspOutputMode': $( if [ ! -z ${VIASH_PAR_WASPOUTPUTMODE+x} ]; then echo "r'${VIASH_PAR_WASPOUTPUTMODE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'input': $( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "r'${VIASH_PAR_INPUT//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'input_r2': $( if [ ! -z ${VIASH_PAR_INPUT_R2+x} ]; then echo "r'${VIASH_PAR_INPUT_R2//\'/\'\"\'\"r\'}'.split(';')"; else echo None; fi ), + 'aligned_reads': $( if [ ! -z ${VIASH_PAR_ALIGNED_READS+x} ]; then echo "r'${VIASH_PAR_ALIGNED_READS//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'reads_per_gene': $( if [ ! -z ${VIASH_PAR_READS_PER_GENE+x} ]; then echo "r'${VIASH_PAR_READS_PER_GENE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'unmapped': $( if [ ! -z ${VIASH_PAR_UNMAPPED+x} ]; then echo "r'${VIASH_PAR_UNMAPPED//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'unmapped_r2': $( if [ ! -z ${VIASH_PAR_UNMAPPED_R2+x} ]; then echo "r'${VIASH_PAR_UNMAPPED_R2//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'chimeric_junctions': $( if [ ! -z ${VIASH_PAR_CHIMERIC_JUNCTIONS+x} ]; then echo "r'${VIASH_PAR_CHIMERIC_JUNCTIONS//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'log': $( if [ ! -z ${VIASH_PAR_LOG+x} ]; then echo "r'${VIASH_PAR_LOG//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'splice_junctions': $( if [ ! -z ${VIASH_PAR_SPLICE_JUNCTIONS+x} ]; then echo "r'${VIASH_PAR_SPLICE_JUNCTIONS//\'/\'\"\'\"r\'}'"; else echo None; fi ) +} +meta = { + 'name': $( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "r'${VIASH_META_NAME//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'functionality_name': $( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "r'${VIASH_META_FUNCTIONALITY_NAME//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'resources_dir': $( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "r'${VIASH_META_RESOURCES_DIR//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'executable': $( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "r'${VIASH_META_EXECUTABLE//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'config': $( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "r'${VIASH_META_CONFIG//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'temp_dir': $( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "r'${VIASH_META_TEMP_DIR//\'/\'\"\'\"r\'}'"; else echo None; fi ), + 'cpus': $( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "int(r'${VIASH_META_CPUS//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'memory_b': $( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "int(r'${VIASH_META_MEMORY_B//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'memory_kb': $( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "int(r'${VIASH_META_MEMORY_KB//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'memory_mb': $( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "int(r'${VIASH_META_MEMORY_MB//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'memory_gb': $( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "int(r'${VIASH_META_MEMORY_GB//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'memory_tb': $( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "int(r'${VIASH_META_MEMORY_TB//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'memory_pb': $( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "int(r'${VIASH_META_MEMORY_PB//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'memory_kib': $( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "int(r'${VIASH_META_MEMORY_KIB//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'memory_mib': $( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "int(r'${VIASH_META_MEMORY_MIB//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'memory_gib': $( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "int(r'${VIASH_META_MEMORY_GIB//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'memory_tib': $( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "int(r'${VIASH_META_MEMORY_TIB//\'/\'\"\'\"r\'}')"; else echo None; fi ), + 'memory_pib': $( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "int(r'${VIASH_META_MEMORY_PIB//\'/\'\"\'\"r\'}')"; else echo None; fi ) +} +dep = { + +} + +## VIASH END + +################################################## +# check and process SE / PE R1 input files +input_r1 = par["input"] +readFilesIn = ",".join(par["input"]) +par["input"] = None + +# check and process PE R2 input files +input_r2 = par["input_r2"] +if input_r2 is not None: + if len(input_r1) != len(input_r2): + raise ValueError("The number of R1 and R2 files do not match.") + readFilesIn = [readFilesIn, ",".join(par["input_r2"])] + par["input_r2"] = None + +# store readFilesIn +par["readFilesIn"] = readFilesIn + +################################################## + +# determine readFilesCommand +if input_r1[0].endswith(".gz"): + print(">> Input files are gzipped, setting readFilesCommand to zcat", flush=True) + par["readFilesCommand"] = "zcat" +elif input_r1[0].endswith(".bz2"): + print(">> Input files are bzipped, setting readFilesCommand to bzcat", flush=True) + par["readFilesCommand"] = "bzcat" + +################################################## +# store output paths +expected_outputs = { + "aligned_reads": ["Aligned.out.sam", "Aligned.out.bam"], + "reads_per_gene": "ReadsPerGene.out.tab", + "chimeric_junctions": "Chimeric.out.junction", + "log": "Log.final.out", + "splice_junctions": "SJ.out.tab", + "unmapped": "Unmapped.out.mate1", + "unmapped_r2": "Unmapped.out.mate2" +} +output_paths = {name: par[name] for name in expected_outputs.keys()} +for name in expected_outputs.keys(): + par[name] = None + +################################################## +# process other args +par["runMode"] = "alignReads" + +if "cpus" in meta and meta["cpus"]: + par["runThreadN"] = meta["cpus"] + +################################################## +# run STAR and move output to final destination +with tempfile.TemporaryDirectory(prefix="star-", dir=meta["temp_dir"], ignore_cleanup_errors=True) as temp_dir: + print(">> Constructing command", flush=True) + + # set output paths + temp_dir = Path(temp_dir) + par["outTmpDir"] = temp_dir / "tempdir" + out_dir = temp_dir / "out" + par["outFileNamePrefix"] = f"{out_dir}/" # star needs this slash + + # construct command + cmd_args = [ "STAR" ] + for name, value in par.items(): + if value is not None: + val_to_add = value if isinstance(value, list) else [value] + cmd_args.extend([f"--{name}"] + [str(x) for x in val_to_add]) + print("", flush=True) + + # run command + print(">> Running STAR with command:", flush=True) + print(f"+ {' '.join(cmd_args)}", end="\\n\\n", flush=True) + subprocess.run( + cmd_args, + check=True + ) + print(">> STAR finished successfully", end="\\n\\n", flush=True) + + # move output to final destination + print(">> Moving output to final destination", flush=True) + for name, paths in expected_outputs.items(): + for expected_path in [paths] if isinstance(paths, str) else paths: + expected_full_path = out_dir / expected_path + if output_paths[name] and expected_full_path.is_file(): + print(f">> Moving {expected_path} to {output_paths[name]}", flush=True) + shutil.move(expected_full_path, output_paths[name]) +VIASHMAIN +python -B "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_GENOMEDIR" ]; then + VIASH_PAR_GENOMEDIR=$(ViashDockerStripAutomount "$VIASH_PAR_GENOMEDIR") + fi + if [ ! -z "$VIASH_PAR_GENOMEFASTAFILES" ]; then + unset VIASH_TEST_GENOMEFASTAFILES + IFS=';' + for var in $VIASH_PAR_GENOMEFASTAFILES; do + unset IFS + if [ -z "$VIASH_TEST_GENOMEFASTAFILES" ]; then + VIASH_TEST_GENOMEFASTAFILES="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_GENOMEFASTAFILES="$VIASH_TEST_GENOMEFASTAFILES;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_GENOMEFASTAFILES="$VIASH_TEST_GENOMEFASTAFILES" + fi + if [ ! -z "$VIASH_PAR_SJDBGTFFILE" ]; then + VIASH_PAR_SJDBGTFFILE=$(ViashDockerStripAutomount "$VIASH_PAR_SJDBGTFFILE") + fi + if [ ! -z "$VIASH_PAR_READFILESMANIFEST" ]; then + VIASH_PAR_READFILESMANIFEST=$(ViashDockerStripAutomount "$VIASH_PAR_READFILESMANIFEST") + fi + if [ ! -z "$VIASH_PAR_INPUT" ]; then + unset VIASH_TEST_INPUT + IFS=';' + for var in $VIASH_PAR_INPUT; do + unset IFS + if [ -z "$VIASH_TEST_INPUT" ]; then + VIASH_TEST_INPUT="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_INPUT="$VIASH_TEST_INPUT;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_INPUT="$VIASH_TEST_INPUT" + fi + if [ ! -z "$VIASH_PAR_INPUT_R2" ]; then + unset VIASH_TEST_INPUT_R2 + IFS=';' + for var in $VIASH_PAR_INPUT_R2; do + unset IFS + if [ -z "$VIASH_TEST_INPUT_R2" ]; then + VIASH_TEST_INPUT_R2="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_INPUT_R2="$VIASH_TEST_INPUT_R2;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_INPUT_R2="$VIASH_TEST_INPUT_R2" + fi + if [ ! -z "$VIASH_PAR_ALIGNED_READS" ]; then + VIASH_PAR_ALIGNED_READS=$(ViashDockerStripAutomount "$VIASH_PAR_ALIGNED_READS") + fi + if [ ! -z "$VIASH_PAR_READS_PER_GENE" ]; then + VIASH_PAR_READS_PER_GENE=$(ViashDockerStripAutomount "$VIASH_PAR_READS_PER_GENE") + fi + if [ ! -z "$VIASH_PAR_UNMAPPED" ]; then + VIASH_PAR_UNMAPPED=$(ViashDockerStripAutomount "$VIASH_PAR_UNMAPPED") + fi + if [ ! -z "$VIASH_PAR_UNMAPPED_R2" ]; then + VIASH_PAR_UNMAPPED_R2=$(ViashDockerStripAutomount "$VIASH_PAR_UNMAPPED_R2") + fi + if [ ! -z "$VIASH_PAR_CHIMERIC_JUNCTIONS" ]; then + VIASH_PAR_CHIMERIC_JUNCTIONS=$(ViashDockerStripAutomount "$VIASH_PAR_CHIMERIC_JUNCTIONS") + fi + if [ ! -z "$VIASH_PAR_LOG" ]; then + VIASH_PAR_LOG=$(ViashDockerStripAutomount "$VIASH_PAR_LOG") + fi + if [ ! -z "$VIASH_PAR_SPLICE_JUNCTIONS" ]; then + VIASH_PAR_SPLICE_JUNCTIONS=$(ViashDockerStripAutomount "$VIASH_PAR_SPLICE_JUNCTIONS") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_ALIGNED_READS" ] && [ ! -e "$VIASH_PAR_ALIGNED_READS" ]; then + ViashError "Output file '$VIASH_PAR_ALIGNED_READS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_READS_PER_GENE" ] && [ ! -e "$VIASH_PAR_READS_PER_GENE" ]; then + ViashError "Output file '$VIASH_PAR_READS_PER_GENE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_UNMAPPED" ] && [ ! -e "$VIASH_PAR_UNMAPPED" ]; then + ViashError "Output file '$VIASH_PAR_UNMAPPED' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_UNMAPPED_R2" ] && [ ! -e "$VIASH_PAR_UNMAPPED_R2" ]; then + ViashError "Output file '$VIASH_PAR_UNMAPPED_R2' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_CHIMERIC_JUNCTIONS" ] && [ ! -e "$VIASH_PAR_CHIMERIC_JUNCTIONS" ]; then + ViashError "Output file '$VIASH_PAR_CHIMERIC_JUNCTIONS' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_LOG" ] && [ ! -e "$VIASH_PAR_LOG" ]; then + ViashError "Output file '$VIASH_PAR_LOG' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_SPLICE_JUNCTIONS" ] && [ ! -e "$VIASH_PAR_SPLICE_JUNCTIONS" ]; then + ViashError "Output file '$VIASH_PAR_SPLICE_JUNCTIONS' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/executable/star/star_genome_generate/.config.vsh.yaml b/target/executable/star/star_genome_generate/.config.vsh.yaml new file mode 100644 index 00000000..10558d7e --- /dev/null +++ b/target/executable/star/star_genome_generate/.config.vsh.yaml @@ -0,0 +1,345 @@ +name: "star_genome_generate" +namespace: "star" +version: "main" +argument_groups: +- name: "Input" + arguments: + - type: "file" + name: "--genomeFastaFiles" + description: "Path(s) to the fasta files with the genome sequences, separated\ + \ by spaces. These files should be plain text FASTA files, they *cannot* be\ + \ zipped.\n" + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--sjdbGTFfile" + description: "Path to the GTF file with annotations" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--sjdbOverhang" + description: "Length of the donor/acceptor sequence on each side of the junctions,\ + \ ideally = (mate_length - 1)" + info: null + example: + - 100 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFchrPrefix" + description: "Prefix for chromosome names in a GTF file (e.g. 'chr' for using\ + \ ENSMEBL annotations with UCSC genomes)" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFfeatureExon" + description: "Feature type in GTF file to be used as exons for building transcripts" + info: null + example: + - "exon" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentTranscript" + description: "GTF attribute name for parent transcript ID (default \"transcript_id\"\ + \ works for GTF files)" + info: null + example: + - "transcript_id" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGene" + description: "GTF attribute name for parent gene ID (default \"gene_id\" works\ + \ for GTF files)" + info: null + example: + - "gene_id" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGeneName" + description: "GTF attribute name for parent gene name" + info: null + example: + - "gene_name" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGeneType" + description: "GTF attribute name for parent gene type" + info: null + example: + - "gene_type" + - "gene_biotype" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "long" + name: "--limitGenomeGenerateRAM" + description: "Maximum available RAM (bytes) for genome generation" + info: null + example: + - 31000000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--genomeSAindexNbases" + description: "Length (bases) of the SA pre-indexing string. Typically between\ + \ 10 and 15. Longer strings will use much more memory, but allow faster searches.\ + \ For small genomes, this parameter must be scaled down to min(14, log2(GenomeLength)/2\ + \ - 1)." + info: null + example: + - 14 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--genomeChrBinNbits" + description: "Defined as log2(chrBin), where chrBin is the size of the bins for\ + \ genome storage. Each chromosome will occupy an integer number of bins. For\ + \ a genome with large number of contigs, it is recommended to scale this parameter\ + \ as min(18, log2[max(GenomeLength/NumberOfReferences,ReadLength)])." + info: null + example: + - 18 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--genomeSAsparseD" + description: "Suffux array sparsity, i.e. distance between indices. Use bigger\ + \ numbers to decrease needed RAM at the cost of mapping speed reduction." + info: null + example: + - 1 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--genomeSuffixLengthMax" + description: "Maximum length of the suffixes, has to be longer than read length.\ + \ Use -1 for infinite length." + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--genomeTransformType" + description: "Type of genome transformation\n None ... no transformation\n\ + \ Haploid ... replace reference alleles with alternative alleles from VCF\ + \ file (e.g. consensus allele)\n Diploid ... create two haplotypes for each\ + \ chromosome listed in VCF file, for genotypes 1|2, assumes perfect phasing\ + \ (e.g. personal genome)\n" + info: null + example: + - "None" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--genomeTransformVCF" + description: "path to VCF file for genome transformation" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output" + arguments: + - type: "file" + name: "--index" + description: "STAR index directory." + info: null + default: + - "STAR_index" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Create index for STAR\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "genome" +- "index" +- "align" +license: "MIT" +references: + doi: + - "10.1093/bioinformatics/bts635" +links: + repository: "https://github.com/alexdobin/STAR" + documentation: "https://github.com/alexdobin/STAR/blob/master/doc/STARmanual.pdf" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "ubuntu:22.04" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "apt-get update && \\\n apt-get install -y --no-install-recommends ${PACKAGES}\ + \ && \\\n cd /tmp && \\\n wget --no-check-certificate https://github.com/alexdobin/STAR/archive/refs/tags/${STAR_VERSION}.zip\ + \ && \\\n unzip ${STAR_VERSION}.zip && \\\n cd STAR-${STAR_VERSION}/source\ + \ && \\\n make STARstatic CXXFLAGS_SIMD=-std=c++11 && \\\n cp STAR /usr/local/bin\ + \ && \\\n cd / && \\\n rm -rf /tmp/STAR-${STAR_VERSION} /tmp/${STAR_VERSION}.zip\ + \ && \\\n apt-get --purge autoremove -y ${PACKAGES} && \\\n apt-get clean\n" + env: + - "STAR_VERSION 2.7.11b" + - "PACKAGES gcc g++ make wget zlib1g-dev unzip xxd" + - type: "docker" + run: + - "STAR --version | sed 's#\\(.*\\)#star: \"\\1\"#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/star/star_genome_generate/config.vsh.yaml" + runner: "executable" + engine: "docker|native" + output: "target/executable/star/star_genome_generate" + executable: "target/executable/star/star_genome_generate/star_genome_generate" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/executable/star/star_genome_generate/star_genome_generate b/target/executable/star/star_genome_generate/star_genome_generate new file mode 100755 index 00000000..61865f20 --- /dev/null +++ b/target/executable/star/star_genome_generate/star_genome_generate @@ -0,0 +1,1462 @@ +#!/usr/bin/env bash + +# star_genome_generate main +# +# This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +# derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +# Data Intuitive. +# +# The component may contain files which fall under a different license. The +# authors of this component should specify the license in the header of such +# files, or include a separate license file detailing the licenses of all included +# files. + +set -e + +if [ -z "$VIASH_TEMP" ]; then + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$VIASH_TMP} + VIASH_TEMP=${VIASH_TEMP:-$TMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TMP} + VIASH_TEMP=${VIASH_TEMP:-$TEMPDIR} + VIASH_TEMP=${VIASH_TEMP:-$TEMP} + VIASH_TEMP=${VIASH_TEMP:-/tmp} +fi + +# define helper functions +# ViashQuote: put quotes around non flag values +# $1 : unquoted string +# return : possibly quoted string +# examples: +# ViashQuote --foo # returns --foo +# ViashQuote bar # returns 'bar' +# Viashquote --foo=bar # returns --foo='bar' +function ViashQuote { + if [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+=.+$ ]]; then + echo "$1" | sed "s#=\(.*\)#='\1'#" + elif [[ "$1" =~ ^-+[a-zA-Z0-9_\-]+$ ]]; then + echo "$1" + else + echo "'$1'" + fi +} +# ViashRemoveFlags: Remove leading flag +# $1 : string with a possible leading flag +# return : string without possible leading flag +# examples: +# ViashRemoveFlags --foo=bar # returns bar +function ViashRemoveFlags { + echo "$1" | sed 's/^--*[a-zA-Z0-9_\-]*=//' +} +# ViashSourceDir: return the path of a bash file, following symlinks +# usage : ViashSourceDir ${BASH_SOURCE[0]} +# $1 : Should always be set to ${BASH_SOURCE[0]} +# returns : The absolute path of the bash file +function ViashSourceDir { + SOURCE="$1" + while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + done + cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd +} +# ViashFindTargetDir: return the path of the '.build.yaml' file, following symlinks +# usage : ViashFindTargetDir 'ScriptPath' +# $1 : The location from where to start the upward search +# returns : The absolute path of the '.build.yaml' file +function ViashFindTargetDir { + SOURCE="$1" + while [[ "$SOURCE" != "" && ! -e "$SOURCE/.build.yaml" ]]; do + SOURCE=${SOURCE%/*} + done + echo $SOURCE +} +# see https://en.wikipedia.org/wiki/Syslog#Severity_level +VIASH_LOGCODE_EMERGENCY=0 +VIASH_LOGCODE_ALERT=1 +VIASH_LOGCODE_CRITICAL=2 +VIASH_LOGCODE_ERROR=3 +VIASH_LOGCODE_WARNING=4 +VIASH_LOGCODE_NOTICE=5 +VIASH_LOGCODE_INFO=6 +VIASH_LOGCODE_DEBUG=7 +VIASH_VERBOSITY=$VIASH_LOGCODE_NOTICE + +# ViashLog: Log events depending on the verbosity level +# usage: ViashLog 1 alert Oh no something went wrong! +# $1: required verbosity level +# $2: display tag +# $3+: messages to display +# stdout: Your input, prepended by '[$2] '. +function ViashLog { + local required_level="$1" + local display_tag="$2" + shift 2 + if [ $VIASH_VERBOSITY -ge $required_level ]; then + >&2 echo "[$display_tag]" "$@" + fi +} + +# ViashEmergency: log events when the system is unstable +# usage: ViashEmergency Oh no something went wrong. +# stdout: Your input, prepended by '[emergency] '. +function ViashEmergency { + ViashLog $VIASH_LOGCODE_EMERGENCY emergency "$@" +} + +# ViashAlert: log events when actions must be taken immediately (e.g. corrupted system database) +# usage: ViashAlert Oh no something went wrong. +# stdout: Your input, prepended by '[alert] '. +function ViashAlert { + ViashLog $VIASH_LOGCODE_ALERT alert "$@" +} + +# ViashCritical: log events when a critical condition occurs +# usage: ViashCritical Oh no something went wrong. +# stdout: Your input, prepended by '[critical] '. +function ViashCritical { + ViashLog $VIASH_LOGCODE_CRITICAL critical "$@" +} + +# ViashError: log events when an error condition occurs +# usage: ViashError Oh no something went wrong. +# stdout: Your input, prepended by '[error] '. +function ViashError { + ViashLog $VIASH_LOGCODE_ERROR error "$@" +} + +# ViashWarning: log potentially abnormal events +# usage: ViashWarning Something may have gone wrong. +# stdout: Your input, prepended by '[warning] '. +function ViashWarning { + ViashLog $VIASH_LOGCODE_WARNING warning "$@" +} + +# ViashNotice: log significant but normal events +# usage: ViashNotice This just happened. +# stdout: Your input, prepended by '[notice] '. +function ViashNotice { + ViashLog $VIASH_LOGCODE_NOTICE notice "$@" +} + +# ViashInfo: log normal events +# usage: ViashInfo This just happened. +# stdout: Your input, prepended by '[info] '. +function ViashInfo { + ViashLog $VIASH_LOGCODE_INFO info "$@" +} + +# ViashDebug: log all events, for debugging purposes +# usage: ViashDebug This just happened. +# stdout: Your input, prepended by '[debug] '. +function ViashDebug { + ViashLog $VIASH_LOGCODE_DEBUG debug "$@" +} + +# find source folder of this component +VIASH_META_RESOURCES_DIR=`ViashSourceDir ${BASH_SOURCE[0]}` + +# find the root of the built components & dependencies +VIASH_TARGET_DIR=`ViashFindTargetDir $VIASH_META_RESOURCES_DIR` + +# define meta fields +VIASH_META_NAME="star_genome_generate" +VIASH_META_FUNCTIONALITY_NAME="star_genome_generate" +VIASH_META_EXECUTABLE="$VIASH_META_RESOURCES_DIR/$VIASH_META_NAME" +VIASH_META_CONFIG="$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" +VIASH_META_TEMP_DIR="$VIASH_TEMP" + + +# ViashHelp: Display helpful explanation about this executable +function ViashHelp { + echo "star_genome_generate main" + echo "" + echo "Create index for STAR" + echo "" + echo "Input:" + echo " --genomeFastaFiles" + echo " type: file, required parameter, multiple values allowed, file must exist" + echo " Path(s) to the fasta files with the genome sequences, separated by" + echo " spaces. These files should be plain text FASTA files, they *cannot* be" + echo " zipped." + echo "" + echo " --sjdbGTFfile" + echo " type: file, file must exist" + echo " Path to the GTF file with annotations" + echo "" + echo " --sjdbOverhang" + echo " type: integer" + echo " example: 100" + echo " Length of the donor/acceptor sequence on each side of the junctions," + echo " ideally = (mate_length - 1)" + echo "" + echo " --sjdbGTFchrPrefix" + echo " type: string" + echo " Prefix for chromosome names in a GTF file (e.g. 'chr' for using ENSMEBL" + echo " annotations with UCSC genomes)" + echo "" + echo " --sjdbGTFfeatureExon" + echo " type: string" + echo " example: exon" + echo " Feature type in GTF file to be used as exons for building transcripts" + echo "" + echo " --sjdbGTFtagExonParentTranscript" + echo " type: string" + echo " example: transcript_id" + echo " GTF attribute name for parent transcript ID (default \"transcript_id\"" + echo " works for GTF files)" + echo "" + echo " --sjdbGTFtagExonParentGene" + echo " type: string" + echo " example: gene_id" + echo " GTF attribute name for parent gene ID (default \"gene_id\" works for GTF" + echo " files)" + echo "" + echo " --sjdbGTFtagExonParentGeneName" + echo " type: string, multiple values allowed" + echo " example: gene_name" + echo " GTF attribute name for parent gene name" + echo "" + echo " --sjdbGTFtagExonParentGeneType" + echo " type: string, multiple values allowed" + echo " example: gene_type;gene_biotype" + echo " GTF attribute name for parent gene type" + echo "" + echo " --limitGenomeGenerateRAM" + echo " type: long" + echo " example: 31000000000" + echo " Maximum available RAM (bytes) for genome generation" + echo "" + echo " --genomeSAindexNbases" + echo " type: integer" + echo " example: 14" + echo " Length (bases) of the SA pre-indexing string. Typically between 10 and" + echo " 15. Longer strings will use much more memory, but allow faster searches." + echo " For small genomes, this parameter must be scaled down to min(14," + echo " log2(GenomeLength)/2 - 1)." + echo "" + echo " --genomeChrBinNbits" + echo " type: integer" + echo " example: 18" + echo " Defined as log2(chrBin), where chrBin is the size of the bins for genome" + echo " storage. Each chromosome will occupy an integer number of bins. For a" + echo " genome with large number of contigs, it is recommended to scale this" + echo " parameter as min(18," + echo " log2[max(GenomeLength/NumberOfReferences,ReadLength)])." + echo "" + echo " --genomeSAsparseD" + echo " type: integer" + echo " example: 1" + echo " min: 0" + echo " Suffux array sparsity, i.e. distance between indices. Use bigger numbers" + echo " to decrease needed RAM at the cost of mapping speed reduction." + echo "" + echo " --genomeSuffixLengthMax" + echo " type: integer" + echo " example: -1" + echo " Maximum length of the suffixes, has to be longer than read length. Use" + echo " -1 for infinite length." + echo "" + echo " --genomeTransformType" + echo " type: string" + echo " example: None" + echo " Type of genome transformation" + echo " None ... no transformation" + echo " Haploid ... replace reference alleles with alternative alleles from" + echo " VCF file (e.g. consensus allele)" + echo " Diploid ... create two haplotypes for each chromosome listed in VCF" + echo " file, for genotypes 1|2, assumes perfect phasing (e.g. personal genome)" + echo "" + echo " --genomeTransformVCF" + echo " type: file, file must exist" + echo " path to VCF file for genome transformation" + echo "" + echo "Output:" + echo " --index" + echo " type: file, required parameter, output, file must exist" + echo " default: STAR_index" + echo " STAR index directory." +} + +# initialise variables +VIASH_MODE='run' +VIASH_ENGINE_ID='docker' + +######## Helper functions for setting up Docker images for viash ######## +# expects: ViashDockerBuild + +# ViashDockerInstallationCheck: check whether Docker is installed correctly +# +# examples: +# ViashDockerInstallationCheck +function ViashDockerInstallationCheck { + ViashDebug "Checking whether Docker is installed" + if [ ! command -v docker &> /dev/null ]; then + ViashCritical "Docker doesn't seem to be installed. See 'https://docs.docker.com/get-docker/' for instructions." + exit 1 + fi + + ViashDebug "Checking whether the Docker daemon is running" + save=$-; set +e + docker_version=$(docker version --format '{{.Client.APIVersion}}' 2> /dev/null) + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashCritical "Docker daemon does not seem to be running. Try one of the following:" + ViashCritical "- Try running 'dockerd' in the command line" + ViashCritical "- See https://docs.docker.com/config/daemon/" + exit 1 + fi +} + +# ViashDockerRemoteTagCheck: check whether a Docker image is available +# on a remote. Assumes `docker login` has been performed, if relevant. +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerRemoteTagCheck python:latest +# echo $? # returns '0' +# ViashDockerRemoteTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerRemoteTagCheck { + docker manifest inspect $1 > /dev/null 2> /dev/null +} + +# ViashDockerLocalTagCheck: check whether a Docker image is available locally +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# docker pull python:latest +# ViashDockerLocalTagCheck python:latest +# echo $? # returns '0' +# ViashDockerLocalTagCheck sdaizudceahifu +# echo $? # returns '1' +function ViashDockerLocalTagCheck { + [ -n "$(docker images -q $1)" ] +} + +# ViashDockerPull: pull a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPull python:latest +# echo $? # returns '0' +# ViashDockerPull sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPull { + ViashNotice "Checking if Docker image is available at '$1'" + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker pull $1 && return 0 || return 1 + else + save=$-; set +e + docker pull $1 2> /dev/null > /dev/null + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashWarning "Could not pull from '$1'. Docker image doesn't exist or is not accessible." + fi + return $out + fi +} + +# ViashDockerPush: push a Docker image +# +# $1 : image identifier with format `[registry/]image[:tag]` +# exit code $? : whether or not the image was found +# examples: +# ViashDockerPush python:latest +# echo $? # returns '0' +# ViashDockerPush sdaizudceahifu +# echo $? # returns '1' +function ViashDockerPush { + ViashNotice "Pushing image to '$1'" + save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + docker push $1 + out=$? + else + docker push $1 2> /dev/null > /dev/null + out=$? + fi + [[ $save =~ e ]] && set -e + if [ $out -eq 0 ]; then + ViashNotice "Container '$1' push succeeded." + else + ViashError "Container '$1' push errored. You might not be logged in or have the necessary permissions." + fi + return $out +} + +# ViashDockerPullElseBuild: pull a Docker image, else build it +# +# $1 : image identifier with format `[registry/]image[:tag]` +# ViashDockerBuild : a Bash function which builds a docker image, takes image identifier as argument. +# examples: +# ViashDockerPullElseBuild mynewcomponent +function ViashDockerPullElseBuild { + save=$-; set +e + ViashDockerPull $1 + out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashDockerBuild $@ + fi +} + +# ViashDockerSetup: create a Docker image, according to specified docker setup strategy +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $2 : docker setup strategy, see DockerSetupStrategy.scala +# examples: +# ViashDockerSetup mynewcomponent alwaysbuild +function ViashDockerSetup { + local image_id="$1" + local setup_strategy="$2" + if [ "$setup_strategy" == "alwaysbuild" -o "$setup_strategy" == "build" -o "$setup_strategy" == "b" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspull" -o "$setup_strategy" == "pull" -o "$setup_strategy" == "p" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "alwayspullelsebuild" -o "$setup_strategy" == "pullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayspullelsecachedbuild" -o "$setup_strategy" == "pullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "alwayscachedbuild" -o "$setup_strategy" == "cachedbuild" -o "$setup_strategy" == "cb" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [[ "$setup_strategy" =~ ^ifneedbe ]]; then + local save=$-; set +e + ViashDockerLocalTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashInfo "Image $image_id already exists" + elif [ "$setup_strategy" == "ifneedbebuild" ]; then + ViashDockerBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbecachedbuild" ]; then + ViashDockerBuild $image_id $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepull" ]; then + ViashDockerPull $image_id + elif [ "$setup_strategy" == "ifneedbepullelsebuild" ]; then + ViashDockerPullElseBuild $image_id --no-cache $(ViashDockerBuildArgs "$engine_id") + elif [ "$setup_strategy" == "ifneedbepullelsecachedbuild" ]; then + ViashDockerPullElseBuild $image_id $(ViashDockerBuildArgs "$engine_id") + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi + elif [ "$setup_strategy" == "push" -o "$setup_strategy" == "forcepush" -o "$setup_strategy" == "alwayspush" ]; then + ViashDockerPush "$image_id" + elif [ "$setup_strategy" == "pushifnotpresent" -o "$setup_strategy" == "gentlepush" -o "$setup_strategy" == "maybepush" ]; then + local save=$-; set +e + ViashDockerRemoteTagCheck $image_id + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -eq 0 ]; then + ViashNotice "Container '$image_id' exists, doing nothing." + else + ViashNotice "Container '$image_id' does not yet exist." + ViashDockerPush "$image_id" + fi + elif [ "$setup_strategy" == "donothing" -o "$setup_strategy" == "meh" ]; then + ViashNotice "Skipping setup." + else + ViashError "Unrecognised Docker strategy: $setup_strategy" + exit 1 + fi +} + +# ViashDockerCheckCommands: Check whether a docker container has the required commands +# +# $1 : image identifier with format `[registry/]image[:tag]` +# $@ : commands to verify being present +# examples: +# ViashDockerCheckCommands bash:4.0 bash ps foo +function ViashDockerCheckCommands { + local image_id="$1" + shift 1 + local commands="$@" + local save=$-; set +e + local missing # mark 'missing' as local in advance, otherwise the exit code of the command will be missing and always be '0' + missing=$(docker run --rm --entrypoint=sh "$image_id" -c "for command in $commands; do command -v \$command >/dev/null 2>&1; if [ \$? -ne 0 ]; then echo \$command; exit 1; fi; done") + local outCheck=$? + [[ $save =~ e ]] && set -e + if [ $outCheck -ne 0 ]; then + ViashError "Docker container '$image_id' does not contain command '$missing'." + exit 1 + fi +} + +# ViashDockerBuild: build a docker image +# $1 : image identifier with format `[registry/]image[:tag]` +# $... : additional arguments to pass to docker build +# $VIASH_META_TEMP_DIR : temporary directory to store dockerfile & optional resources in +# $VIASH_META_NAME : name of the component +# $VIASH_META_RESOURCES_DIR : directory containing the resources +# $VIASH_VERBOSITY : verbosity level +# exit code $? : whether or not the image was built successfully +function ViashDockerBuild { + local image_id="$1" + shift 1 + + # create temporary directory to store dockerfile & optional resources in + local tmpdir=$(mktemp -d "$VIASH_META_TEMP_DIR/dockerbuild-$VIASH_META_NAME-XXXXXX") + local dockerfile="$tmpdir/Dockerfile" + function clean_up { + rm -rf "$tmpdir" + } + trap clean_up EXIT + + # store dockerfile and resources + ViashDockerfile "$VIASH_ENGINE_ID" > "$dockerfile" + + # generate the build command + local docker_build_cmd="docker build -t '$image_id' $@ '$VIASH_META_RESOURCES_DIR' -f '$dockerfile'" + + # build the container + ViashNotice "Building container '$image_id' with Dockerfile" + ViashInfo "$docker_build_cmd" + local save=$-; set +e + if [ $VIASH_VERBOSITY -ge $VIASH_LOGCODE_INFO ]; then + eval $docker_build_cmd + else + eval $docker_build_cmd &> "$tmpdir/docker_build.log" + fi + + # check exit code + local out=$? + [[ $save =~ e ]] && set -e + if [ $out -ne 0 ]; then + ViashError "Error occurred while building container '$image_id'" + if [ $VIASH_VERBOSITY -lt $VIASH_LOGCODE_INFO ]; then + ViashError "Transcript: --------------------------------" + cat "$tmpdir/docker_build.log" + ViashError "End of transcript --------------------------" + fi + exit 1 + fi +} + +######## End of helper functions for setting up Docker images for viash ######## + +# ViashDockerFile: print the dockerfile to stdout +# $1 : engine identifier +# return : dockerfile required to run this component +# examples: +# ViashDockerFile +function ViashDockerfile { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + cat << 'VIASHDOCKER' +FROM ubuntu:22.04 +ENTRYPOINT [] +ENV STAR_VERSION 2.7.11b +ENV PACKAGES gcc g++ make wget zlib1g-dev unzip xxd +RUN apt-get update && \ + apt-get install -y --no-install-recommends ${PACKAGES} && \ + cd /tmp && \ + wget --no-check-certificate https://github.com/alexdobin/STAR/archive/refs/tags/${STAR_VERSION}.zip && \ + unzip ${STAR_VERSION}.zip && \ + cd STAR-${STAR_VERSION}/source && \ + make STARstatic CXXFLAGS_SIMD=-std=c++11 && \ + cp STAR /usr/local/bin && \ + cd / && \ + rm -rf /tmp/STAR-${STAR_VERSION} /tmp/${STAR_VERSION}.zip && \ + apt-get --purge autoremove -y ${PACKAGES} && \ + apt-get clean + +RUN STAR --version | sed 's#\(.*\)#star: "\1"#' > /var/software_versions.txt + +LABEL org.opencontainers.image.description="Companion container for running component star star_genome_generate" +LABEL org.opencontainers.image.created="2024-06-24T08:36:46Z" +LABEL org.opencontainers.image.source="https://github.com/alexdobin/STAR" +LABEL org.opencontainers.image.revision="d0c648fb7eefe067f5b5b3d402a204354bb37198" +LABEL org.opencontainers.image.version="main" + +VIASHDOCKER + fi +} + +# ViashDockerBuildArgs: return the arguments to pass to docker build +# $1 : engine identifier +# return : arguments to pass to docker build +function ViashDockerBuildArgs { + local engine_id="$1" + + if [[ "$engine_id" == "docker" ]]; then + echo "" + fi +} + +# ViashAbsolutePath: generate absolute path from relative path +# borrowed from https://stackoverflow.com/a/21951256 +# $1 : relative filename +# return : absolute path +# examples: +# ViashAbsolutePath some_file.txt # returns /path/to/some_file.txt +# ViashAbsolutePath /foo/bar/.. # returns /foo +function ViashAbsolutePath { + local thePath + if [[ ! "$1" =~ ^/ ]]; then + thePath="$PWD/$1" + else + thePath="$1" + fi + echo "$thePath" | ( + IFS=/ + read -a parr + declare -a outp + for i in "${parr[@]}"; do + case "$i" in + ''|.) continue ;; + ..) + len=${#outp[@]} + if ((len==0)); then + continue + else + unset outp[$((len-1))] + fi + ;; + *) + len=${#outp[@]} + outp[$len]="$i" + ;; + esac + done + echo /"${outp[*]}" + ) +} +# ViashDockerAutodetectMount: auto configuring docker mounts from parameters +# $1 : The parameter value +# returns : New parameter +# $VIASH_DIRECTORY_MOUNTS : Added another parameter to be passed to docker +# examples: +# ViashDockerAutodetectMount /path/to/bar # returns '/viash_automount/path/to/bar' +# ViashDockerAutodetectMountArg /path/to/bar # returns '--volume="/path/to:/viash_automount/path/to"' +function ViashDockerAutodetectMount { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + if [ -z "$base_name" ]; then + echo "$mount_target" + else + echo "$mount_target/$base_name" + fi +} +function ViashDockerAutodetectMountArg { + abs_path=$(ViashAbsolutePath "$1") + if [ -d "$abs_path" ]; then + mount_source="$abs_path" + base_name="" + else + mount_source=`dirname "$abs_path"` + base_name=`basename "$abs_path"` + fi + mount_target="/viash_automount$mount_source" + ViashDebug "ViashDockerAutodetectMountArg $1 -> $mount_source -> $mount_target" + echo "--volume=\"$mount_source:$mount_target\"" +} +function ViashDockerStripAutomount { + abs_path=$(ViashAbsolutePath "$1") + echo "${abs_path#/viash_automount}" +} +# initialise variables +VIASH_DIRECTORY_MOUNTS=() + +# initialise docker variables +VIASH_DOCKER_RUN_ARGS=(-i --rm) + +# initialise array +VIASH_POSITIONAL_ARGS='' + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + ViashHelp + exit + ;; + ---v|---verbose) + let "VIASH_VERBOSITY=VIASH_VERBOSITY+1" + shift 1 + ;; + ---verbosity) + VIASH_VERBOSITY="$2" + shift 2 + ;; + ---verbosity=*) + VIASH_VERBOSITY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + --version) + echo "star_genome_generate main" + exit + ;; + --genomeFastaFiles) + if [ -z "$VIASH_PAR_GENOMEFASTAFILES" ]; then + VIASH_PAR_GENOMEFASTAFILES="$2" + else + VIASH_PAR_GENOMEFASTAFILES="$VIASH_PAR_GENOMEFASTAFILES;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeFastaFiles. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeFastaFiles=*) + if [ -z "$VIASH_PAR_GENOMEFASTAFILES" ]; then + VIASH_PAR_GENOMEFASTAFILES=$(ViashRemoveFlags "$1") + else + VIASH_PAR_GENOMEFASTAFILES="$VIASH_PAR_GENOMEFASTAFILES;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --sjdbGTFfile) + [ -n "$VIASH_PAR_SJDBGTFFILE" ] && ViashError Bad arguments for option \'--sjdbGTFfile\': \'$VIASH_PAR_SJDBGTFFILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFFILE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFfile. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFfile=*) + [ -n "$VIASH_PAR_SJDBGTFFILE" ] && ViashError Bad arguments for option \'--sjdbGTFfile=*\': \'$VIASH_PAR_SJDBGTFFILE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFFILE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbOverhang) + [ -n "$VIASH_PAR_SJDBOVERHANG" ] && ViashError Bad arguments for option \'--sjdbOverhang\': \'$VIASH_PAR_SJDBOVERHANG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBOVERHANG="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbOverhang. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbOverhang=*) + [ -n "$VIASH_PAR_SJDBOVERHANG" ] && ViashError Bad arguments for option \'--sjdbOverhang=*\': \'$VIASH_PAR_SJDBOVERHANG\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBOVERHANG=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbGTFchrPrefix) + [ -n "$VIASH_PAR_SJDBGTFCHRPREFIX" ] && ViashError Bad arguments for option \'--sjdbGTFchrPrefix\': \'$VIASH_PAR_SJDBGTFCHRPREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFCHRPREFIX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFchrPrefix. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFchrPrefix=*) + [ -n "$VIASH_PAR_SJDBGTFCHRPREFIX" ] && ViashError Bad arguments for option \'--sjdbGTFchrPrefix=*\': \'$VIASH_PAR_SJDBGTFCHRPREFIX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFCHRPREFIX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbGTFfeatureExon) + [ -n "$VIASH_PAR_SJDBGTFFEATUREEXON" ] && ViashError Bad arguments for option \'--sjdbGTFfeatureExon\': \'$VIASH_PAR_SJDBGTFFEATUREEXON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFFEATUREEXON="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFfeatureExon. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFfeatureExon=*) + [ -n "$VIASH_PAR_SJDBGTFFEATUREEXON" ] && ViashError Bad arguments for option \'--sjdbGTFfeatureExon=*\': \'$VIASH_PAR_SJDBGTFFEATUREEXON\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFFEATUREEXON=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbGTFtagExonParentTranscript) + [ -n "$VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT" ] && ViashError Bad arguments for option \'--sjdbGTFtagExonParentTranscript\': \'$VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFtagExonParentTranscript. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFtagExonParentTranscript=*) + [ -n "$VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT" ] && ViashError Bad arguments for option \'--sjdbGTFtagExonParentTranscript=*\': \'$VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbGTFtagExonParentGene) + [ -n "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENE" ] && ViashError Bad arguments for option \'--sjdbGTFtagExonParentGene\': \'$VIASH_PAR_SJDBGTFTAGEXONPARENTGENE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFTAGEXONPARENTGENE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFtagExonParentGene. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFtagExonParentGene=*) + [ -n "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENE" ] && ViashError Bad arguments for option \'--sjdbGTFtagExonParentGene=*\': \'$VIASH_PAR_SJDBGTFTAGEXONPARENTGENE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_SJDBGTFTAGEXONPARENTGENE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --sjdbGTFtagExonParentGeneName) + if [ -z "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME" ]; then + VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME="$2" + else + VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME="$VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFtagExonParentGeneName. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFtagExonParentGeneName=*) + if [ -z "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME" ]; then + VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME=$(ViashRemoveFlags "$1") + else + VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME="$VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --sjdbGTFtagExonParentGeneType) + if [ -z "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE" ]; then + VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE="$2" + else + VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE="$VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE;""$2" + fi + [ $# -lt 2 ] && ViashError Not enough arguments passed to --sjdbGTFtagExonParentGeneType. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --sjdbGTFtagExonParentGeneType=*) + if [ -z "$VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE" ]; then + VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE=$(ViashRemoveFlags "$1") + else + VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE="$VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE;"$(ViashRemoveFlags "$1") + fi + shift 1 + ;; + --limitGenomeGenerateRAM) + [ -n "$VIASH_PAR_LIMITGENOMEGENERATERAM" ] && ViashError Bad arguments for option \'--limitGenomeGenerateRAM\': \'$VIASH_PAR_LIMITGENOMEGENERATERAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITGENOMEGENERATERAM="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --limitGenomeGenerateRAM. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --limitGenomeGenerateRAM=*) + [ -n "$VIASH_PAR_LIMITGENOMEGENERATERAM" ] && ViashError Bad arguments for option \'--limitGenomeGenerateRAM=*\': \'$VIASH_PAR_LIMITGENOMEGENERATERAM\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_LIMITGENOMEGENERATERAM=$(ViashRemoveFlags "$1") + shift 1 + ;; + --genomeSAindexNbases) + [ -n "$VIASH_PAR_GENOMESAINDEXNBASES" ] && ViashError Bad arguments for option \'--genomeSAindexNbases\': \'$VIASH_PAR_GENOMESAINDEXNBASES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMESAINDEXNBASES="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeSAindexNbases. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeSAindexNbases=*) + [ -n "$VIASH_PAR_GENOMESAINDEXNBASES" ] && ViashError Bad arguments for option \'--genomeSAindexNbases=*\': \'$VIASH_PAR_GENOMESAINDEXNBASES\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMESAINDEXNBASES=$(ViashRemoveFlags "$1") + shift 1 + ;; + --genomeChrBinNbits) + [ -n "$VIASH_PAR_GENOMECHRBINNBITS" ] && ViashError Bad arguments for option \'--genomeChrBinNbits\': \'$VIASH_PAR_GENOMECHRBINNBITS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMECHRBINNBITS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeChrBinNbits. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeChrBinNbits=*) + [ -n "$VIASH_PAR_GENOMECHRBINNBITS" ] && ViashError Bad arguments for option \'--genomeChrBinNbits=*\': \'$VIASH_PAR_GENOMECHRBINNBITS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMECHRBINNBITS=$(ViashRemoveFlags "$1") + shift 1 + ;; + --genomeSAsparseD) + [ -n "$VIASH_PAR_GENOMESASPARSED" ] && ViashError Bad arguments for option \'--genomeSAsparseD\': \'$VIASH_PAR_GENOMESASPARSED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMESASPARSED="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeSAsparseD. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeSAsparseD=*) + [ -n "$VIASH_PAR_GENOMESASPARSED" ] && ViashError Bad arguments for option \'--genomeSAsparseD=*\': \'$VIASH_PAR_GENOMESASPARSED\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMESASPARSED=$(ViashRemoveFlags "$1") + shift 1 + ;; + --genomeSuffixLengthMax) + [ -n "$VIASH_PAR_GENOMESUFFIXLENGTHMAX" ] && ViashError Bad arguments for option \'--genomeSuffixLengthMax\': \'$VIASH_PAR_GENOMESUFFIXLENGTHMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMESUFFIXLENGTHMAX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeSuffixLengthMax. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeSuffixLengthMax=*) + [ -n "$VIASH_PAR_GENOMESUFFIXLENGTHMAX" ] && ViashError Bad arguments for option \'--genomeSuffixLengthMax=*\': \'$VIASH_PAR_GENOMESUFFIXLENGTHMAX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMESUFFIXLENGTHMAX=$(ViashRemoveFlags "$1") + shift 1 + ;; + --genomeTransformType) + [ -n "$VIASH_PAR_GENOMETRANSFORMTYPE" ] && ViashError Bad arguments for option \'--genomeTransformType\': \'$VIASH_PAR_GENOMETRANSFORMTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMETRANSFORMTYPE="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeTransformType. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeTransformType=*) + [ -n "$VIASH_PAR_GENOMETRANSFORMTYPE" ] && ViashError Bad arguments for option \'--genomeTransformType=*\': \'$VIASH_PAR_GENOMETRANSFORMTYPE\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMETRANSFORMTYPE=$(ViashRemoveFlags "$1") + shift 1 + ;; + --genomeTransformVCF) + [ -n "$VIASH_PAR_GENOMETRANSFORMVCF" ] && ViashError Bad arguments for option \'--genomeTransformVCF\': \'$VIASH_PAR_GENOMETRANSFORMVCF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMETRANSFORMVCF="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --genomeTransformVCF. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --genomeTransformVCF=*) + [ -n "$VIASH_PAR_GENOMETRANSFORMVCF" ] && ViashError Bad arguments for option \'--genomeTransformVCF=*\': \'$VIASH_PAR_GENOMETRANSFORMVCF\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_GENOMETRANSFORMVCF=$(ViashRemoveFlags "$1") + shift 1 + ;; + --index) + [ -n "$VIASH_PAR_INDEX" ] && ViashError Bad arguments for option \'--index\': \'$VIASH_PAR_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to --index. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + --index=*) + [ -n "$VIASH_PAR_INDEX" ] && ViashError Bad arguments for option \'--index=*\': \'$VIASH_PAR_INDEX\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_PAR_INDEX=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---engine) + VIASH_ENGINE_ID="$2" + shift 2 + ;; + ---engine=*) + VIASH_ENGINE_ID="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---setup) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$2" + shift 2 + ;; + ---setup=*) + VIASH_MODE='setup' + VIASH_SETUP_STRATEGY="$(ViashRemoveFlags "$1")" + shift 1 + ;; + ---dockerfile) + VIASH_MODE='dockerfile' + shift 1 + ;; + ---debug) + VIASH_MODE='debug' + shift 1 + ;; + ---cpus) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---cpus. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---cpus=*) + [ -n "$VIASH_META_CPUS" ] && ViashError Bad arguments for option \'---cpus=*\': \'$VIASH_META_CPUS\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_CPUS=$(ViashRemoveFlags "$1") + shift 1 + ;; + ---memory) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY="$2" + [ $# -lt 2 ] && ViashError Not enough arguments passed to ---memory. Use "--help" to get more information on the parameters. && exit 1 + shift 2 + ;; + ---memory=*) + [ -n "$VIASH_META_MEMORY" ] && ViashError Bad arguments for option \'---memory=*\': \'$VIASH_META_MEMORY\' \& \'$2\' - you should provide exactly one argument for this option. && exit 1 + VIASH_META_MEMORY=$(ViashRemoveFlags "$1") + shift 1 + ;; + *) # positional arg or unknown option + # since the positional args will be eval'd, can we always quote, instead of using ViashQuote + VIASH_POSITIONAL_ARGS="$VIASH_POSITIONAL_ARGS '$1'" + [[ $1 == -* ]] && ViashWarning $1 looks like a parameter but is not a defined parameter and will instead be treated as a positional argument. Use "--help" to get more information on the parameters. + shift # past argument + ;; + esac +done + +# parse positional parameters +eval set -- $VIASH_POSITIONAL_ARGS + + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + VIASH_ENGINE_TYPE='native' +elif [ "$VIASH_ENGINE_ID" == "docker" ] ; then + VIASH_ENGINE_TYPE='docker' +else + ViashError "Engine '$VIASH_ENGINE_ID' is not recognized. Options are: docker, native." + exit 1 +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # check if docker is installed properly + ViashDockerInstallationCheck + + # determine docker image id + if [[ "$VIASH_ENGINE_ID" == 'docker' ]]; then + VIASH_DOCKER_IMAGE_ID='images.viash-hub.com/vsh/biobox/star/star_genome_generate:main' + fi + + # print dockerfile + if [ "$VIASH_MODE" == "dockerfile" ]; then + ViashDockerfile "$VIASH_ENGINE_ID" + exit 0 + + # enter docker container + elif [[ "$VIASH_MODE" == "debug" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} -v '$(pwd)':/pwd --workdir /pwd -t $VIASH_DOCKER_IMAGE_ID" + ViashNotice "+ $VIASH_CMD" + eval $VIASH_CMD + exit + + # build docker image + elif [ "$VIASH_MODE" == "setup" ]; then + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" "$VIASH_SETUP_STRATEGY" + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' + exit 0 + fi + + # check if docker image exists + ViashDockerSetup "$VIASH_DOCKER_IMAGE_ID" ifneedbepullelsecachedbuild + ViashDockerCheckCommands "$VIASH_DOCKER_IMAGE_ID" 'ps' 'bash' +fi + +# setting computational defaults + +# helper function for parsing memory strings +function ViashMemoryAsBytes { + local memory=`echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'` + local memory_regex='^([0-9]+)([kmgtp]i?b?|b)$' + if [[ $memory =~ $memory_regex ]]; then + local number=${memory/[^0-9]*/} + local symbol=${memory/*[0-9]/} + + case $symbol in + b) memory_b=$number ;; + kb|k) memory_b=$(( $number * 1000 )) ;; + mb|m) memory_b=$(( $number * 1000 * 1000 )) ;; + gb|g) memory_b=$(( $number * 1000 * 1000 * 1000 )) ;; + tb|t) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 )) ;; + pb|p) memory_b=$(( $number * 1000 * 1000 * 1000 * 1000 * 1000 )) ;; + kib|ki) memory_b=$(( $number * 1024 )) ;; + mib|mi) memory_b=$(( $number * 1024 * 1024 )) ;; + gib|gi) memory_b=$(( $number * 1024 * 1024 * 1024 )) ;; + tib|ti) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 )) ;; + pib|pi) memory_b=$(( $number * 1024 * 1024 * 1024 * 1024 * 1024 )) ;; + esac + echo "$memory_b" + fi +} +# compute memory in different units +if [ ! -z ${VIASH_META_MEMORY+x} ]; then + VIASH_META_MEMORY_B=`ViashMemoryAsBytes $VIASH_META_MEMORY` + # do not define other variables if memory_b is an empty string + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_META_MEMORY_KB=$(( ($VIASH_META_MEMORY_B+999) / 1000 )) + VIASH_META_MEMORY_MB=$(( ($VIASH_META_MEMORY_KB+999) / 1000 )) + VIASH_META_MEMORY_GB=$(( ($VIASH_META_MEMORY_MB+999) / 1000 )) + VIASH_META_MEMORY_TB=$(( ($VIASH_META_MEMORY_GB+999) / 1000 )) + VIASH_META_MEMORY_PB=$(( ($VIASH_META_MEMORY_TB+999) / 1000 )) + VIASH_META_MEMORY_KIB=$(( ($VIASH_META_MEMORY_B+1023) / 1024 )) + VIASH_META_MEMORY_MIB=$(( ($VIASH_META_MEMORY_KIB+1023) / 1024 )) + VIASH_META_MEMORY_GIB=$(( ($VIASH_META_MEMORY_MIB+1023) / 1024 )) + VIASH_META_MEMORY_TIB=$(( ($VIASH_META_MEMORY_GIB+1023) / 1024 )) + VIASH_META_MEMORY_PIB=$(( ($VIASH_META_MEMORY_TIB+1023) / 1024 )) + else + # unset memory if string is empty + unset $VIASH_META_MEMORY_B + fi +fi +# unset nproc if string is empty +if [ -z "$VIASH_META_CPUS" ]; then + unset $VIASH_META_CPUS +fi + + +# check whether required parameters exist +if [ -z ${VIASH_PAR_GENOMEFASTAFILES+x} ]; then + ViashError '--genomeFastaFiles' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_PAR_INDEX+x} ]; then + ViashError '--index' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_NAME+x} ]; then + ViashError 'name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then + ViashError 'functionality_name' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_RESOURCES_DIR+x} ]; then + ViashError 'resources_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_EXECUTABLE+x} ]; then + ViashError 'executable' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_CONFIG+x} ]; then + ViashError 'config' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi +if [ -z ${VIASH_META_TEMP_DIR+x} ]; then + ViashError 'temp_dir' is a required argument. Use "--help" to get more information on the parameters. + exit 1 +fi + +# check whether required files exist +if [ ! -z "$VIASH_PAR_GENOMEFASTAFILES" ]; then + IFS=';' + set -f + for file in $VIASH_PAR_GENOMEFASTAFILES; do + unset IFS + if [ ! -e "$file" ]; then + ViashError "Input file '$file' does not exist." + exit 1 + fi + done + set +f +fi +if [ ! -z "$VIASH_PAR_SJDBGTFFILE" ] && [ ! -e "$VIASH_PAR_SJDBGTFFILE" ]; then + ViashError "Input file '$VIASH_PAR_SJDBGTFFILE' does not exist." + exit 1 +fi +if [ ! -z "$VIASH_PAR_GENOMETRANSFORMVCF" ] && [ ! -e "$VIASH_PAR_GENOMETRANSFORMVCF" ]; then + ViashError "Input file '$VIASH_PAR_GENOMETRANSFORMVCF' does not exist." + exit 1 +fi + +# check whether parameters values are of the right type +if [[ -n "$VIASH_PAR_SJDBOVERHANG" ]]; then + if ! [[ "$VIASH_PAR_SJDBOVERHANG" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--sjdbOverhang' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_LIMITGENOMEGENERATERAM" ]]; then + if ! [[ "$VIASH_PAR_LIMITGENOMEGENERATERAM" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--limitGenomeGenerateRAM' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GENOMESAINDEXNBASES" ]]; then + if ! [[ "$VIASH_PAR_GENOMESAINDEXNBASES" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--genomeSAindexNbases' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GENOMECHRBINNBITS" ]]; then + if ! [[ "$VIASH_PAR_GENOMECHRBINNBITS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--genomeChrBinNbits' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GENOMESASPARSED" ]]; then + if ! [[ "$VIASH_PAR_GENOMESASPARSED" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--genomeSAsparseD' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi + if [[ $VIASH_PAR_GENOMESASPARSED -lt 0 ]]; then + ViashError '--genomeSAsparseD' has be more than or equal to 0. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_PAR_GENOMESUFFIXLENGTHMAX" ]]; then + if ! [[ "$VIASH_PAR_GENOMESUFFIXLENGTHMAX" =~ ^[-+]?[0-9]+$ ]]; then + ViashError '--genomeSuffixLengthMax' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_CPUS" ]]; then + if ! [[ "$VIASH_META_CPUS" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'cpus' has to be an integer. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_B" ]]; then + if ! [[ "$VIASH_META_MEMORY_B" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_b' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pb' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_KIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_KIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_kib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_MIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_MIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_mib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_GIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_GIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_gib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_TIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_TIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_tib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi +if [[ -n "$VIASH_META_MEMORY_PIB" ]]; then + if ! [[ "$VIASH_META_MEMORY_PIB" =~ ^[-+]?[0-9]+$ ]]; then + ViashError 'memory_pib' has to be a long. Use "--help" to get more information on the parameters. + exit 1 + fi +fi + +# create parent directories of output files, if so desired +if [ ! -z "$VIASH_PAR_INDEX" ] && [ ! -d "$(dirname "$VIASH_PAR_INDEX")" ]; then + mkdir -p "$(dirname "$VIASH_PAR_INDEX")" +fi + +if [ "$VIASH_ENGINE_ID" == "native" ] ; then + if [ "$VIASH_MODE" == "run" ]; then + VIASH_CMD="bash" + else + ViashError "Engine '$VIASH_ENGINE_ID' does not support mode '$VIASH_MODE'." + exit 1 + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # detect volumes from file arguments + VIASH_CHOWN_VARS=() +if [ ! -z "$VIASH_PAR_GENOMEFASTAFILES" ]; then + VIASH_TEST_GENOMEFASTAFILES=() + IFS=';' + for var in $VIASH_PAR_GENOMEFASTAFILES; do + unset IFS + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$var")" ) + var=$(ViashDockerAutodetectMount "$var") + VIASH_TEST_GENOMEFASTAFILES+=( "$var" ) + done + VIASH_PAR_GENOMEFASTAFILES=$(IFS=';' ; echo "${VIASH_TEST_GENOMEFASTAFILES[*]}") +fi +if [ ! -z "$VIASH_PAR_SJDBGTFFILE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_SJDBGTFFILE")" ) + VIASH_PAR_SJDBGTFFILE=$(ViashDockerAutodetectMount "$VIASH_PAR_SJDBGTFFILE") +fi +if [ ! -z "$VIASH_PAR_GENOMETRANSFORMVCF" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_GENOMETRANSFORMVCF")" ) + VIASH_PAR_GENOMETRANSFORMVCF=$(ViashDockerAutodetectMount "$VIASH_PAR_GENOMETRANSFORMVCF") +fi +if [ ! -z "$VIASH_PAR_INDEX" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_PAR_INDEX")" ) + VIASH_PAR_INDEX=$(ViashDockerAutodetectMount "$VIASH_PAR_INDEX") + VIASH_CHOWN_VARS+=( "$VIASH_PAR_INDEX" ) +fi +if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_RESOURCES_DIR")" ) + VIASH_META_RESOURCES_DIR=$(ViashDockerAutodetectMount "$VIASH_META_RESOURCES_DIR") +fi +if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_EXECUTABLE")" ) + VIASH_META_EXECUTABLE=$(ViashDockerAutodetectMount "$VIASH_META_EXECUTABLE") +fi +if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_CONFIG")" ) + VIASH_META_CONFIG=$(ViashDockerAutodetectMount "$VIASH_META_CONFIG") +fi +if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_DIRECTORY_MOUNTS+=( "$(ViashDockerAutodetectMountArg "$VIASH_META_TEMP_DIR")" ) + VIASH_META_TEMP_DIR=$(ViashDockerAutodetectMount "$VIASH_META_TEMP_DIR") +fi + + # get unique mounts + VIASH_UNIQUE_MOUNTS=($(for val in "${VIASH_DIRECTORY_MOUNTS[@]}"; do echo "$val"; done | sort -u)) +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # change file ownership + function ViashPerformChown { + if (( ${#VIASH_CHOWN_VARS[@]} )); then + set +e + VIASH_CMD="docker run --entrypoint=bash --rm ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID -c 'chown $(id -u):$(id -g) --silent --recursive ${VIASH_CHOWN_VARS[@]}'" + ViashDebug "+ $VIASH_CMD" + eval $VIASH_CMD + set -e + fi + } + trap ViashPerformChown EXIT +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # helper function for filling in extra docker args + if [ ! -z "$VIASH_META_MEMORY_B" ]; then + VIASH_DOCKER_RUN_ARGS+=("--memory=${VIASH_META_MEMORY_B}") + fi + if [ ! -z "$VIASH_META_CPUS" ]; then + VIASH_DOCKER_RUN_ARGS+=("--cpus=${VIASH_META_CPUS}") + fi +fi + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + VIASH_CMD="docker run --entrypoint=bash ${VIASH_DOCKER_RUN_ARGS[@]} ${VIASH_UNIQUE_MOUNTS[@]} $VIASH_DOCKER_IMAGE_ID" +fi + + +# set dependency paths + + +ViashDebug "Running command: $(echo $VIASH_CMD)" +cat << VIASHEOF | eval $VIASH_CMD +set -e +tempscript=\$(mktemp "$VIASH_META_TEMP_DIR/viash-run-star_genome_generate-XXXXXX").sh +function clean_up { + rm "\$tempscript" +} +function interrupt { + echo -e "\nCTRL-C Pressed..." + exit 1 +} +trap clean_up EXIT +trap interrupt INT SIGINT +cat > "\$tempscript" << 'VIASHMAIN' +#!/bin/bash + +set -e + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_GENOMEFASTAFILES+x} ]; then echo "${VIASH_PAR_GENOMEFASTAFILES}" | sed "s#'#'\"'\"'#g;s#.*#par_genomeFastaFiles='&'#" ; else echo "# par_genomeFastaFiles="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFFILE+x} ]; then echo "${VIASH_PAR_SJDBGTFFILE}" | sed "s#'#'\"'\"'#g;s#.*#par_sjdbGTFfile='&'#" ; else echo "# par_sjdbGTFfile="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBOVERHANG+x} ]; then echo "${VIASH_PAR_SJDBOVERHANG}" | sed "s#'#'\"'\"'#g;s#.*#par_sjdbOverhang='&'#" ; else echo "# par_sjdbOverhang="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFCHRPREFIX+x} ]; then echo "${VIASH_PAR_SJDBGTFCHRPREFIX}" | sed "s#'#'\"'\"'#g;s#.*#par_sjdbGTFchrPrefix='&'#" ; else echo "# par_sjdbGTFchrPrefix="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFFEATUREEXON+x} ]; then echo "${VIASH_PAR_SJDBGTFFEATUREEXON}" | sed "s#'#'\"'\"'#g;s#.*#par_sjdbGTFfeatureExon='&'#" ; else echo "# par_sjdbGTFfeatureExon="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT+x} ]; then echo "${VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT}" | sed "s#'#'\"'\"'#g;s#.*#par_sjdbGTFtagExonParentTranscript='&'#" ; else echo "# par_sjdbGTFtagExonParentTranscript="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENE+x} ]; then echo "${VIASH_PAR_SJDBGTFTAGEXONPARENTGENE}" | sed "s#'#'\"'\"'#g;s#.*#par_sjdbGTFtagExonParentGene='&'#" ; else echo "# par_sjdbGTFtagExonParentGene="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME+x} ]; then echo "${VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME}" | sed "s#'#'\"'\"'#g;s#.*#par_sjdbGTFtagExonParentGeneName='&'#" ; else echo "# par_sjdbGTFtagExonParentGeneName="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE+x} ]; then echo "${VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE}" | sed "s#'#'\"'\"'#g;s#.*#par_sjdbGTFtagExonParentGeneType='&'#" ; else echo "# par_sjdbGTFtagExonParentGeneType="; fi ) +$( if [ ! -z ${VIASH_PAR_LIMITGENOMEGENERATERAM+x} ]; then echo "${VIASH_PAR_LIMITGENOMEGENERATERAM}" | sed "s#'#'\"'\"'#g;s#.*#par_limitGenomeGenerateRAM='&'#" ; else echo "# par_limitGenomeGenerateRAM="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMESAINDEXNBASES+x} ]; then echo "${VIASH_PAR_GENOMESAINDEXNBASES}" | sed "s#'#'\"'\"'#g;s#.*#par_genomeSAindexNbases='&'#" ; else echo "# par_genomeSAindexNbases="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMECHRBINNBITS+x} ]; then echo "${VIASH_PAR_GENOMECHRBINNBITS}" | sed "s#'#'\"'\"'#g;s#.*#par_genomeChrBinNbits='&'#" ; else echo "# par_genomeChrBinNbits="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMESASPARSED+x} ]; then echo "${VIASH_PAR_GENOMESASPARSED}" | sed "s#'#'\"'\"'#g;s#.*#par_genomeSAsparseD='&'#" ; else echo "# par_genomeSAsparseD="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMESUFFIXLENGTHMAX+x} ]; then echo "${VIASH_PAR_GENOMESUFFIXLENGTHMAX}" | sed "s#'#'\"'\"'#g;s#.*#par_genomeSuffixLengthMax='&'#" ; else echo "# par_genomeSuffixLengthMax="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMETRANSFORMTYPE+x} ]; then echo "${VIASH_PAR_GENOMETRANSFORMTYPE}" | sed "s#'#'\"'\"'#g;s#.*#par_genomeTransformType='&'#" ; else echo "# par_genomeTransformType="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMETRANSFORMVCF+x} ]; then echo "${VIASH_PAR_GENOMETRANSFORMVCF}" | sed "s#'#'\"'\"'#g;s#.*#par_genomeTransformVCF='&'#" ; else echo "# par_genomeTransformVCF="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX+x} ]; then echo "${VIASH_PAR_INDEX}" | sed "s#'#'\"'\"'#g;s#.*#par_index='&'#" ; else echo "# par_index="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\"'\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\"'\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\"'\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\"'\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\"'\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\"'\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +mkdir -p \$par_index + +STAR \\ + --runMode genomeGenerate \\ + --genomeDir \$par_index \\ + --genomeFastaFiles \$par_genomeFastaFiles \\ + \${meta_cpus:+--runThreadN "\${meta_cpus}"} \\ + \${par_sjdbGTFfile:+--sjdbGTFfile "\${par_sjdbGTFfile}"} \\ + \${par_sjdbOverhang:+--sjdbOverhang "\${par_sjdbOverhang}"} \\ + \${par_genomeSAindexNbases:+--genomeSAindexNbases "\${par_genomeSAindexNbases}"} \\ + \${par_sjdbGTFchrPrefix:+--sjdbGTFchrPrefix "\${par_sjdbGTFchrPrefix}"} \\ + \${par_sjdbGTFfeatureExon:+--sjdbGTFfeatureExon "\${par_sjdbGTFfeatureExon}"} \\ + \${par_sjdbGTFtagExonParentTranscript:+--sjdbGTFtagExonParentTranscript "\${par_sjdbGTFtagExonParentTranscript}"} \\ + \${par_sjdbGTFtagExonParentGene:+--sjdbGTFtagExonParentGene "\${par_sjdbGTFtagExonParentGene}"} \\ + \${par_sjdbGTFtagExonParentGeneName:+--sjdbGTFtagExonParentGeneName "\${par_sjdbGTFtagExonParentGeneName}"} \\ + \${par_sjdbGTFtagExonParentGeneType:+--sjdbGTFtagExonParentGeneType "\${sjdbGTFtagExonParentGeneType}"} \\ + \${par_limitGenomeGenerateRAM:+--limitGenomeGenerateRAM "\${par_limitGenomeGenerateRAM}"} \\ + \${par_genomeChrBinNbits:+--genomeChrBinNbits "\${par_genomeChrBinNbits}"} \\ + \${par_genomeSAsparseD:+--genomeSAsparseD "\${par_genomeSAsparseD}"} \\ + \${par_genomeSuffixLengthMax:+--genomeSuffixLengthMax "\${par_genomeSuffixLengthMax}"} \\ + \${par_genomeTransformType:+--genomeTransformType "\${par_genomeTransformType}"} \\ + \${par_genomeTransformVCF:+--genomeTransformVCF "\${par_genomeTransformVCF}"} \\ +VIASHMAIN +bash "\$tempscript" & +wait "\$!" + +VIASHEOF + + +if [[ "$VIASH_ENGINE_TYPE" == "docker" ]]; then + # strip viash automount from file paths + + if [ ! -z "$VIASH_PAR_GENOMEFASTAFILES" ]; then + unset VIASH_TEST_GENOMEFASTAFILES + IFS=';' + for var in $VIASH_PAR_GENOMEFASTAFILES; do + unset IFS + if [ -z "$VIASH_TEST_GENOMEFASTAFILES" ]; then + VIASH_TEST_GENOMEFASTAFILES="$(ViashDockerStripAutomount "$var")" + else + VIASH_TEST_GENOMEFASTAFILES="$VIASH_TEST_GENOMEFASTAFILES;""$(ViashDockerStripAutomount "$var")" + fi + done + VIASH_PAR_GENOMEFASTAFILES="$VIASH_TEST_GENOMEFASTAFILES" + fi + if [ ! -z "$VIASH_PAR_SJDBGTFFILE" ]; then + VIASH_PAR_SJDBGTFFILE=$(ViashDockerStripAutomount "$VIASH_PAR_SJDBGTFFILE") + fi + if [ ! -z "$VIASH_PAR_GENOMETRANSFORMVCF" ]; then + VIASH_PAR_GENOMETRANSFORMVCF=$(ViashDockerStripAutomount "$VIASH_PAR_GENOMETRANSFORMVCF") + fi + if [ ! -z "$VIASH_PAR_INDEX" ]; then + VIASH_PAR_INDEX=$(ViashDockerStripAutomount "$VIASH_PAR_INDEX") + fi + if [ ! -z "$VIASH_META_RESOURCES_DIR" ]; then + VIASH_META_RESOURCES_DIR=$(ViashDockerStripAutomount "$VIASH_META_RESOURCES_DIR") + fi + if [ ! -z "$VIASH_META_EXECUTABLE" ]; then + VIASH_META_EXECUTABLE=$(ViashDockerStripAutomount "$VIASH_META_EXECUTABLE") + fi + if [ ! -z "$VIASH_META_CONFIG" ]; then + VIASH_META_CONFIG=$(ViashDockerStripAutomount "$VIASH_META_CONFIG") + fi + if [ ! -z "$VIASH_META_TEMP_DIR" ]; then + VIASH_META_TEMP_DIR=$(ViashDockerStripAutomount "$VIASH_META_TEMP_DIR") + fi +fi + + +# check whether required files exist +if [ ! -z "$VIASH_PAR_INDEX" ] && [ ! -e "$VIASH_PAR_INDEX" ]; then + ViashError "Output file '$VIASH_PAR_INDEX' does not exist." + exit 1 +fi + + +exit 0 diff --git a/target/nextflow/arriba/.config.vsh.yaml b/target/nextflow/arriba/.config.vsh.yaml new file mode 100644 index 00000000..ce15f276 --- /dev/null +++ b/target/nextflow/arriba/.config.vsh.yaml @@ -0,0 +1,714 @@ +name: "arriba" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--bam" + alternatives: + - "-x" + description: "File in SAM/BAM/CRAM format with main alignments as generated by\ + \ STAR\n(Aligned.out.sam). Arriba extracts candidate reads from this file.\n" + info: null + example: + - "Aligned.out.bam" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--genome" + alternatives: + - "-a" + description: "FastA file with genome sequence (assembly). The file may be gzip-compressed.\ + \ An \nindex with the file extension .fai must exist only if CRAM files are\ + \ processed.\n" + info: null + example: + - "assembly.fa" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--gene_annotation" + alternatives: + - "-g" + description: "GTF file with gene annotation. The file may be gzip-compressed.\n" + info: null + example: + - "annotation.gtf" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--known_fusions" + alternatives: + - "-k" + description: "File containing known/recurrent fusions. Some cancer entities are\ + \ often \ncharacterized by fusions between the same pair of genes. In order\ + \ to boost \nsensitivity, a list of known fusions can be supplied using this\ + \ parameter. The list \nmust contain two columns with the names of the fused\ + \ genes, separated by tabs.\n" + info: null + example: + - "known_fusions.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--blacklist" + alternatives: + - "-b" + description: "File containing blacklisted events (recurrent artifacts and transcripts\ + \ \nobserved in healthy tissue).\n" + info: null + example: + - "blacklist.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--structural_variants" + alternatives: + - "-d" + description: "Tab-separated file with coordinates of structural variants found\ + \ using \nwhole-genome sequencing data. These coordinates serve to increase\ + \ sensitivity \ntowards weakly expressed fusions and to eliminate fusions with\ + \ low evidence. \n" + info: null + example: + - "structural_variants_from_WGS.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--tags" + alternatives: + - "-t" + description: "Tab-separated file containing fusions to annotate with tags in the\ + \ 'tags' column. \nThe first two columns specify the genes; the third column\ + \ specifies the tag. The \nfile may be gzip-compressed. \n" + info: null + example: + - "tags.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--protein_domains" + alternatives: + - "-p" + description: "File in GFF3 format containing coordinates of the protein domains\ + \ of genes. The\nprotein domains retained in a fusion are listed in the column\n\ + 'retained_protein_domains'. The file may be gzip-compressed.\n" + info: null + example: + - "protein_domains.gff3" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--fusions" + alternatives: + - "-o" + description: "Output file with fusions that have passed all filters.\n" + info: null + example: + - "fusions.tsv" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--fusions_discarded" + alternatives: + - "-O" + description: "Output file with fusions that were discarded due to filtering. \n" + info: null + example: + - "fusions.discarded.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Arguments" + arguments: + - type: "long" + name: "--max_genomic_breakpoint_distance" + alternatives: + - "-D" + description: "When a file with genomic breakpoints obtained via \nwhole-genome\ + \ sequencing is supplied via the --structural_variants\nparameter, this parameter\ + \ determines how far a \ngenomic breakpoint may be away from a \ntranscriptomic\ + \ breakpoint to consider it as a \nrelated event. For events inside genes, the\ + \ \ndistance is added to the end of the gene; for \nintergenic events, the distance\ + \ threshold is \napplied as is. Default: 100000.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--strandedness" + alternatives: + - "-s" + description: "Whether a strand-specific protocol was used for library preparation,\ + \ \nand if so, the type of strandedness (auto/yes/no/reverse). When \nunstranded\ + \ data is processed, the strand can sometimes be inferred from \nsplice-patterns.\ + \ But in unclear situations, stranded data helps \nresolve ambiguities. Default:\ + \ auto\n" + info: null + required: false + choices: + - "auto" + - "yes" + - "no" + - "reverse" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--interesting_contigs" + alternatives: + - "-i" + description: "List of interesting contigs. Fusions between genes \non other contigs\ + \ are ignored. Contigs can be specified with or without the \nprefix \"chr\"\ + . Asterisks (*) are treated as wild-cards. \nDefault: 1 2 3 4 5 6 7 8 9 10 11\ + \ 12 13 14 15 16 17 18 19 20 21 22 X Y AC_* NC_*\n" + info: null + example: + - "1" + - "2" + - "AC_*" + - "NC_*" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--viral_contigs" + alternatives: + - "-v" + description: "List of viral contigs. Asterisks (*) are treated as \nwild-cards.\n\ + Default: AC_* NC_*\n" + info: null + example: + - "AC_*" + - "NC_*" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--disable_filters" + alternatives: + - "-f" + description: "List of filters to disable. By default all filters are \nenabled.\ + \ \n" + info: null + required: false + choices: + - "homologs" + - "low_entropy" + - "isoforms" + - "top_expressed_viral_contigs" + - "viral_contigs" + - "uninteresting_contigs" + - "non_coding_neighbors" + - "mismatches" + - "duplicates" + - "no_genomic_support" + - "genomic_support" + - "intronic" + - "end_to_end" + - "relative_support" + - "low_coverage_viral_contigs" + - "merge_adjacent" + - "mismappers" + - "multimappers" + - "same_gene" + - "long_gap" + - "internal_tandem_duplication" + - "small_insert_size" + - "read_through" + - "inconsistently_clipped" + - "intragenic_exonic" + - "marginal_read_through" + - "spliced" + - "hairpin" + - "blacklist" + - "min_support" + - "select_best" + - "in_vitro" + - "short_anchor" + - "known_fusions" + - "no_coverage" + - "homopolymer" + - "many_spliced" + direction: "input" + multiple: true + multiple_sep: ";" + - type: "double" + name: "--max_e_value" + alternatives: + - "-E" + description: "Arriba estimates the number of fusions with a given number of supporting\ + \ \nreads which one would expect to see by random chance. If the expected number\ + \ \nof fusions (e-value) is higher than this threshold, the fusion is \ndiscarded\ + \ by the 'relative_support' filter. Note: Increasing this \nthreshold can dramatically\ + \ increase the number of false positives and may \nincrease the runtime of resource-intensive\ + \ steps. Fractional values are \npossible. Default: 0.300000 \n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_supporting_reads" + alternatives: + - "-S" + description: "The 'min_support' filter discards all fusions with fewer than \n\ + this many supporting reads (split reads and discordant mates \ncombined). Default:\ + \ 2 \n" + info: null + example: + - 2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--max_mismappers" + alternatives: + - "-m" + description: "When more than this fraction of supporting reads turns out to be\ + \ \nmismappers, the 'mismappers' filter discards the fusion. Default: \n0.800000\n" + info: null + example: + - 0.8 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--max_homolog_identity" + alternatives: + - "-L" + description: "Genes with more than the given fraction of sequence identity are\ + \ \nconsidered homologs and removed by the 'homologs' filter. \nDefault: 0.300000\ + \ \n" + info: null + example: + - 0.3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--homopolymer_length" + alternatives: + - "-H" + description: "The 'homopolymer' filter removes breakpoints adjacent to \nhomopolymers\ + \ of the given length or more. Default: 6\n" + info: null + example: + - 6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--read_through_distance" + alternatives: + - "-R" + description: "The 'read_through' filter removes read-through fusions \nwhere the\ + \ breakpoints are less than the given distance away \nfrom each other. Default:\ + \ 10000 \n" + info: null + example: + - 10000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_anchor_length" + alternatives: + - "-A" + description: "Alignment artifacts are often characterized by split reads coming\ + \ \nfrom only one gene and no discordant mates. Moreover, the split \nreads\ + \ only align to a short stretch in one of the genes. The \n'short_anchor' filter\ + \ removes these fusions. This parameter sets \nthe threshold in bp for what\ + \ the filter considers short. Default: 23 \n" + info: null + example: + - 23 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--many_spliced_events" + alternatives: + - "-M" + description: "The 'many_spliced' filter recovers fusions between genes that \n\ + have at least this many spliced breakpoints. Default: 4\n" + info: null + example: + - 4 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--max_kmer_content" + alternatives: + - "-K" + description: "The 'low_entropy' filter removes reads with repetitive 3-mers. If\ + \ \nthe 3-mers make up more than the given fraction of the sequence, then \n\ + the read is discarded. Default: 0.600000 \n" + info: null + example: + - 0.6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--max_mismatch_pvalue" + alternatives: + - "-V" + description: "The 'mismatches' filter uses a binomial model to calculate a \n\ + p-value for observing a given number of mismatches in a read. If \nthe number\ + \ of mismatches is too high, the read is discarded. \nDefault: 0.010000 \n" + info: null + example: + - 0.05 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--fragment_length" + alternatives: + - "-F" + description: "When paired-end data is given, the fragment length is estimated\ + \ \nautomatically and this parameter has no effect. But when single-end \ndata\ + \ is given, the mean fragment length should be specified to \neffectively filter\ + \ fusions that arise from hairpin structures. \nDefault: 200 \n" + info: null + example: + - 200 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_reads" + alternatives: + - "-U" + description: "Subsample fusions with more than the given number of supporting\ + \ reads. This \nimproves performance without compromising sensitivity, as long\ + \ as the \nthreshold is high. Counting of supporting reads beyond the threshold\ + \ is \ninaccurate, obviously. Default: 300 \n" + info: null + example: + - 300 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--quantile" + alternatives: + - "-Q" + description: "Highly expressed genes are prone to produce artifacts during library\ + \ \npreparation. Genes with an expression above the given quantile are eligible\ + \ \nfor filtering by the 'in_vitro' filter. Default: 0.998000\n" + info: null + example: + - 0.998 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--exonic_fraction" + alternatives: + - "-e" + description: "The breakpoints of false-positive predictions of intragenic events\ + \ \nare often both in exons. True predictions are more likely to have at \n\ + least one breakpoint in an intron, because introns are larger. If the \nfraction\ + \ of exonic sequence between two breakpoints is smaller than \nthe given fraction,\ + \ the 'intragenic_exonic' filter discards the \nevent. Default: 0.330000 \n" + info: null + example: + - 0.33 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--top_n" + alternatives: + - "-T" + description: "Only report viral integration sites of the top N most highly expressed\ + \ viral \ncontigs. Default: 5\n" + info: null + example: + - 5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--covered_fraction" + alternatives: + - "-C" + description: "Ignore virally associated events if the virus is not fully \nexpressed,\ + \ i.e., less than the given fraction of the viral contig is \ntranscribed. Default:\ + \ 0.050000 \n" + info: null + example: + - 0.05 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_itd_length" + alternatives: + - "-l" + description: "Maximum length of internal tandem duplications. Note: Increasing\ + \ \nthis value beyond the default can impair performance and lead to many \n\ + false positives. Default: 100 \n" + info: null + example: + - 100 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--min_itd_allele_fraction" + alternatives: + - "-z" + description: "Required fraction of supporting reads to report an internal \ntandem\ + \ duplication. Default: 0.070000 \n" + info: null + example: + - 0.07 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_itd_supporting_reads" + alternatives: + - "-Z" + description: "Required absolute number of supporting reads to report an \ninternal\ + \ tandem duplication. Default: 10 \n" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--skip_duplicate_marking" + alternatives: + - "-u" + description: "Instead of performing duplicate marking itself, Arriba relies on\ + \ duplicate marking by a \npreceding program using the BAM_FDUP flag. This makes\ + \ sense when unique molecular \nidentifiers (UMI) are used.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--extra_information" + alternatives: + - "-X" + description: "To reduce the runtime and file size, by default, the columns 'fusion_transcript',\ + \ \n'peptide_sequence', and 'read_identifiers' are left empty in the file containing\ + \ \ndiscarded fusion candidates (see parameter -O). When this flag is set, this\ + \ extra \ninformation is reported in the discarded fusions file.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--fill_gaps" + alternatives: + - "-I" + description: "If assembly of the fusion transcript sequence from the supporting\ + \ reads is incomplete \n(denoted as '...'), fill the gaps using the assembly\ + \ sequence wherever possible. \n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Detect gene fusions from RNA-Seq data" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + cpus: 1 + commands: + - "ps" +keywords: +- "Gene fusion" +- "RNA-Seq" +license: "MIT" +references: + doi: + - "10.1101/gr.257246.119" +links: + repository: "https://github.com/suhrig/arriba" + homepage: "https://arriba.readthedocs.io/en/latest/" + documentation: "https://arriba.readthedocs.io/en/latest/" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/arriba:2.4.0--h0033a41_2" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "arriba -h | grep 'Version:' 2>&1 | sed 's/Version:\\s\\(.*\\)/arriba: \"\\\ + 1\"/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/arriba/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/arriba" + executable: "target/nextflow/arriba/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/arriba/main.nf b/target/nextflow/arriba/main.nf new file mode 100644 index 00000000..2a6088b4 --- /dev/null +++ b/target/nextflow/arriba/main.nf @@ -0,0 +1,4164 @@ +// arriba main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "arriba", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--bam", + "alternatives" : [ + "-x" + ], + "description" : "File in SAM/BAM/CRAM format with main alignments as generated by STAR\n(Aligned.out.sam). Arriba extracts candidate reads from this file.\n", + "example" : [ + "Aligned.out.bam" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--genome", + "alternatives" : [ + "-a" + ], + "description" : "FastA file with genome sequence (assembly). The file may be gzip-compressed. An \nindex with the file extension .fai must exist only if CRAM files are processed.\n", + "example" : [ + "assembly.fa" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--gene_annotation", + "alternatives" : [ + "-g" + ], + "description" : "GTF file with gene annotation. The file may be gzip-compressed.\n", + "example" : [ + "annotation.gtf" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--known_fusions", + "alternatives" : [ + "-k" + ], + "description" : "File containing known/recurrent fusions. Some cancer entities are often \ncharacterized by fusions between the same pair of genes. In order to boost \nsensitivity, a list of known fusions can be supplied using this parameter. The list \nmust contain two columns with the names of the fused genes, separated by tabs.\n", + "example" : [ + "known_fusions.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--blacklist", + "alternatives" : [ + "-b" + ], + "description" : "File containing blacklisted events (recurrent artifacts and transcripts \nobserved in healthy tissue).\n", + "example" : [ + "blacklist.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--structural_variants", + "alternatives" : [ + "-d" + ], + "description" : "Tab-separated file with coordinates of structural variants found using \nwhole-genome sequencing data. These coordinates serve to increase sensitivity \ntowards weakly expressed fusions and to eliminate fusions with low evidence. \n", + "example" : [ + "structural_variants_from_WGS.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--tags", + "alternatives" : [ + "-t" + ], + "description" : "Tab-separated file containing fusions to annotate with tags in the 'tags' column. \nThe first two columns specify the genes; the third column specifies the tag. The \nfile may be gzip-compressed. \n", + "example" : [ + "tags.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--protein_domains", + "alternatives" : [ + "-p" + ], + "description" : "File in GFF3 format containing coordinates of the protein domains of genes. The\nprotein domains retained in a fusion are listed in the column\n'retained_protein_domains'. The file may be gzip-compressed.\n", + "example" : [ + "protein_domains.gff3" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--fusions", + "alternatives" : [ + "-o" + ], + "description" : "Output file with fusions that have passed all filters.\n", + "example" : [ + "fusions.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--fusions_discarded", + "alternatives" : [ + "-O" + ], + "description" : "Output file with fusions that were discarded due to filtering. \n", + "example" : [ + "fusions.discarded.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Arguments", + "arguments" : [ + { + "type" : "long", + "name" : "--max_genomic_breakpoint_distance", + "alternatives" : [ + "-D" + ], + "description" : "When a file with genomic breakpoints obtained via \nwhole-genome sequencing is supplied via the --structural_variants\nparameter, this parameter determines how far a \ngenomic breakpoint may be away from a \ntranscriptomic breakpoint to consider it as a \nrelated event. For events inside genes, the \ndistance is added to the end of the gene; for \nintergenic events, the distance threshold is \napplied as is. Default: 100000.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--strandedness", + "alternatives" : [ + "-s" + ], + "description" : "Whether a strand-specific protocol was used for library preparation, \nand if so, the type of strandedness (auto/yes/no/reverse). When \nunstranded data is processed, the strand can sometimes be inferred from \nsplice-patterns. But in unclear situations, stranded data helps \nresolve ambiguities. Default: auto\n", + "required" : false, + "choices" : [ + "auto", + "yes", + "no", + "reverse" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--interesting_contigs", + "alternatives" : [ + "-i" + ], + "description" : "List of interesting contigs. Fusions between genes \non other contigs are ignored. Contigs can be specified with or without the \nprefix \\"chr\\". Asterisks (*) are treated as wild-cards. \nDefault: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 X Y AC_* NC_*\n", + "example" : [ + "1", + "2", + "AC_*", + "NC_*" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--viral_contigs", + "alternatives" : [ + "-v" + ], + "description" : "List of viral contigs. Asterisks (*) are treated as \nwild-cards.\nDefault: AC_* NC_*\n", + "example" : [ + "AC_*", + "NC_*" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--disable_filters", + "alternatives" : [ + "-f" + ], + "description" : "List of filters to disable. By default all filters are \nenabled. \n", + "required" : false, + "choices" : [ + "homologs", + "low_entropy", + "isoforms", + "top_expressed_viral_contigs", + "viral_contigs", + "uninteresting_contigs", + "non_coding_neighbors", + "mismatches", + "duplicates", + "no_genomic_support", + "genomic_support", + "intronic", + "end_to_end", + "relative_support", + "low_coverage_viral_contigs", + "merge_adjacent", + "mismappers", + "multimappers", + "same_gene", + "long_gap", + "internal_tandem_duplication", + "small_insert_size", + "read_through", + "inconsistently_clipped", + "intragenic_exonic", + "marginal_read_through", + "spliced", + "hairpin", + "blacklist", + "min_support", + "select_best", + "in_vitro", + "short_anchor", + "known_fusions", + "no_coverage", + "homopolymer", + "many_spliced" + ], + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--max_e_value", + "alternatives" : [ + "-E" + ], + "description" : "Arriba estimates the number of fusions with a given number of supporting \nreads which one would expect to see by random chance. If the expected number \nof fusions (e-value) is higher than this threshold, the fusion is \ndiscarded by the 'relative_support' filter. Note: Increasing this \nthreshold can dramatically increase the number of false positives and may \nincrease the runtime of resource-intensive steps. Fractional values are \npossible. Default: 0.300000 \n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_supporting_reads", + "alternatives" : [ + "-S" + ], + "description" : "The 'min_support' filter discards all fusions with fewer than \nthis many supporting reads (split reads and discordant mates \ncombined). Default: 2 \n", + "example" : [ + 2 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--max_mismappers", + "alternatives" : [ + "-m" + ], + "description" : "When more than this fraction of supporting reads turns out to be \nmismappers, the 'mismappers' filter discards the fusion. Default: \n0.800000\n", + "example" : [ + 0.8 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--max_homolog_identity", + "alternatives" : [ + "-L" + ], + "description" : "Genes with more than the given fraction of sequence identity are \nconsidered homologs and removed by the 'homologs' filter. \nDefault: 0.300000 \n", + "example" : [ + 0.3 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--homopolymer_length", + "alternatives" : [ + "-H" + ], + "description" : "The 'homopolymer' filter removes breakpoints adjacent to \nhomopolymers of the given length or more. Default: 6\n", + "example" : [ + 6 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--read_through_distance", + "alternatives" : [ + "-R" + ], + "description" : "The 'read_through' filter removes read-through fusions \nwhere the breakpoints are less than the given distance away \nfrom each other. Default: 10000 \n", + "example" : [ + 10000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_anchor_length", + "alternatives" : [ + "-A" + ], + "description" : "Alignment artifacts are often characterized by split reads coming \nfrom only one gene and no discordant mates. Moreover, the split \nreads only align to a short stretch in one of the genes. The \n'short_anchor' filter removes these fusions. This parameter sets \nthe threshold in bp for what the filter considers short. Default: 23 \n", + "example" : [ + 23 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--many_spliced_events", + "alternatives" : [ + "-M" + ], + "description" : "The 'many_spliced' filter recovers fusions between genes that \nhave at least this many spliced breakpoints. Default: 4\n", + "example" : [ + 4 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--max_kmer_content", + "alternatives" : [ + "-K" + ], + "description" : "The 'low_entropy' filter removes reads with repetitive 3-mers. If \nthe 3-mers make up more than the given fraction of the sequence, then \nthe read is discarded. Default: 0.600000 \n", + "example" : [ + 0.6 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--max_mismatch_pvalue", + "alternatives" : [ + "-V" + ], + "description" : "The 'mismatches' filter uses a binomial model to calculate a \np-value for observing a given number of mismatches in a read. If \nthe number of mismatches is too high, the read is discarded. \nDefault: 0.010000 \n", + "example" : [ + 0.05 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--fragment_length", + "alternatives" : [ + "-F" + ], + "description" : "When paired-end data is given, the fragment length is estimated \nautomatically and this parameter has no effect. But when single-end \ndata is given, the mean fragment length should be specified to \neffectively filter fusions that arise from hairpin structures. \nDefault: 200 \n", + "example" : [ + 200 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--max_reads", + "alternatives" : [ + "-U" + ], + "description" : "Subsample fusions with more than the given number of supporting reads. This \nimproves performance without compromising sensitivity, as long as the \nthreshold is high. Counting of supporting reads beyond the threshold is \ninaccurate, obviously. Default: 300 \n", + "example" : [ + 300 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--quantile", + "alternatives" : [ + "-Q" + ], + "description" : "Highly expressed genes are prone to produce artifacts during library \npreparation. Genes with an expression above the given quantile are eligible \nfor filtering by the 'in_vitro' filter. Default: 0.998000\n", + "example" : [ + 0.998 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--exonic_fraction", + "alternatives" : [ + "-e" + ], + "description" : "The breakpoints of false-positive predictions of intragenic events \nare often both in exons. True predictions are more likely to have at \nleast one breakpoint in an intron, because introns are larger. If the \nfraction of exonic sequence between two breakpoints is smaller than \nthe given fraction, the 'intragenic_exonic' filter discards the \nevent. Default: 0.330000 \n", + "example" : [ + 0.33 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--top_n", + "alternatives" : [ + "-T" + ], + "description" : "Only report viral integration sites of the top N most highly expressed viral \ncontigs. Default: 5\n", + "example" : [ + 5 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--covered_fraction", + "alternatives" : [ + "-C" + ], + "description" : "Ignore virally associated events if the virus is not fully \nexpressed, i.e., less than the given fraction of the viral contig is \ntranscribed. Default: 0.050000 \n", + "example" : [ + 0.05 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--max_itd_length", + "alternatives" : [ + "-l" + ], + "description" : "Maximum length of internal tandem duplications. Note: Increasing \nthis value beyond the default can impair performance and lead to many \nfalse positives. Default: 100 \n", + "example" : [ + 100 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--min_itd_allele_fraction", + "alternatives" : [ + "-z" + ], + "description" : "Required fraction of supporting reads to report an internal \ntandem duplication. Default: 0.070000 \n", + "example" : [ + 0.07 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_itd_supporting_reads", + "alternatives" : [ + "-Z" + ], + "description" : "Required absolute number of supporting reads to report an \ninternal tandem duplication. Default: 10 \n", + "example" : [ + 10 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--skip_duplicate_marking", + "alternatives" : [ + "-u" + ], + "description" : "Instead of performing duplicate marking itself, Arriba relies on duplicate marking by a \npreceding program using the BAM_FDUP flag. This makes sense when unique molecular \nidentifiers (UMI) are used.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--extra_information", + "alternatives" : [ + "-X" + ], + "description" : "To reduce the runtime and file size, by default, the columns 'fusion_transcript', \n'peptide_sequence', and 'read_identifiers' are left empty in the file containing \ndiscarded fusion candidates (see parameter -O). When this flag is set, this extra \ninformation is reported in the discarded fusions file.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--fill_gaps", + "alternatives" : [ + "-I" + ], + "description" : "If assembly of the fusion transcript sequence from the supporting reads is incomplete \n(denoted as '...'), fill the gaps using the assembly sequence wherever possible. \n", + "direction" : "input" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Detect gene fusions from RNA-Seq data", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "cpus" : 1, + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "Gene fusion", + "RNA-Seq" + ], + "license" : "MIT", + "references" : { + "doi" : [ + "10.1101/gr.257246.119" + ] + }, + "links" : { + "repository" : "https://github.com/suhrig/arriba", + "homepage" : "https://arriba.readthedocs.io/en/latest/", + "documentation" : "https://arriba.readthedocs.io/en/latest/" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/arriba:2.4.0--h0033a41_2", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "arriba -h | grep 'Version:' 2>&1 | sed 's/Version:\\\\s\\\\(.*\\\\)/arriba: \\"\\\\1\\"/' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/arriba/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/arriba", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_BAM+x} ]; then echo "${VIASH_PAR_BAM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bam='&'#" ; else echo "# par_bam="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOME+x} ]; then echo "${VIASH_PAR_GENOME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_genome='&'#" ; else echo "# par_genome="; fi ) +$( if [ ! -z ${VIASH_PAR_GENE_ANNOTATION+x} ]; then echo "${VIASH_PAR_GENE_ANNOTATION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_gene_annotation='&'#" ; else echo "# par_gene_annotation="; fi ) +$( if [ ! -z ${VIASH_PAR_KNOWN_FUSIONS+x} ]; then echo "${VIASH_PAR_KNOWN_FUSIONS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_known_fusions='&'#" ; else echo "# par_known_fusions="; fi ) +$( if [ ! -z ${VIASH_PAR_BLACKLIST+x} ]; then echo "${VIASH_PAR_BLACKLIST}" | sed "s#'#'\\"'\\"'#g;s#.*#par_blacklist='&'#" ; else echo "# par_blacklist="; fi ) +$( if [ ! -z ${VIASH_PAR_STRUCTURAL_VARIANTS+x} ]; then echo "${VIASH_PAR_STRUCTURAL_VARIANTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_structural_variants='&'#" ; else echo "# par_structural_variants="; fi ) +$( if [ ! -z ${VIASH_PAR_TAGS+x} ]; then echo "${VIASH_PAR_TAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_tags='&'#" ; else echo "# par_tags="; fi ) +$( if [ ! -z ${VIASH_PAR_PROTEIN_DOMAINS+x} ]; then echo "${VIASH_PAR_PROTEIN_DOMAINS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_protein_domains='&'#" ; else echo "# par_protein_domains="; fi ) +$( if [ ! -z ${VIASH_PAR_FUSIONS+x} ]; then echo "${VIASH_PAR_FUSIONS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fusions='&'#" ; else echo "# par_fusions="; fi ) +$( if [ ! -z ${VIASH_PAR_FUSIONS_DISCARDED+x} ]; then echo "${VIASH_PAR_FUSIONS_DISCARDED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fusions_discarded='&'#" ; else echo "# par_fusions_discarded="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE+x} ]; then echo "${VIASH_PAR_MAX_GENOMIC_BREAKPOINT_DISTANCE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_genomic_breakpoint_distance='&'#" ; else echo "# par_max_genomic_breakpoint_distance="; fi ) +$( if [ ! -z ${VIASH_PAR_STRANDEDNESS+x} ]; then echo "${VIASH_PAR_STRANDEDNESS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_strandedness='&'#" ; else echo "# par_strandedness="; fi ) +$( if [ ! -z ${VIASH_PAR_INTERESTING_CONTIGS+x} ]; then echo "${VIASH_PAR_INTERESTING_CONTIGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_interesting_contigs='&'#" ; else echo "# par_interesting_contigs="; fi ) +$( if [ ! -z ${VIASH_PAR_VIRAL_CONTIGS+x} ]; then echo "${VIASH_PAR_VIRAL_CONTIGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_viral_contigs='&'#" ; else echo "# par_viral_contigs="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_FILTERS+x} ]; then echo "${VIASH_PAR_DISABLE_FILTERS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_disable_filters='&'#" ; else echo "# par_disable_filters="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_E_VALUE+x} ]; then echo "${VIASH_PAR_MAX_E_VALUE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_e_value='&'#" ; else echo "# par_max_e_value="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_SUPPORTING_READS+x} ]; then echo "${VIASH_PAR_MIN_SUPPORTING_READS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_supporting_reads='&'#" ; else echo "# par_min_supporting_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_MISMAPPERS+x} ]; then echo "${VIASH_PAR_MAX_MISMAPPERS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_mismappers='&'#" ; else echo "# par_max_mismappers="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_HOMOLOG_IDENTITY+x} ]; then echo "${VIASH_PAR_MAX_HOMOLOG_IDENTITY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_homolog_identity='&'#" ; else echo "# par_max_homolog_identity="; fi ) +$( if [ ! -z ${VIASH_PAR_HOMOPOLYMER_LENGTH+x} ]; then echo "${VIASH_PAR_HOMOPOLYMER_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_homopolymer_length='&'#" ; else echo "# par_homopolymer_length="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_THROUGH_DISTANCE+x} ]; then echo "${VIASH_PAR_READ_THROUGH_DISTANCE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_read_through_distance='&'#" ; else echo "# par_read_through_distance="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ANCHOR_LENGTH+x} ]; then echo "${VIASH_PAR_MIN_ANCHOR_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_anchor_length='&'#" ; else echo "# par_min_anchor_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MANY_SPLICED_EVENTS+x} ]; then echo "${VIASH_PAR_MANY_SPLICED_EVENTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_many_spliced_events='&'#" ; else echo "# par_many_spliced_events="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_KMER_CONTENT+x} ]; then echo "${VIASH_PAR_MAX_KMER_CONTENT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_kmer_content='&'#" ; else echo "# par_max_kmer_content="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_MISMATCH_PVALUE+x} ]; then echo "${VIASH_PAR_MAX_MISMATCH_PVALUE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_mismatch_pvalue='&'#" ; else echo "# par_max_mismatch_pvalue="; fi ) +$( if [ ! -z ${VIASH_PAR_FRAGMENT_LENGTH+x} ]; then echo "${VIASH_PAR_FRAGMENT_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fragment_length='&'#" ; else echo "# par_fragment_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_READS+x} ]; then echo "${VIASH_PAR_MAX_READS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_reads='&'#" ; else echo "# par_max_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_QUANTILE+x} ]; then echo "${VIASH_PAR_QUANTILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_quantile='&'#" ; else echo "# par_quantile="; fi ) +$( if [ ! -z ${VIASH_PAR_EXONIC_FRACTION+x} ]; then echo "${VIASH_PAR_EXONIC_FRACTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_exonic_fraction='&'#" ; else echo "# par_exonic_fraction="; fi ) +$( if [ ! -z ${VIASH_PAR_TOP_N+x} ]; then echo "${VIASH_PAR_TOP_N}" | sed "s#'#'\\"'\\"'#g;s#.*#par_top_n='&'#" ; else echo "# par_top_n="; fi ) +$( if [ ! -z ${VIASH_PAR_COVERED_FRACTION+x} ]; then echo "${VIASH_PAR_COVERED_FRACTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_covered_fraction='&'#" ; else echo "# par_covered_fraction="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_ITD_LENGTH+x} ]; then echo "${VIASH_PAR_MAX_ITD_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_itd_length='&'#" ; else echo "# par_max_itd_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ITD_ALLELE_FRACTION+x} ]; then echo "${VIASH_PAR_MIN_ITD_ALLELE_FRACTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_itd_allele_fraction='&'#" ; else echo "# par_min_itd_allele_fraction="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ITD_SUPPORTING_READS+x} ]; then echo "${VIASH_PAR_MIN_ITD_SUPPORTING_READS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_itd_supporting_reads='&'#" ; else echo "# par_min_itd_supporting_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_SKIP_DUPLICATE_MARKING+x} ]; then echo "${VIASH_PAR_SKIP_DUPLICATE_MARKING}" | sed "s#'#'\\"'\\"'#g;s#.*#par_skip_duplicate_marking='&'#" ; else echo "# par_skip_duplicate_marking="; fi ) +$( if [ ! -z ${VIASH_PAR_EXTRA_INFORMATION+x} ]; then echo "${VIASH_PAR_EXTRA_INFORMATION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_extra_information='&'#" ; else echo "# par_extra_information="; fi ) +$( if [ ! -z ${VIASH_PAR_FILL_GAPS+x} ]; then echo "${VIASH_PAR_FILL_GAPS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fill_gaps='&'#" ; else echo "# par_fill_gaps="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# unset flags +[[ "\\$par_skip_duplicate_marking" == "false" ]] && unset par_skip_duplicate_marking +[[ "\\$par_extra_information" == "false" ]] && unset par_extra_information +[[ "\\$par_fill_gaps" == "false" ]] && unset par_fill_gaps + +# replace ';' with ',' +par_interesting_contigs=\\$(echo \\$par_interesting_contigs | tr ';' ',') +par_viral_contigs=\\$(echo \\$par_viral_contigs | tr ';' ',') +par_disable_filters=\\$(echo \\$par_disable_filters | tr ';' ',') + +# run arriba +arriba \\\\ + -x "\\$par_bam" \\\\ + -a "\\$par_genome" \\\\ + -g "\\$par_gene_annotation" \\\\ + -o "\\$par_fusions" \\\\ + \\${par_known_fusions:+-k "\\${par_known_fusions}"} \\\\ + \\${par_blacklist:+-b "\\${par_blacklist}"} \\\\ + \\${par_structural_variants:+-d "\\${par_structural_variants}"} \\\\ + \\${par_tags:+-t "\\${par_tags}"} \\\\ + \\${par_protein_domains:+-p "\\${par_protein_domains}"} \\\\ + \\${par_fusions_discarded:+-O "\\${par_fusions_discarded}"} \\\\ + \\${par_max_genomic_breakpoint_distance:+-D "\\${par_max_genomic_breakpoint_distance}"} \\\\ + \\${par_strandedness:+-s "\\${par_strandedness}"} \\\\ + \\${par_interesting_contigs:+-i "\\${par_interesting_contigs}"} \\\\ + \\${par_viral_contigs:+-v "\\${par_viral_contigs}"} \\\\ + \\${par_disable_filters:+-f "\\${par_disable_filters}"} \\\\ + \\${par_max_e_value:+-E "\\${par_max_e_value}"} \\\\ + \\${par_min_supporting_reads:+-S "\\${par_min_supporting_reads}"} \\\\ + \\${par_max_mismappers:+-m "\\${par_max_mismappers}"} \\\\ + \\${par_max_homolog_identity:+-L "\\${par_max_homolog_identity}"} \\\\ + \\${par_homopolymer_length:+-H "\\${par_homopolymer_length}"} \\\\ + \\${par_read_through_distance:+-R "\\${par_read_through_distance}"} \\\\ + \\${par_min_anchor_length:+-A "\\${par_min_anchor_length}"} \\\\ + \\${par_many_spliced_events:+-M "\\${par_many_spliced_events}"} \\\\ + \\${par_max_kmer_content:+-K "\\${par_max_kmer_content}"} \\\\ + \\${par_max_mismatch_pvalue:+-V "\\${par_max_mismatch_pvalue}"} \\\\ + \\${par_fragment_length:+-F "\\${par_fragment_length}"} \\\\ + \\${par_max_reads:+-U "\\${par_max_reads}"} \\\\ + \\${par_quantile:+-Q "\\${par_quantile}"} \\\\ + \\${par_exonic_fraction:+-e "\\${par_exonic_fraction}"} \\\\ + \\${par_top_n:+-T "\\${par_top_n}"} \\\\ + \\${par_covered_fraction:+-C "\\${par_covered_fraction}"} \\\\ + \\${par_max_itd_length:+-l "\\${par_max_itd_length}"} \\\\ + \\${par_min_itd_allele_fraction:+-z "\\${par_min_itd_allele_fraction}"} \\\\ + \\${par_min_itd_supporting_reads:+-Z "\\${par_min_itd_supporting_reads}"} \\\\ + \\${par_skip_duplicate_marking:+-u} \\\\ + \\${par_extra_information:+-X} \\\\ + \\${par_fill_gaps:+-I} +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/arriba", + "tag" : "main" + }, + "cpus" : 1, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/arriba/nextflow.config b/target/nextflow/arriba/nextflow.config new file mode 100644 index 00000000..cc0f4fb4 --- /dev/null +++ b/target/nextflow/arriba/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'arriba' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Detect gene fusions from RNA-Seq data' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/arriba/nextflow_schema.json b/target/nextflow/arriba/nextflow_schema.json new file mode 100644 index 00000000..d9b5968e --- /dev/null +++ b/target/nextflow/arriba/nextflow_schema.json @@ -0,0 +1,467 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "arriba", +"description": "Detect gene fusions from RNA-Seq data", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "bam": { + "type": + "string", + "description": "Type: `file`, required, example: `Aligned.out.bam`. File in SAM/BAM/CRAM format with main alignments as generated by STAR\n(Aligned", + "help_text": "Type: `file`, required, example: `Aligned.out.bam`. File in SAM/BAM/CRAM format with main alignments as generated by STAR\n(Aligned.out.sam). Arriba extracts candidate reads from this file.\n" + + } + + + , + "genome": { + "type": + "string", + "description": "Type: `file`, required, example: `assembly.fa`. FastA file with genome sequence (assembly)", + "help_text": "Type: `file`, required, example: `assembly.fa`. FastA file with genome sequence (assembly). The file may be gzip-compressed. An \nindex with the file extension .fai must exist only if CRAM files are processed.\n" + + } + + + , + "gene_annotation": { + "type": + "string", + "description": "Type: `file`, required, example: `annotation.gtf`. GTF file with gene annotation", + "help_text": "Type: `file`, required, example: `annotation.gtf`. GTF file with gene annotation. The file may be gzip-compressed.\n" + + } + + + , + "known_fusions": { + "type": + "string", + "description": "Type: `file`, example: `known_fusions.tsv`. File containing known/recurrent fusions", + "help_text": "Type: `file`, example: `known_fusions.tsv`. File containing known/recurrent fusions. Some cancer entities are often \ncharacterized by fusions between the same pair of genes. In order to boost \nsensitivity, a list of known fusions can be supplied using this parameter. The list \nmust contain two columns with the names of the fused genes, separated by tabs.\n" + + } + + + , + "blacklist": { + "type": + "string", + "description": "Type: `file`, example: `blacklist.tsv`. File containing blacklisted events (recurrent artifacts and transcripts \nobserved in healthy tissue)", + "help_text": "Type: `file`, example: `blacklist.tsv`. File containing blacklisted events (recurrent artifacts and transcripts \nobserved in healthy tissue).\n" + + } + + + , + "structural_variants": { + "type": + "string", + "description": "Type: `file`, example: `structural_variants_from_WGS.tsv`. Tab-separated file with coordinates of structural variants found using \nwhole-genome sequencing data", + "help_text": "Type: `file`, example: `structural_variants_from_WGS.tsv`. Tab-separated file with coordinates of structural variants found using \nwhole-genome sequencing data. These coordinates serve to increase sensitivity \ntowards weakly expressed fusions and to eliminate fusions with low evidence. \n" + + } + + + , + "tags": { + "type": + "string", + "description": "Type: `file`, example: `tags.tsv`. Tab-separated file containing fusions to annotate with tags in the \u0027tags\u0027 column", + "help_text": "Type: `file`, example: `tags.tsv`. Tab-separated file containing fusions to annotate with tags in the \u0027tags\u0027 column. \nThe first two columns specify the genes; the third column specifies the tag. The \nfile may be gzip-compressed. \n" + + } + + + , + "protein_domains": { + "type": + "string", + "description": "Type: `file`, example: `protein_domains.gff3`. File in GFF3 format containing coordinates of the protein domains of genes", + "help_text": "Type: `file`, example: `protein_domains.gff3`. File in GFF3 format containing coordinates of the protein domains of genes. The\nprotein domains retained in a fusion are listed in the column\n\u0027retained_protein_domains\u0027. The file may be gzip-compressed.\n" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "fusions": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.fusions.tsv`, example: `fusions.tsv`. Output file with fusions that have passed all filters", + "help_text": "Type: `file`, required, default: `$id.$key.fusions.tsv`, example: `fusions.tsv`. Output file with fusions that have passed all filters.\n" + , + "default": "$id.$key.fusions.tsv" + } + + + , + "fusions_discarded": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.fusions_discarded.tsv`, example: `fusions.discarded.tsv`. Output file with fusions that were discarded due to filtering", + "help_text": "Type: `file`, default: `$id.$key.fusions_discarded.tsv`, example: `fusions.discarded.tsv`. Output file with fusions that were discarded due to filtering. \n" + , + "default": "$id.$key.fusions_discarded.tsv" + } + + +} +}, + + + "arguments" : { + "title": "Arguments", + "type": "object", + "description": "No description", + "properties": { + + + "max_genomic_breakpoint_distance": { + "type": + "string", + "description": "Type: `long`. When a file with genomic breakpoints obtained via \nwhole-genome sequencing is supplied via the --structural_variants\nparameter, this parameter determines how far a \ngenomic breakpoint may be away from a \ntranscriptomic breakpoint to consider it as a \nrelated event", + "help_text": "Type: `long`. When a file with genomic breakpoints obtained via \nwhole-genome sequencing is supplied via the --structural_variants\nparameter, this parameter determines how far a \ngenomic breakpoint may be away from a \ntranscriptomic breakpoint to consider it as a \nrelated event. For events inside genes, the \ndistance is added to the end of the gene; for \nintergenic events, the distance threshold is \napplied as is. Default: 100000.\n" + + } + + + , + "strandedness": { + "type": + "string", + "description": "Type: `string`, choices: ``auto`, `yes`, `no`, `reverse``. Whether a strand-specific protocol was used for library preparation, \nand if so, the type of strandedness (auto/yes/no/reverse)", + "help_text": "Type: `string`, choices: ``auto`, `yes`, `no`, `reverse``. Whether a strand-specific protocol was used for library preparation, \nand if so, the type of strandedness (auto/yes/no/reverse). When \nunstranded data is processed, the strand can sometimes be inferred from \nsplice-patterns. But in unclear situations, stranded data helps \nresolve ambiguities. Default: auto\n", + "enum": ["auto", "yes", "no", "reverse"] + + + } + + + , + "interesting_contigs": { + "type": + "string", + "description": "Type: List of `string`, example: `1:2:AC_*:NC_*`, multiple_sep: `\":\"`. List of interesting contigs", + "help_text": "Type: List of `string`, example: `1:2:AC_*:NC_*`, multiple_sep: `\":\"`. List of interesting contigs. Fusions between genes \non other contigs are ignored. Contigs can be specified with or without the \nprefix \"chr\". Asterisks (*) are treated as wild-cards. \nDefault: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 X Y AC_* NC_*\n" + + } + + + , + "viral_contigs": { + "type": + "string", + "description": "Type: List of `string`, example: `AC_*:NC_*`, multiple_sep: `\":\"`. List of viral contigs", + "help_text": "Type: List of `string`, example: `AC_*:NC_*`, multiple_sep: `\":\"`. List of viral contigs. Asterisks (*) are treated as \nwild-cards.\nDefault: AC_* NC_*\n" + + } + + + , + "disable_filters": { + "type": + "string", + "description": "Type: List of `string`, multiple_sep: `\":\"`, choices: ``homologs`, `low_entropy`, `isoforms`, `top_expressed_viral_contigs`, `viral_contigs`, `uninteresting_contigs`, `non_coding_neighbors`, `mismatches`, `duplicates`, `no_genomic_support`, `genomic_support`, `intronic`, `end_to_end`, `relative_support`, `low_coverage_viral_contigs`, `merge_adjacent`, `mismappers`, `multimappers`, `same_gene`, `long_gap`, `internal_tandem_duplication`, `small_insert_size`, `read_through`, `inconsistently_clipped`, `intragenic_exonic`, `marginal_read_through`, `spliced`, `hairpin`, `blacklist`, `min_support`, `select_best`, `in_vitro`, `short_anchor`, `known_fusions`, `no_coverage`, `homopolymer`, `many_spliced``. List of filters to disable", + "help_text": "Type: List of `string`, multiple_sep: `\":\"`, choices: ``homologs`, `low_entropy`, `isoforms`, `top_expressed_viral_contigs`, `viral_contigs`, `uninteresting_contigs`, `non_coding_neighbors`, `mismatches`, `duplicates`, `no_genomic_support`, `genomic_support`, `intronic`, `end_to_end`, `relative_support`, `low_coverage_viral_contigs`, `merge_adjacent`, `mismappers`, `multimappers`, `same_gene`, `long_gap`, `internal_tandem_duplication`, `small_insert_size`, `read_through`, `inconsistently_clipped`, `intragenic_exonic`, `marginal_read_through`, `spliced`, `hairpin`, `blacklist`, `min_support`, `select_best`, `in_vitro`, `short_anchor`, `known_fusions`, `no_coverage`, `homopolymer`, `many_spliced``. List of filters to disable. By default all filters are \nenabled. \n", + "enum": ["homologs", "low_entropy", "isoforms", "top_expressed_viral_contigs", "viral_contigs", "uninteresting_contigs", "non_coding_neighbors", "mismatches", "duplicates", "no_genomic_support", "genomic_support", "intronic", "end_to_end", "relative_support", "low_coverage_viral_contigs", "merge_adjacent", "mismappers", "multimappers", "same_gene", "long_gap", "internal_tandem_duplication", "small_insert_size", "read_through", "inconsistently_clipped", "intragenic_exonic", "marginal_read_through", "spliced", "hairpin", "blacklist", "min_support", "select_best", "in_vitro", "short_anchor", "known_fusions", "no_coverage", "homopolymer", "many_spliced"] + + + } + + + , + "max_e_value": { + "type": + "number", + "description": "Type: `double`. Arriba estimates the number of fusions with a given number of supporting \nreads which one would expect to see by random chance", + "help_text": "Type: `double`. Arriba estimates the number of fusions with a given number of supporting \nreads which one would expect to see by random chance. If the expected number \nof fusions (e-value) is higher than this threshold, the fusion is \ndiscarded by the \u0027relative_support\u0027 filter. Note: Increasing this \nthreshold can dramatically increase the number of false positives and may \nincrease the runtime of resource-intensive steps. Fractional values are \npossible. Default: 0.300000 \n" + + } + + + , + "min_supporting_reads": { + "type": + "integer", + "description": "Type: `integer`, example: `2`. The \u0027min_support\u0027 filter discards all fusions with fewer than \nthis many supporting reads (split reads and discordant mates \ncombined)", + "help_text": "Type: `integer`, example: `2`. The \u0027min_support\u0027 filter discards all fusions with fewer than \nthis many supporting reads (split reads and discordant mates \ncombined). Default: 2 \n" + + } + + + , + "max_mismappers": { + "type": + "number", + "description": "Type: `double`, example: `0.8`. When more than this fraction of supporting reads turns out to be \nmismappers, the \u0027mismappers\u0027 filter discards the fusion", + "help_text": "Type: `double`, example: `0.8`. When more than this fraction of supporting reads turns out to be \nmismappers, the \u0027mismappers\u0027 filter discards the fusion. Default: \n0.800000\n" + + } + + + , + "max_homolog_identity": { + "type": + "number", + "description": "Type: `double`, example: `0.3`. Genes with more than the given fraction of sequence identity are \nconsidered homologs and removed by the \u0027homologs\u0027 filter", + "help_text": "Type: `double`, example: `0.3`. Genes with more than the given fraction of sequence identity are \nconsidered homologs and removed by the \u0027homologs\u0027 filter. \nDefault: 0.300000 \n" + + } + + + , + "homopolymer_length": { + "type": + "integer", + "description": "Type: `integer`, example: `6`. The \u0027homopolymer\u0027 filter removes breakpoints adjacent to \nhomopolymers of the given length or more", + "help_text": "Type: `integer`, example: `6`. The \u0027homopolymer\u0027 filter removes breakpoints adjacent to \nhomopolymers of the given length or more. Default: 6\n" + + } + + + , + "read_through_distance": { + "type": + "integer", + "description": "Type: `integer`, example: `10000`. The \u0027read_through\u0027 filter removes read-through fusions \nwhere the breakpoints are less than the given distance away \nfrom each other", + "help_text": "Type: `integer`, example: `10000`. The \u0027read_through\u0027 filter removes read-through fusions \nwhere the breakpoints are less than the given distance away \nfrom each other. Default: 10000 \n" + + } + + + , + "min_anchor_length": { + "type": + "integer", + "description": "Type: `integer`, example: `23`. Alignment artifacts are often characterized by split reads coming \nfrom only one gene and no discordant mates", + "help_text": "Type: `integer`, example: `23`. Alignment artifacts are often characterized by split reads coming \nfrom only one gene and no discordant mates. Moreover, the split \nreads only align to a short stretch in one of the genes. The \n\u0027short_anchor\u0027 filter removes these fusions. This parameter sets \nthe threshold in bp for what the filter considers short. Default: 23 \n" + + } + + + , + "many_spliced_events": { + "type": + "integer", + "description": "Type: `integer`, example: `4`. The \u0027many_spliced\u0027 filter recovers fusions between genes that \nhave at least this many spliced breakpoints", + "help_text": "Type: `integer`, example: `4`. The \u0027many_spliced\u0027 filter recovers fusions between genes that \nhave at least this many spliced breakpoints. Default: 4\n" + + } + + + , + "max_kmer_content": { + "type": + "number", + "description": "Type: `double`, example: `0.6`. The \u0027low_entropy\u0027 filter removes reads with repetitive 3-mers", + "help_text": "Type: `double`, example: `0.6`. The \u0027low_entropy\u0027 filter removes reads with repetitive 3-mers. If \nthe 3-mers make up more than the given fraction of the sequence, then \nthe read is discarded. Default: 0.600000 \n" + + } + + + , + "max_mismatch_pvalue": { + "type": + "number", + "description": "Type: `double`, example: `0.05`. The \u0027mismatches\u0027 filter uses a binomial model to calculate a \np-value for observing a given number of mismatches in a read", + "help_text": "Type: `double`, example: `0.05`. The \u0027mismatches\u0027 filter uses a binomial model to calculate a \np-value for observing a given number of mismatches in a read. If \nthe number of mismatches is too high, the read is discarded. \nDefault: 0.010000 \n" + + } + + + , + "fragment_length": { + "type": + "integer", + "description": "Type: `integer`, example: `200`. When paired-end data is given, the fragment length is estimated \nautomatically and this parameter has no effect", + "help_text": "Type: `integer`, example: `200`. When paired-end data is given, the fragment length is estimated \nautomatically and this parameter has no effect. But when single-end \ndata is given, the mean fragment length should be specified to \neffectively filter fusions that arise from hairpin structures. \nDefault: 200 \n" + + } + + + , + "max_reads": { + "type": + "integer", + "description": "Type: `integer`, example: `300`. Subsample fusions with more than the given number of supporting reads", + "help_text": "Type: `integer`, example: `300`. Subsample fusions with more than the given number of supporting reads. This \nimproves performance without compromising sensitivity, as long as the \nthreshold is high. Counting of supporting reads beyond the threshold is \ninaccurate, obviously. Default: 300 \n" + + } + + + , + "quantile": { + "type": + "number", + "description": "Type: `double`, example: `0.998`. Highly expressed genes are prone to produce artifacts during library \npreparation", + "help_text": "Type: `double`, example: `0.998`. Highly expressed genes are prone to produce artifacts during library \npreparation. Genes with an expression above the given quantile are eligible \nfor filtering by the \u0027in_vitro\u0027 filter. Default: 0.998000\n" + + } + + + , + "exonic_fraction": { + "type": + "number", + "description": "Type: `double`, example: `0.33`. The breakpoints of false-positive predictions of intragenic events \nare often both in exons", + "help_text": "Type: `double`, example: `0.33`. The breakpoints of false-positive predictions of intragenic events \nare often both in exons. True predictions are more likely to have at \nleast one breakpoint in an intron, because introns are larger. If the \nfraction of exonic sequence between two breakpoints is smaller than \nthe given fraction, the \u0027intragenic_exonic\u0027 filter discards the \nevent. Default: 0.330000 \n" + + } + + + , + "top_n": { + "type": + "integer", + "description": "Type: `integer`, example: `5`. Only report viral integration sites of the top N most highly expressed viral \ncontigs", + "help_text": "Type: `integer`, example: `5`. Only report viral integration sites of the top N most highly expressed viral \ncontigs. Default: 5\n" + + } + + + , + "covered_fraction": { + "type": + "number", + "description": "Type: `double`, example: `0.05`. Ignore virally associated events if the virus is not fully \nexpressed, i", + "help_text": "Type: `double`, example: `0.05`. Ignore virally associated events if the virus is not fully \nexpressed, i.e., less than the given fraction of the viral contig is \ntranscribed. Default: 0.050000 \n" + + } + + + , + "max_itd_length": { + "type": + "integer", + "description": "Type: `integer`, example: `100`. Maximum length of internal tandem duplications", + "help_text": "Type: `integer`, example: `100`. Maximum length of internal tandem duplications. Note: Increasing \nthis value beyond the default can impair performance and lead to many \nfalse positives. Default: 100 \n" + + } + + + , + "min_itd_allele_fraction": { + "type": + "number", + "description": "Type: `double`, example: `0.07`. Required fraction of supporting reads to report an internal \ntandem duplication", + "help_text": "Type: `double`, example: `0.07`. Required fraction of supporting reads to report an internal \ntandem duplication. Default: 0.070000 \n" + + } + + + , + "min_itd_supporting_reads": { + "type": + "integer", + "description": "Type: `integer`, example: `10`. Required absolute number of supporting reads to report an \ninternal tandem duplication", + "help_text": "Type: `integer`, example: `10`. Required absolute number of supporting reads to report an \ninternal tandem duplication. Default: 10 \n" + + } + + + , + "skip_duplicate_marking": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Instead of performing duplicate marking itself, Arriba relies on duplicate marking by a \npreceding program using the BAM_FDUP flag", + "help_text": "Type: `boolean_true`, default: `false`. Instead of performing duplicate marking itself, Arriba relies on duplicate marking by a \npreceding program using the BAM_FDUP flag. This makes sense when unique molecular \nidentifiers (UMI) are used.\n" + , + "default": "False" + } + + + , + "extra_information": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. To reduce the runtime and file size, by default, the columns \u0027fusion_transcript\u0027, \n\u0027peptide_sequence\u0027, and \u0027read_identifiers\u0027 are left empty in the file containing \ndiscarded fusion candidates (see parameter -O)", + "help_text": "Type: `boolean_true`, default: `false`. To reduce the runtime and file size, by default, the columns \u0027fusion_transcript\u0027, \n\u0027peptide_sequence\u0027, and \u0027read_identifiers\u0027 are left empty in the file containing \ndiscarded fusion candidates (see parameter -O). When this flag is set, this extra \ninformation is reported in the discarded fusions file.\n" + , + "default": "False" + } + + + , + "fill_gaps": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. If assembly of the fusion transcript sequence from the supporting reads is incomplete \n(denoted as \u0027", + "help_text": "Type: `boolean_true`, default: `false`. If assembly of the fusion transcript sequence from the supporting reads is incomplete \n(denoted as \u0027...\u0027), fill the gaps using the assembly sequence wherever possible. \n" + , + "default": "False" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/arguments" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/bcl_convert/.config.vsh.yaml b/target/nextflow/bcl_convert/.config.vsh.yaml new file mode 100644 index 00000000..a410028c --- /dev/null +++ b/target/nextflow/bcl_convert/.config.vsh.yaml @@ -0,0 +1,412 @@ +name: "bcl_convert" +version: "main" +argument_groups: +- name: "Input arguments" + arguments: + - type: "file" + name: "--bcl_input_directory" + alternatives: + - "-i" + description: "Input run directory" + info: null + example: + - "bcl_dir" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--sample_sheet" + alternatives: + - "-s" + description: "Path to SampleSheet.csv file (default searched for in --bcl_input_directory)" + info: null + example: + - "bcl_dir/sample_sheet.csv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--run_info" + description: "Path to RunInfo.xml file (default root of BCL input directory)" + info: null + example: + - "bcl_dir/RunInfo.xml" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Lane and tile settings" + arguments: + - type: "integer" + name: "--bcl_only_lane" + description: "Convert only specified lane number (default all lanes)" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--first_tile_only" + description: "Only convert first tile of input (for testing & debugging)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--tiles" + description: "Process only a subset of tiles by a regular expression" + info: null + example: + - "s_[0-9]+_1" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--exclude_tiles" + description: "Exclude set of tiles by a regular expression" + info: null + example: + - "s_[0-9]+_1" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Resource arguments" + arguments: + - type: "boolean" + name: "--shared_thread_odirect_output" + description: "Use linux native asynchronous io (io_submit) for file output (Default=false)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bcl_num_parallel_tiles" + description: "\\# of tiles to process in parallel (default 1)" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bcl_num_conversion_threads" + description: "\\# of threads for conversion (per tile, default # cpu threads)" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bcl_num_compression_threads" + description: "\\# of threads for fastq.gz output compression (per tile, default\ + \ # cpu threads, or HW+12)" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bcl_num_decompression_threads" + description: "\\# of threads for bcl/cbcl input decompression (per tile, default\ + \ half # cpu threads, or HW+8). Only applies when preloading files" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Run arguments" + arguments: + - type: "boolean" + name: "--bcl_only_matched_reads" + description: "For pure BCL conversion, do not output files for 'Undetermined'\ + \ [unmatched] reads (output by default)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--no_lane_splitting" + description: "Do not split FASTQ file by lane (false by default)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--num_unknown_barcodes_reported" + description: "\\# of Top Unknown Barcodes to output (1000 by default)" + info: null + example: + - 1000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--bcl_validate_sample_sheet_only" + description: "Only validate RunInfo.xml & SampleSheet files (produce no FASTQ\ + \ files)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--strict_mode" + description: "Abort if any files are missing (false by default)" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--sample_name_column_enabled" + description: "Use sample sheet 'Sample_Name' column when naming fastq files &\ + \ subdirectories" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output arguments" + arguments: + - type: "file" + name: "--output_directory" + alternatives: + - "-o" + description: "Output directory containig fastq files" + info: null + example: + - "fastq_dir" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean" + name: "--bcl_sampleproject_subdirectories" + description: "Output to subdirectories based upon sample sheet 'Sample_Project'\ + \ column" + info: null + example: + - true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--fastq_gzip_compression_level" + description: "Set fastq output compression level 0-9 (default 1)" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reports" + description: "Reports directory" + info: null + example: + - "reports_dir" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--logs" + description: "Reports directory" + info: null + example: + - "logs_dir" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Convert bcl files to fastq files using bcl-convert.\nInformation about\ + \ upgrading from bcl2fastq via\n[Upgrading from bcl2fastq to BCL Convert](https://emea.support.illumina.com/bulletins/2020/10/upgrading-from-bcl2fastq-to-bcl-convert.html)\n\ + and [BCL Convert Compatible Products](https://support.illumina.com/sequencing/sequencing_software/bcl-convert/compatibility.html)\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +license: "MIT" +links: + repository: "https://github.com/viash-hub/biobox" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "debian:trixie-slim" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "apt" + packages: + - "wget" + - "gdb" + - "which" + - "hostname" + - "alien" + - "procps" + interactive: false + - type: "docker" + run: + - "wget https://s3.amazonaws.com/webdata.illumina.com/downloads/software/bcl-convert/bcl-convert-4.2.7-2.el8.x86_64.rpm\ + \ -O /tmp/bcl-convert.rpm && \\\nalien -i /tmp/bcl-convert.rpm && \\\nrm -rf\ + \ /var/lib/apt/lists/* && \\\nrm /tmp/bcl-convert.rpm\n" + - type: "docker" + run: + - "echo \"bcl-convert: \\\"$(bcl-convert -V 2>&1 >/dev/null | sed -n '/Version/\ + \ s/^bcl-convert\\ Version //p')\\\"\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/bcl_convert/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/bcl_convert" + executable: "target/nextflow/bcl_convert/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/bcl_convert/main.nf b/target/nextflow/bcl_convert/main.nf new file mode 100644 index 00000000..cdc631da --- /dev/null +++ b/target/nextflow/bcl_convert/main.nf @@ -0,0 +1,3852 @@ +// bcl_convert main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "bcl_convert", + "version" : "main", + "argument_groups" : [ + { + "name" : "Input arguments", + "arguments" : [ + { + "type" : "file", + "name" : "--bcl_input_directory", + "alternatives" : [ + "-i" + ], + "description" : "Input run directory", + "example" : [ + "bcl_dir" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--sample_sheet", + "alternatives" : [ + "-s" + ], + "description" : "Path to SampleSheet.csv file (default searched for in --bcl_input_directory)", + "example" : [ + "bcl_dir/sample_sheet.csv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--run_info", + "description" : "Path to RunInfo.xml file (default root of BCL input directory)", + "example" : [ + "bcl_dir/RunInfo.xml" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Lane and tile settings", + "arguments" : [ + { + "type" : "integer", + "name" : "--bcl_only_lane", + "description" : "Convert only specified lane number (default all lanes)", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean", + "name" : "--first_tile_only", + "description" : "Only convert first tile of input (for testing & debugging)", + "example" : [ + true + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--tiles", + "description" : "Process only a subset of tiles by a regular expression", + "example" : [ + "s_[0-9]+_1" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--exclude_tiles", + "description" : "Exclude set of tiles by a regular expression", + "example" : [ + "s_[0-9]+_1" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Resource arguments", + "arguments" : [ + { + "type" : "boolean", + "name" : "--shared_thread_odirect_output", + "description" : "Use linux native asynchronous io (io_submit) for file output (Default=false)", + "example" : [ + true + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--bcl_num_parallel_tiles", + "description" : "\\\\# of tiles to process in parallel (default 1)", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--bcl_num_conversion_threads", + "description" : "\\\\# of threads for conversion (per tile, default # cpu threads)", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--bcl_num_compression_threads", + "description" : "\\\\# of threads for fastq.gz output compression (per tile, default # cpu threads, or HW+12)", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--bcl_num_decompression_threads", + "description" : "\\\\# of threads for bcl/cbcl input decompression (per tile, default half # cpu threads, or HW+8). Only applies when preloading files", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Run arguments", + "arguments" : [ + { + "type" : "boolean", + "name" : "--bcl_only_matched_reads", + "description" : "For pure BCL conversion, do not output files for 'Undetermined' [unmatched] reads (output by default)", + "example" : [ + true + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean", + "name" : "--no_lane_splitting", + "description" : "Do not split FASTQ file by lane (false by default)", + "example" : [ + true + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--num_unknown_barcodes_reported", + "description" : "\\\\# of Top Unknown Barcodes to output (1000 by default)", + "example" : [ + 1000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean", + "name" : "--bcl_validate_sample_sheet_only", + "description" : "Only validate RunInfo.xml & SampleSheet files (produce no FASTQ files)", + "example" : [ + true + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean", + "name" : "--strict_mode", + "description" : "Abort if any files are missing (false by default)", + "example" : [ + true + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean", + "name" : "--sample_name_column_enabled", + "description" : "Use sample sheet 'Sample_Name' column when naming fastq files & subdirectories", + "example" : [ + true + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Output arguments", + "arguments" : [ + { + "type" : "file", + "name" : "--output_directory", + "alternatives" : [ + "-o" + ], + "description" : "Output directory containig fastq files", + "example" : [ + "fastq_dir" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean", + "name" : "--bcl_sampleproject_subdirectories", + "description" : "Output to subdirectories based upon sample sheet 'Sample_Project' column", + "example" : [ + true + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--fastq_gzip_compression_level", + "description" : "Set fastq output compression level 0-9 (default 1)", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--reports", + "description" : "Reports directory", + "example" : [ + "reports_dir" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--logs", + "description" : "Reports directory", + "example" : [ + "logs_dir" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Convert bcl files to fastq files using bcl-convert.\nInformation about upgrading from bcl2fastq via\n[Upgrading from bcl2fastq to BCL Convert](https://emea.support.illumina.com/bulletins/2020/10/upgrading-from-bcl2fastq-to-bcl-convert.html)\nand [BCL Convert Compatible Products](https://support.illumina.com/sequencing/sequencing_software/bcl-convert/compatibility.html)\n", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "license" : "MIT", + "links" : { + "repository" : "https://github.com/viash-hub/biobox" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "debian:trixie-slim", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "apt", + "packages" : [ + "wget", + "gdb", + "which", + "hostname", + "alien", + "procps" + ], + "interactive" : false + }, + { + "type" : "docker", + "run" : [ + "wget https://s3.amazonaws.com/webdata.illumina.com/downloads/software/bcl-convert/bcl-convert-4.2.7-2.el8.x86_64.rpm -O /tmp/bcl-convert.rpm && \\\\\nalien -i /tmp/bcl-convert.rpm && \\\\\nrm -rf /var/lib/apt/lists/* && \\\\\nrm /tmp/bcl-convert.rpm\n" + ] + }, + { + "type" : "docker", + "run" : [ + "echo \\"bcl-convert: \\\\\\"$(bcl-convert -V 2>&1 >/dev/null | sed -n '/Version/ s/^bcl-convert\\\\ Version //p')\\\\\\"\\" > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/bcl_convert/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/bcl_convert", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_BCL_INPUT_DIRECTORY+x} ]; then echo "${VIASH_PAR_BCL_INPUT_DIRECTORY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bcl_input_directory='&'#" ; else echo "# par_bcl_input_directory="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_SHEET+x} ]; then echo "${VIASH_PAR_SAMPLE_SHEET}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sample_sheet='&'#" ; else echo "# par_sample_sheet="; fi ) +$( if [ ! -z ${VIASH_PAR_RUN_INFO+x} ]; then echo "${VIASH_PAR_RUN_INFO}" | sed "s#'#'\\"'\\"'#g;s#.*#par_run_info='&'#" ; else echo "# par_run_info="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_ONLY_LANE+x} ]; then echo "${VIASH_PAR_BCL_ONLY_LANE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bcl_only_lane='&'#" ; else echo "# par_bcl_only_lane="; fi ) +$( if [ ! -z ${VIASH_PAR_FIRST_TILE_ONLY+x} ]; then echo "${VIASH_PAR_FIRST_TILE_ONLY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_first_tile_only='&'#" ; else echo "# par_first_tile_only="; fi ) +$( if [ ! -z ${VIASH_PAR_TILES+x} ]; then echo "${VIASH_PAR_TILES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_tiles='&'#" ; else echo "# par_tiles="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCLUDE_TILES+x} ]; then echo "${VIASH_PAR_EXCLUDE_TILES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_exclude_tiles='&'#" ; else echo "# par_exclude_tiles="; fi ) +$( if [ ! -z ${VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT+x} ]; then echo "${VIASH_PAR_SHARED_THREAD_ODIRECT_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_shared_thread_odirect_output='&'#" ; else echo "# par_shared_thread_odirect_output="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_NUM_PARALLEL_TILES+x} ]; then echo "${VIASH_PAR_BCL_NUM_PARALLEL_TILES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bcl_num_parallel_tiles='&'#" ; else echo "# par_bcl_num_parallel_tiles="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_NUM_CONVERSION_THREADS+x} ]; then echo "${VIASH_PAR_BCL_NUM_CONVERSION_THREADS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bcl_num_conversion_threads='&'#" ; else echo "# par_bcl_num_conversion_threads="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_NUM_COMPRESSION_THREADS+x} ]; then echo "${VIASH_PAR_BCL_NUM_COMPRESSION_THREADS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bcl_num_compression_threads='&'#" ; else echo "# par_bcl_num_compression_threads="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS+x} ]; then echo "${VIASH_PAR_BCL_NUM_DECOMPRESSION_THREADS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bcl_num_decompression_threads='&'#" ; else echo "# par_bcl_num_decompression_threads="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_ONLY_MATCHED_READS+x} ]; then echo "${VIASH_PAR_BCL_ONLY_MATCHED_READS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bcl_only_matched_reads='&'#" ; else echo "# par_bcl_only_matched_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_LANE_SPLITTING+x} ]; then echo "${VIASH_PAR_NO_LANE_SPLITTING}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_lane_splitting='&'#" ; else echo "# par_no_lane_splitting="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED+x} ]; then echo "${VIASH_PAR_NUM_UNKNOWN_BARCODES_REPORTED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_num_unknown_barcodes_reported='&'#" ; else echo "# par_num_unknown_barcodes_reported="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY+x} ]; then echo "${VIASH_PAR_BCL_VALIDATE_SAMPLE_SHEET_ONLY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bcl_validate_sample_sheet_only='&'#" ; else echo "# par_bcl_validate_sample_sheet_only="; fi ) +$( if [ ! -z ${VIASH_PAR_STRICT_MODE+x} ]; then echo "${VIASH_PAR_STRICT_MODE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_strict_mode='&'#" ; else echo "# par_strict_mode="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED+x} ]; then echo "${VIASH_PAR_SAMPLE_NAME_COLUMN_ENABLED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sample_name_column_enabled='&'#" ; else echo "# par_sample_name_column_enabled="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_DIRECTORY+x} ]; then echo "${VIASH_PAR_OUTPUT_DIRECTORY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_directory='&'#" ; else echo "# par_output_directory="; fi ) +$( if [ ! -z ${VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES+x} ]; then echo "${VIASH_PAR_BCL_SAMPLEPROJECT_SUBDIRECTORIES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bcl_sampleproject_subdirectories='&'#" ; else echo "# par_bcl_sampleproject_subdirectories="; fi ) +$( if [ ! -z ${VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL+x} ]; then echo "${VIASH_PAR_FASTQ_GZIP_COMPRESSION_LEVEL}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fastq_gzip_compression_level='&'#" ; else echo "# par_fastq_gzip_compression_level="; fi ) +$( if [ ! -z ${VIASH_PAR_REPORTS+x} ]; then echo "${VIASH_PAR_REPORTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_reports='&'#" ; else echo "# par_reports="; fi ) +$( if [ ! -z ${VIASH_PAR_LOGS+x} ]; then echo "${VIASH_PAR_LOGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_logs='&'#" ; else echo "# par_logs="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END +#!/bin/bash + +set -eo pipefail + +\\$(which bcl-convert) \\\\ + --bcl-input-directory "\\$par_bcl_input_directory" \\\\ + --output-directory "\\$par_output_directory" \\\\ + \\${par_sample_sheet:+ --sample-sheet "\\$par_sample_sheet"} \\\\ + \\${par_run_info:+ --run-info "\\$par_run_info"} \\\\ + \\${par_bcl_only_lane:+ --bcl-only-lane "\\$par_bcl_only_lane"} \\\\ + \\${par_first_tile_only:+ --first-tile-only "\\$par_first_tile_only"} \\\\ + \\${par_tiles:+ --tiles "\\$par_tiles"} \\\\ + \\${par_exclude_tiles:+ --exclude-tiles "\\$par_exclude_tiles"} \\\\ + \\${par_shared_thread_odirect_output:+ --shared-thread-odirect-output "\\$par_shared_thread_odirect_output"} \\\\ + \\${par_bcl_num_parallel_tiles:+ --bcl-num-parallel-tiles "\\$par_bcl_num_parallel_tiles"} \\\\ + \\${par_bcl_num_conversion_threads:+ --bcl-num-conversion-threads "\\$par_bcl_num_conversion_threads"} \\\\ + \\${par_bcl_num_compression_threads:+ --bcl-num-compression-threads "\\$par_bcl_num_compression_threads"} \\\\ + \\${par_bcl_num_decompression_threads:+ --bcl-num-decompression-threads "\\$par_bcl_num_decompression_threads"} \\\\ + \\${par_bcl_only_matched_reads:+ --bcl-only-matched-reads "\\$par_bcl_only_matched_reads"} \\\\ + \\${par_no_lane_splitting:+ --no-lane-splitting "\\$par_no_lane_splitting"} \\\\ + \\${par_num_unknown_barcodes_reported:+ --num-unknown-barcodes-reported "\\$par_num_unknown_barcodes_reported"} \\\\ + \\${par_bcl_validate_sample_sheet_only:+ --bcl-validate-sample-sheet-only "\\$par_bcl_validate_sample_sheet_only"} \\\\ + \\${par_strict_mode:+ --strict-mode "\\$par_strict_mode"} \\\\ + \\${par_sample_name_column_enabled:+ --sample-name-column-enabled "\\$par_sample_name_column_enabled"} \\\\ + \\${par_bcl_sampleproject_subdirectories:+ --bcl-sampleproject-subdirectories "\\$par_bcl_sampleproject_subdirectories"} \\\\ + \\${par_fastq_gzip_compression_level:+ --fastq-gzip-compression-level "\\$par_fastq_gzip_compression_level"} + +if [ ! -z "\\$par_reports" ]; then + echo "Moving reports to their own location" + mv "\\${par_output_directory}/Reports" "\\$par_reports" +else + echo "Leaving reports alone" +fi + +if [ ! -z "\\$par_logs" ]; then + echo "Moving logs to their own location" + mv "\\${par_output_directory}/Logs" "\\$par_logs" +else + echo "Leaving logs alone" +fi +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/bcl_convert", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/bcl_convert/nextflow.config b/target/nextflow/bcl_convert/nextflow.config new file mode 100644 index 00000000..47a9bd3b --- /dev/null +++ b/target/nextflow/bcl_convert/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'bcl_convert' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Convert bcl files to fastq files using bcl-convert.\nInformation about upgrading from bcl2fastq via\n[Upgrading from bcl2fastq to BCL Convert](https://emea.support.illumina.com/bulletins/2020/10/upgrading-from-bcl2fastq-to-bcl-convert.html)\nand [BCL Convert Compatible Products](https://support.illumina.com/sequencing/sequencing_software/bcl-convert/compatibility.html)\n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/bcl_convert/nextflow_schema.json b/target/nextflow/bcl_convert/nextflow_schema.json new file mode 100644 index 00000000..5fa9b054 --- /dev/null +++ b/target/nextflow/bcl_convert/nextflow_schema.json @@ -0,0 +1,349 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "bcl_convert", +"description": "Convert bcl files to fastq files using bcl-convert.\nInformation about upgrading from bcl2fastq via\n[Upgrading from bcl2fastq to BCL Convert](https://emea.support.illumina.com/bulletins/2020/10/upgrading-from-bcl2fastq-to-bcl-convert.html)\nand [BCL Convert Compatible Products](https://support.illumina.com/sequencing/sequencing_software/bcl-convert/compatibility.html)\n", +"type": "object", +"definitions": { + + + + "input arguments" : { + "title": "Input arguments", + "type": "object", + "description": "No description", + "properties": { + + + "bcl_input_directory": { + "type": + "string", + "description": "Type: `file`, required, example: `bcl_dir`. Input run directory", + "help_text": "Type: `file`, required, example: `bcl_dir`. Input run directory" + + } + + + , + "sample_sheet": { + "type": + "string", + "description": "Type: `file`, example: `bcl_dir/sample_sheet.csv`. Path to SampleSheet", + "help_text": "Type: `file`, example: `bcl_dir/sample_sheet.csv`. Path to SampleSheet.csv file (default searched for in --bcl_input_directory)" + + } + + + , + "run_info": { + "type": + "string", + "description": "Type: `file`, example: `bcl_dir/RunInfo.xml`. Path to RunInfo", + "help_text": "Type: `file`, example: `bcl_dir/RunInfo.xml`. Path to RunInfo.xml file (default root of BCL input directory)" + + } + + +} +}, + + + "lane and tile settings" : { + "title": "Lane and tile settings", + "type": "object", + "description": "No description", + "properties": { + + + "bcl_only_lane": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. Convert only specified lane number (default all lanes)", + "help_text": "Type: `integer`, example: `1`. Convert only specified lane number (default all lanes)" + + } + + + , + "first_tile_only": { + "type": + "boolean", + "description": "Type: `boolean`, example: `true`. Only convert first tile of input (for testing \u0026 debugging)", + "help_text": "Type: `boolean`, example: `true`. Only convert first tile of input (for testing \u0026 debugging)" + + } + + + , + "tiles": { + "type": + "string", + "description": "Type: `string`, example: `s_[0-9]+_1`. Process only a subset of tiles by a regular expression", + "help_text": "Type: `string`, example: `s_[0-9]+_1`. Process only a subset of tiles by a regular expression" + + } + + + , + "exclude_tiles": { + "type": + "string", + "description": "Type: `string`, example: `s_[0-9]+_1`. Exclude set of tiles by a regular expression", + "help_text": "Type: `string`, example: `s_[0-9]+_1`. Exclude set of tiles by a regular expression" + + } + + +} +}, + + + "resource arguments" : { + "title": "Resource arguments", + "type": "object", + "description": "No description", + "properties": { + + + "shared_thread_odirect_output": { + "type": + "boolean", + "description": "Type: `boolean`, example: `true`. Use linux native asynchronous io (io_submit) for file output (Default=false)", + "help_text": "Type: `boolean`, example: `true`. Use linux native asynchronous io (io_submit) for file output (Default=false)" + + } + + + , + "bcl_num_parallel_tiles": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. \\# of tiles to process in parallel (default 1)", + "help_text": "Type: `integer`, example: `1`. \\# of tiles to process in parallel (default 1)" + + } + + + , + "bcl_num_conversion_threads": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. \\# of threads for conversion (per tile, default # cpu threads)", + "help_text": "Type: `integer`, example: `1`. \\# of threads for conversion (per tile, default # cpu threads)" + + } + + + , + "bcl_num_compression_threads": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. \\# of threads for fastq", + "help_text": "Type: `integer`, example: `1`. \\# of threads for fastq.gz output compression (per tile, default # cpu threads, or HW+12)" + + } + + + , + "bcl_num_decompression_threads": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. \\# of threads for bcl/cbcl input decompression (per tile, default half # cpu threads, or HW+8)", + "help_text": "Type: `integer`, example: `1`. \\# of threads for bcl/cbcl input decompression (per tile, default half # cpu threads, or HW+8). Only applies when preloading files" + + } + + +} +}, + + + "run arguments" : { + "title": "Run arguments", + "type": "object", + "description": "No description", + "properties": { + + + "bcl_only_matched_reads": { + "type": + "boolean", + "description": "Type: `boolean`, example: `true`. For pure BCL conversion, do not output files for \u0027Undetermined\u0027 [unmatched] reads (output by default)", + "help_text": "Type: `boolean`, example: `true`. For pure BCL conversion, do not output files for \u0027Undetermined\u0027 [unmatched] reads (output by default)" + + } + + + , + "no_lane_splitting": { + "type": + "boolean", + "description": "Type: `boolean`, example: `true`. Do not split FASTQ file by lane (false by default)", + "help_text": "Type: `boolean`, example: `true`. Do not split FASTQ file by lane (false by default)" + + } + + + , + "num_unknown_barcodes_reported": { + "type": + "integer", + "description": "Type: `integer`, example: `1000`. \\# of Top Unknown Barcodes to output (1000 by default)", + "help_text": "Type: `integer`, example: `1000`. \\# of Top Unknown Barcodes to output (1000 by default)" + + } + + + , + "bcl_validate_sample_sheet_only": { + "type": + "boolean", + "description": "Type: `boolean`, example: `true`. Only validate RunInfo", + "help_text": "Type: `boolean`, example: `true`. Only validate RunInfo.xml \u0026 SampleSheet files (produce no FASTQ files)" + + } + + + , + "strict_mode": { + "type": + "boolean", + "description": "Type: `boolean`, example: `true`. Abort if any files are missing (false by default)", + "help_text": "Type: `boolean`, example: `true`. Abort if any files are missing (false by default)" + + } + + + , + "sample_name_column_enabled": { + "type": + "boolean", + "description": "Type: `boolean`, example: `true`. Use sample sheet \u0027Sample_Name\u0027 column when naming fastq files \u0026 subdirectories", + "help_text": "Type: `boolean`, example: `true`. Use sample sheet \u0027Sample_Name\u0027 column when naming fastq files \u0026 subdirectories" + + } + + +} +}, + + + "output arguments" : { + "title": "Output arguments", + "type": "object", + "description": "No description", + "properties": { + + + "output_directory": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output_directory.output_directory`, example: `fastq_dir`. Output directory containig fastq files", + "help_text": "Type: `file`, required, default: `$id.$key.output_directory.output_directory`, example: `fastq_dir`. Output directory containig fastq files" + , + "default": "$id.$key.output_directory.output_directory" + } + + + , + "bcl_sampleproject_subdirectories": { + "type": + "boolean", + "description": "Type: `boolean`, example: `true`. Output to subdirectories based upon sample sheet \u0027Sample_Project\u0027 column", + "help_text": "Type: `boolean`, example: `true`. Output to subdirectories based upon sample sheet \u0027Sample_Project\u0027 column" + + } + + + , + "fastq_gzip_compression_level": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. Set fastq output compression level 0-9 (default 1)", + "help_text": "Type: `integer`, example: `1`. Set fastq output compression level 0-9 (default 1)" + + } + + + , + "reports": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.reports.reports`, example: `reports_dir`. Reports directory", + "help_text": "Type: `file`, default: `$id.$key.reports.reports`, example: `reports_dir`. Reports directory" + , + "default": "$id.$key.reports.reports" + } + + + , + "logs": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.logs.logs`, example: `logs_dir`. Reports directory", + "help_text": "Type: `file`, default: `$id.$key.logs.logs`, example: `logs_dir`. Reports directory" + , + "default": "$id.$key.logs.logs" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/input arguments" + }, + + { + "$ref": "#/definitions/lane and tile settings" + }, + + { + "$ref": "#/definitions/resource arguments" + }, + + { + "$ref": "#/definitions/run arguments" + }, + + { + "$ref": "#/definitions/output arguments" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/bedtools/bedtools_getfasta/.config.vsh.yaml b/target/nextflow/bedtools/bedtools_getfasta/.config.vsh.yaml new file mode 100644 index 00000000..56c803f0 --- /dev/null +++ b/target/nextflow/bedtools/bedtools_getfasta/.config.vsh.yaml @@ -0,0 +1,243 @@ +name: "bedtools_getfasta" +namespace: "bedtools" +version: "main" +argument_groups: +- name: "Input arguments" + arguments: + - type: "file" + name: "--input_fasta" + description: "FASTA file containing sequences for each interval specified in the\ + \ input BED file.\nThe headers in the input FASTA file must exactly match the\ + \ chromosome column in the BED file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--input_bed" + description: "BED file containing intervals to extract from the FASTA file.\n\ + BED files containing a single region require a newline character\nat the end\ + \ of the line, otherwise a blank output file is produced.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--rna" + description: "The FASTA is RNA not DNA. Reverse complementation handled accordingly.\n" + info: null + direction: "input" +- name: "Run arguments" + arguments: + - type: "boolean_true" + name: "--strandedness" + alternatives: + - "-s" + description: "Force strandedness. If the feature occupies the antisense strand,\ + \ the output sequence will\nbe reverse complemented. By default strandedness\ + \ is not taken into account.\n" + info: null + direction: "input" +- name: "Output arguments" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output file where the output from the 'bedtools getfasta' commend\ + \ will\nbe written to.\n" + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--tab" + description: "Report extract sequences in a tab-delimited format instead of in\ + \ FASTA format.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--bed_out" + description: "Report extract sequences in a tab-delimited BED format instead of\ + \ in FASTA format.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--name" + description: "Set the FASTA header for each extracted sequence to be the \"name\"\ + \ and coordinate columns from the BED feature.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--name_only" + description: "Set the FASTA header for each extracted sequence to be the \"name\"\ + \ columns from the BED feature.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--split" + description: "When --input is in BED12 format, create a separate fasta entry for\ + \ each block in a BED12 record,\nblocks being described in the 11th and 12th\ + \ column of the BED.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--full_header" + description: "Use full fasta header. By default, only the word before the first\ + \ space or tab is used.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Extract sequences from a FASTA file for each of the intervals defined\ + \ in a BED/GFF/VCF file." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "sequencing" +- "fasta" +- "BED" +- "GFF" +- "VCF" +license: "GPL-2.0" +references: + doi: + - "10.1093/bioinformatics/btq033" +links: + repository: "https://github.com/arq5x/bedtools2" + documentation: "https://bedtools.readthedocs.io/en/latest/content/tools/getfasta.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "debian:stable-slim" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "apt" + packages: + - "bedtools" + - "procps" + interactive: false + - type: "docker" + run: + - "echo \"bedtools: \\\"$(bedtools --version | sed -n 's/^bedtools //p')\\\"\"\ + \ > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/bedtools/bedtools_getfasta/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/bedtools/bedtools_getfasta" + executable: "target/nextflow/bedtools/bedtools_getfasta/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/bedtools/bedtools_getfasta/main.nf b/target/nextflow/bedtools/bedtools_getfasta/main.nf new file mode 100644 index 00000000..e0952c6e --- /dev/null +++ b/target/nextflow/bedtools/bedtools_getfasta/main.nf @@ -0,0 +1,3605 @@ +// bedtools_getfasta main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "bedtools_getfasta", + "namespace" : "bedtools", + "version" : "main", + "argument_groups" : [ + { + "name" : "Input arguments", + "arguments" : [ + { + "type" : "file", + "name" : "--input_fasta", + "description" : "FASTA file containing sequences for each interval specified in the input BED file.\nThe headers in the input FASTA file must exactly match the chromosome column in the BED file.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--input_bed", + "description" : "BED file containing intervals to extract from the FASTA file.\nBED files containing a single region require a newline character\nat the end of the line, otherwise a blank output file is produced.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--rna", + "description" : "The FASTA is RNA not DNA. Reverse complementation handled accordingly.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Run arguments", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--strandedness", + "alternatives" : [ + "-s" + ], + "description" : "Force strandedness. If the feature occupies the antisense strand, the output sequence will\nbe reverse complemented. By default strandedness is not taken into account.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Output arguments", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "alternatives" : [ + "-o" + ], + "description" : "Output file where the output from the 'bedtools getfasta' commend will\nbe written to.\n", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--tab", + "description" : "Report extract sequences in a tab-delimited format instead of in FASTA format.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--bed_out", + "description" : "Report extract sequences in a tab-delimited BED format instead of in FASTA format.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--name", + "description" : "Set the FASTA header for each extracted sequence to be the \\"name\\" and coordinate columns from the BED feature.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--name_only", + "description" : "Set the FASTA header for each extracted sequence to be the \\"name\\" columns from the BED feature.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--split", + "description" : "When --input is in BED12 format, create a separate fasta entry for each block in a BED12 record,\nblocks being described in the 11th and 12th column of the BED.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--full_header", + "description" : "Use full fasta header. By default, only the word before the first space or tab is used.\n", + "direction" : "input" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Extract sequences from a FASTA file for each of the intervals defined in a BED/GFF/VCF file.", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "sequencing", + "fasta", + "BED", + "GFF", + "VCF" + ], + "license" : "GPL-2.0", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btq033" + ] + }, + "links" : { + "repository" : "https://github.com/arq5x/bedtools2", + "documentation" : "https://bedtools.readthedocs.io/en/latest/content/tools/getfasta.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "debian:stable-slim", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "apt", + "packages" : [ + "bedtools", + "procps" + ], + "interactive" : false + }, + { + "type" : "docker", + "run" : [ + "echo \\"bedtools: \\\\\\"$(bedtools --version | sed -n 's/^bedtools //p')\\\\\\"\\" > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/bedtools/bedtools_getfasta/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/bedtools/bedtools_getfasta", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT_FASTA+x} ]; then echo "${VIASH_PAR_INPUT_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input_fasta='&'#" ; else echo "# par_input_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_BED+x} ]; then echo "${VIASH_PAR_INPUT_BED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input_bed='&'#" ; else echo "# par_input_bed="; fi ) +$( if [ ! -z ${VIASH_PAR_RNA+x} ]; then echo "${VIASH_PAR_RNA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_rna='&'#" ; else echo "# par_rna="; fi ) +$( if [ ! -z ${VIASH_PAR_STRANDEDNESS+x} ]; then echo "${VIASH_PAR_STRANDEDNESS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_strandedness='&'#" ; else echo "# par_strandedness="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_TAB+x} ]; then echo "${VIASH_PAR_TAB}" | sed "s#'#'\\"'\\"'#g;s#.*#par_tab='&'#" ; else echo "# par_tab="; fi ) +$( if [ ! -z ${VIASH_PAR_BED_OUT+x} ]; then echo "${VIASH_PAR_BED_OUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bed_out='&'#" ; else echo "# par_bed_out="; fi ) +$( if [ ! -z ${VIASH_PAR_NAME+x} ]; then echo "${VIASH_PAR_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_name='&'#" ; else echo "# par_name="; fi ) +$( if [ ! -z ${VIASH_PAR_NAME_ONLY+x} ]; then echo "${VIASH_PAR_NAME_ONLY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_name_only='&'#" ; else echo "# par_name_only="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLIT+x} ]; then echo "${VIASH_PAR_SPLIT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_split='&'#" ; else echo "# par_split="; fi ) +$( if [ ! -z ${VIASH_PAR_FULL_HEADER+x} ]; then echo "${VIASH_PAR_FULL_HEADER}" | sed "s#'#'\\"'\\"'#g;s#.*#par_full_header='&'#" ; else echo "# par_full_header="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END +#!/usr/bin/env bash +set -eo pipefail + +unset_if_false=( par_rna par_strandedness par_tab par_bed_out par_name par_name_only par_split par_full_header ) + +for par in \\${unset_if_false[@]}; do + test_val="\\${!par}" + [[ "\\$test_val" == "false" ]] && unset \\$par +done + +bedtools getfasta \\\\ + -fi "\\$par_input_fasta" \\\\ + -bed "\\$par_input_bed" \\\\ + \\${par_rna:+-rna} \\\\ + \\${par_name:+-name} \\\\ + \\${par_name_only:+-nameOnly} \\\\ + \\${par_tab:+-tab} \\\\ + \\${par_bed_out:+-bedOut} \\\\ + \\${par_strandedness:+-s} \\\\ + \\${par_split:+-split} \\\\ + \\${par_full_header:+-fullHeader} > "\\$par_output" +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/bedtools/bedtools_getfasta", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/bedtools/bedtools_getfasta/nextflow.config b/target/nextflow/bedtools/bedtools_getfasta/nextflow.config new file mode 100644 index 00000000..64ab02e6 --- /dev/null +++ b/target/nextflow/bedtools/bedtools_getfasta/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'bedtools/bedtools_getfasta' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Extract sequences from a FASTA file for each of the intervals defined in a BED/GFF/VCF file.' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/bedtools/bedtools_getfasta/nextflow_schema.json b/target/nextflow/bedtools/bedtools_getfasta/nextflow_schema.json new file mode 100644 index 00000000..c3f7e079 --- /dev/null +++ b/target/nextflow/bedtools/bedtools_getfasta/nextflow_schema.json @@ -0,0 +1,207 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "bedtools_getfasta", +"description": "Extract sequences from a FASTA file for each of the intervals defined in a BED/GFF/VCF file.", +"type": "object", +"definitions": { + + + + "input arguments" : { + "title": "Input arguments", + "type": "object", + "description": "No description", + "properties": { + + + "input_fasta": { + "type": + "string", + "description": "Type: `file`. FASTA file containing sequences for each interval specified in the input BED file", + "help_text": "Type: `file`. FASTA file containing sequences for each interval specified in the input BED file.\nThe headers in the input FASTA file must exactly match the chromosome column in the BED file.\n" + + } + + + , + "input_bed": { + "type": + "string", + "description": "Type: `file`. BED file containing intervals to extract from the FASTA file", + "help_text": "Type: `file`. BED file containing intervals to extract from the FASTA file.\nBED files containing a single region require a newline character\nat the end of the line, otherwise a blank output file is produced.\n" + + } + + + , + "rna": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. The FASTA is RNA not DNA", + "help_text": "Type: `boolean_true`, default: `false`. The FASTA is RNA not DNA. Reverse complementation handled accordingly.\n" + , + "default": "False" + } + + +} +}, + + + "run arguments" : { + "title": "Run arguments", + "type": "object", + "description": "No description", + "properties": { + + + "strandedness": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Force strandedness", + "help_text": "Type: `boolean_true`, default: `false`. Force strandedness. If the feature occupies the antisense strand, the output sequence will\nbe reverse complemented. By default strandedness is not taken into account.\n" + , + "default": "False" + } + + +} +}, + + + "output arguments" : { + "title": "Output arguments", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output.output`. Output file where the output from the \u0027bedtools getfasta\u0027 commend will\nbe written to", + "help_text": "Type: `file`, required, default: `$id.$key.output.output`. Output file where the output from the \u0027bedtools getfasta\u0027 commend will\nbe written to.\n" + , + "default": "$id.$key.output.output" + } + + + , + "tab": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Report extract sequences in a tab-delimited format instead of in FASTA format", + "help_text": "Type: `boolean_true`, default: `false`. Report extract sequences in a tab-delimited format instead of in FASTA format.\n" + , + "default": "False" + } + + + , + "bed_out": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Report extract sequences in a tab-delimited BED format instead of in FASTA format", + "help_text": "Type: `boolean_true`, default: `false`. Report extract sequences in a tab-delimited BED format instead of in FASTA format.\n" + , + "default": "False" + } + + + , + "name": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Set the FASTA header for each extracted sequence to be the \"name\" and coordinate columns from the BED feature", + "help_text": "Type: `boolean_true`, default: `false`. Set the FASTA header for each extracted sequence to be the \"name\" and coordinate columns from the BED feature.\n" + , + "default": "False" + } + + + , + "name_only": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Set the FASTA header for each extracted sequence to be the \"name\" columns from the BED feature", + "help_text": "Type: `boolean_true`, default: `false`. Set the FASTA header for each extracted sequence to be the \"name\" columns from the BED feature.\n" + , + "default": "False" + } + + + , + "split": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. When --input is in BED12 format, create a separate fasta entry for each block in a BED12 record,\nblocks being described in the 11th and 12th column of the BED", + "help_text": "Type: `boolean_true`, default: `false`. When --input is in BED12 format, create a separate fasta entry for each block in a BED12 record,\nblocks being described in the 11th and 12th column of the BED.\n" + , + "default": "False" + } + + + , + "full_header": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use full fasta header", + "help_text": "Type: `boolean_true`, default: `false`. Use full fasta header. By default, only the word before the first space or tab is used.\n" + , + "default": "False" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/input arguments" + }, + + { + "$ref": "#/definitions/run arguments" + }, + + { + "$ref": "#/definitions/output arguments" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/busco/busco_download_datasets/.config.vsh.yaml b/target/nextflow/busco/busco_download_datasets/.config.vsh.yaml new file mode 100644 index 00000000..5558e1dc --- /dev/null +++ b/target/nextflow/busco/busco_download_datasets/.config.vsh.yaml @@ -0,0 +1,170 @@ +name: "busco_download_datasets" +namespace: "busco" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "string" + name: "--download" + description: "Download dataset. Possible values are a specific dataset name, \"\ + all\", \"prokaryota\", \"eukaryota\", or \"virus\".\nThe full list of available\ + \ datasets can be viewed [here](https://busco-data.ezlab.org/v5/data/lineages/)\ + \ or by running the busco/busco_list_datasets component.\n" + info: null + example: + - "stramenopiles_odb10" + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--download_path" + description: "Local filepath for storing BUSCO dataset downloads\n" + info: null + example: + - "busco_downloads" + default: + - "busco_downloads" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Downloads available busco datasets" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "lineage datasets" +license: "MIT" +references: + doi: + - "10.1007/978-1-4939-9173-0_14" +links: + repository: "https://gitlab.com/ezlab/busco" + homepage: "https://busco.ezlab.org/" + documentation: "https://busco.ezlab.org/busco_userguide.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "busco --version | sed 's/BUSCO\\s\\(.*\\)/busco: \"\\1\"/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/busco/busco_download_datasets/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/busco/busco_download_datasets" + executable: "target/nextflow/busco/busco_download_datasets/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/busco/busco_download_datasets/main.nf b/target/nextflow/busco/busco_download_datasets/main.nf new file mode 100644 index 00000000..aa27b1c9 --- /dev/null +++ b/target/nextflow/busco/busco_download_datasets/main.nf @@ -0,0 +1,3513 @@ +// busco_download_datasets main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "busco_download_datasets", + "namespace" : "busco", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "string", + "name" : "--download", + "description" : "Download dataset. Possible values are a specific dataset name, \\"all\\", \\"prokaryota\\", \\"eukaryota\\", or \\"virus\\".\nThe full list of available datasets can be viewed [here](https://busco-data.ezlab.org/v5/data/lineages/) or by running the busco/busco_list_datasets component.\n", + "example" : [ + "stramenopiles_odb10" + ], + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--download_path", + "description" : "Local filepath for storing BUSCO dataset downloads\n", + "example" : [ + "busco_downloads" + ], + "default" : [ + "busco_downloads" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Downloads available busco datasets", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "lineage datasets" + ], + "license" : "MIT", + "references" : { + "doi" : [ + "10.1007/978-1-4939-9173-0_14" + ] + }, + "links" : { + "repository" : "https://gitlab.com/ezlab/busco", + "homepage" : "https://busco.ezlab.org/", + "documentation" : "https://busco.ezlab.org/busco_userguide.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "busco --version | sed 's/BUSCO\\\\s\\\\(.*\\\\)/busco: \\"\\\\1\\"/' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/busco/busco_download_datasets/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/busco/busco_download_datasets", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_DOWNLOAD+x} ]; then echo "${VIASH_PAR_DOWNLOAD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_download='&'#" ; else echo "# par_download="; fi ) +$( if [ ! -z ${VIASH_PAR_DOWNLOAD_PATH+x} ]; then echo "${VIASH_PAR_DOWNLOAD_PATH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_download_path='&'#" ; else echo "# par_download_path="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + + +if [ ! -d "\\$par_download_path" ]; then + mkdir -p "\\$par_download_path" +fi + +busco \\\\ + --download_path "\\$par_download_path" \\\\ + --download "\\$par_download" + +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/busco/busco_download_datasets", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/busco/busco_download_datasets/nextflow.config b/target/nextflow/busco/busco_download_datasets/nextflow.config new file mode 100644 index 00000000..69698f33 --- /dev/null +++ b/target/nextflow/busco/busco_download_datasets/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'busco/busco_download_datasets' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Downloads available busco datasets' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/busco/busco_download_datasets/nextflow_schema.json b/target/nextflow/busco/busco_download_datasets/nextflow_schema.json new file mode 100644 index 00000000..10f7d894 --- /dev/null +++ b/target/nextflow/busco/busco_download_datasets/nextflow_schema.json @@ -0,0 +1,95 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "busco_download_datasets", +"description": "Downloads available busco datasets", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "download": { + "type": + "string", + "description": "Type: `string`, required, example: `stramenopiles_odb10`. Download dataset", + "help_text": "Type: `string`, required, example: `stramenopiles_odb10`. Download dataset. Possible values are a specific dataset name, \"all\", \"prokaryota\", \"eukaryota\", or \"virus\".\nThe full list of available datasets can be viewed [here](https://busco-data.ezlab.org/v5/data/lineages/) or by running the busco/busco_list_datasets component.\n" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "download_path": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.download_path.download_path`, example: `busco_downloads`. Local filepath for storing BUSCO dataset downloads\n", + "help_text": "Type: `file`, default: `$id.$key.download_path.download_path`, example: `busco_downloads`. Local filepath for storing BUSCO dataset downloads\n" + , + "default": "$id.$key.download_path.download_path" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/busco/busco_list_datasets/.config.vsh.yaml b/target/nextflow/busco/busco_list_datasets/.config.vsh.yaml new file mode 100644 index 00000000..e3131512 --- /dev/null +++ b/target/nextflow/busco/busco_list_datasets/.config.vsh.yaml @@ -0,0 +1,157 @@ +name: "busco_list_datasets" +namespace: "busco" +version: "main" +argument_groups: +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output file of the available busco datasets\n" + info: null + example: + - "file.txt" + default: + - "busco_dataset_list.txt" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Lists the available busco datasets" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "lineage datasets" +license: "MIT" +references: + doi: + - "10.1007/978-1-4939-9173-0_14" +links: + repository: "https://gitlab.com/ezlab/busco" + homepage: "https://busco.ezlab.org/" + documentation: "https://busco.ezlab.org/busco_userguide.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "busco --version | sed 's/BUSCO\\s\\(.*\\)/busco: \"\\1\"/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/busco/busco_list_datasets/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/busco/busco_list_datasets" + executable: "target/nextflow/busco/busco_list_datasets/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/busco/busco_list_datasets/main.nf b/target/nextflow/busco/busco_list_datasets/main.nf new file mode 100644 index 00000000..4857d947 --- /dev/null +++ b/target/nextflow/busco/busco_list_datasets/main.nf @@ -0,0 +1,3490 @@ +// busco_list_datasets main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "busco_list_datasets", + "namespace" : "busco", + "version" : "main", + "argument_groups" : [ + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "alternatives" : [ + "-o" + ], + "description" : "Output file of the available busco datasets\n", + "example" : [ + "file.txt" + ], + "default" : [ + "busco_dataset_list.txt" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Lists the available busco datasets", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "lineage datasets" + ], + "license" : "MIT", + "references" : { + "doi" : [ + "10.1007/978-1-4939-9173-0_14" + ] + }, + "links" : { + "repository" : "https://gitlab.com/ezlab/busco", + "homepage" : "https://busco.ezlab.org/", + "documentation" : "https://busco.ezlab.org/busco_userguide.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "busco --version | sed 's/BUSCO\\\\s\\\\(.*\\\\)/busco: \\"\\\\1\\"/' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/busco/busco_list_datasets/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/busco/busco_list_datasets", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +busco --list-datasets | awk '/^#{40}/{flag=1; next} flag{print}' > \\$par_output +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/busco/busco_list_datasets", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/busco/busco_list_datasets/nextflow.config b/target/nextflow/busco/busco_list_datasets/nextflow.config new file mode 100644 index 00000000..1e24f6da --- /dev/null +++ b/target/nextflow/busco/busco_list_datasets/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'busco/busco_list_datasets' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Lists the available busco datasets' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/busco/busco_list_datasets/nextflow_schema.json b/target/nextflow/busco/busco_list_datasets/nextflow_schema.json new file mode 100644 index 00000000..c2f65754 --- /dev/null +++ b/target/nextflow/busco/busco_list_datasets/nextflow_schema.json @@ -0,0 +1,71 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "busco_list_datasets", +"description": "Lists the available busco datasets", +"type": "object", +"definitions": { + + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.output.txt`, example: `file.txt`. Output file of the available busco datasets\n", + "help_text": "Type: `file`, default: `$id.$key.output.txt`, example: `file.txt`. Output file of the available busco datasets\n" + , + "default": "$id.$key.output.txt" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/busco/busco_run/.config.vsh.yaml b/target/nextflow/busco/busco_run/.config.vsh.yaml new file mode 100644 index 00000000..cee05b75 --- /dev/null +++ b/target/nextflow/busco/busco_run/.config.vsh.yaml @@ -0,0 +1,430 @@ +name: "busco_run" +namespace: "busco" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + alternatives: + - "-i" + description: "Input sequence file in FASTA format. Can be an assembled genome\ + \ or transcriptome (DNA), or protein sequences from an annotated gene set. Also\ + \ possible to use a path to a directory containing multiple input files.\n" + info: null + example: + - "file.fasta" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--mode" + alternatives: + - "-m" + description: "Specify which BUSCO analysis mode to run. There are three valid\ + \ modes:\n - geno or genome, for genome assemblies (DNA)\n - tran or transcriptome,\ + \ for transcriptome assemblies (DNA)\n - prot or proteins, for annotated gene\ + \ sets (protein)\n" + info: null + example: + - "proteins" + required: true + choices: + - "genome" + - "geno" + - "transcriptome" + - "tran" + - "proteins" + - "prot" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--lineage_dataset" + alternatives: + - "-l" + description: "Specify a BUSCO lineage dataset that is most closely related to\ + \ the assembly or gene set being assessed. \nThe full list of available datasets\ + \ can be viewed [here](https://busco-data.ezlab.org/v5/data/lineages/) or by\ + \ running the busco/busco_list_datasets component.\nWhen unsure, the \"--auto_lineage\"\ + \ flag can be set to automatically find the optimal lineage path.\nBUSCO will\ + \ automatically download the requested dataset if it is not already present\ + \ in the download folder. \nYou can optionally provide a path to a local dataset\ + \ instead of a name, e.g. path/to/dataset.\nDatasets can be downloaded using\ + \ the busco/busco_download_dataset component.\n" + info: null + example: + - "stramenopiles_odb10" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--short_summary_json" + description: "Output file for short summary in JSON format.\n" + info: null + example: + - "short_summary.json" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--short_summary_txt" + description: "Output file for short summary in TXT format.\n" + info: null + example: + - "short_summary.txt" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--full_table" + description: "Full table output in TSV format.\n" + info: null + example: + - "full_table.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--missing_busco_list" + description: "Missing list output in TSV format.\n" + info: null + example: + - "missing_busco_list.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--output_dir" + description: "The full output directory, if so desired.\n" + info: null + example: + - "output_dir" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Resource and Run Settings" + arguments: + - type: "boolean_true" + name: "--force" + description: "Force rewriting of existing files. Must be used when output files\ + \ with the provided name already exist.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--quiet" + alternatives: + - "-q" + description: "Disable the info logs, displays only errors.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--restart" + alternatives: + - "-r" + description: "Continue a run that had already partially completed. Restarting\ + \ skips calls to tools that have completed but performs all pre- and post-processing\ + \ steps.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--tar" + description: "Compress some subdirectories with many files to save space.\n" + info: null + direction: "input" +- name: "Lineage Dataset Settings" + arguments: + - type: "boolean_true" + name: "--auto_lineage" + description: "Run auto-lineage pipelilne to automatically determine BUSCO lineage\ + \ dataset that is most closely related to the assembly or gene set being assessed.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--auto_lineage_euk" + description: "Run auto-placement just on eukaryota tree to find optimal lineage\ + \ path.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--auto_lineage_prok" + description: "Run auto_lineage just on prokaryota trees to find optimum lineage\ + \ path.\n" + info: null + direction: "input" + - type: "string" + name: "--datasets_version" + description: "Specify the version of BUSCO datasets\n" + info: null + example: + - "odb10" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Augustus Settings" + arguments: + - type: "boolean_true" + name: "--augustus" + description: "Use augustus gene predictor for eukaryote runs.\n" + info: null + direction: "input" + - type: "string" + name: "--augustus_parameters" + description: "Additional parameters to be passed to Augustus (see Augustus documentation:\ + \ https://github.com/Gaius-Augustus/Augustus/blob/master/docs/RUNNING-AUGUSTUS.md).\n\ + Parameters should be contained within a single string, without whitespace and\ + \ seperated by commas.\n" + info: null + example: + - "--PARAM1=VALUE1,--PARAM2=VALUE2" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--augustus_species" + description: "Specify the augustus species\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--long" + description: "Optimize Augustus self-training mode. This adds considerably to\ + \ the run time, but can improve results for some non-model organisms.\n" + info: null + direction: "input" +- name: "BBTools Settings" + arguments: + - type: "integer" + name: "--contig_break" + description: "Number of contiguous Ns to signify a break between contigs in BBTools\ + \ analysis.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--limit" + description: "Number of candidate regions (contig or transcript) from the BLAST\ + \ output to consider per BUSCO.\nThis option is only effective in pipelines\ + \ using BLAST, i.e. the genome pipeline (see --augustus) or the prokaryota transcriptome\ + \ pipeline.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--scaffold_composition" + description: "Writes ACGTN content per scaffold to a file scaffold_composition.txt.\n" + info: null + direction: "input" +- name: "BLAST Settings" + arguments: + - type: "double" + name: "--e_value" + description: "E-value cutoff for BLAST searches.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Protein Gene Prediction settings" + arguments: + - type: "boolean_true" + name: "--miniprot" + description: "Use Miniprot gene predictor.\n" + info: null + direction: "input" +- name: "MetaEuk Settings" + arguments: + - type: "string" + name: "--metaeuk_parameters" + description: "Pass additional arguments to Metaeuk for the first run (see Metaeuk\ + \ documentation https://github.com/soedinglab/metaeuk).\nAll parameters should\ + \ be contained within a single string with no white space, with each parameter\ + \ separated by a comma.\n" + info: null + example: + - "--max-overlap=15,--min-exon-aa=15" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--metaeuk_rerun_parameters" + description: "Pass additional arguments to Metaeuk for the second run (see Metaeuk\ + \ documentation https://github.com/soedinglab/metaeuk).\nAll parameters should\ + \ be contained within a single string with no white space, with each parameter\ + \ separated by a comma.\n" + info: null + example: + - "--max-overlap=15,--min-exon-aa=15" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Assessment of genome assembly and annotation completeness with single\ + \ copy orthologs" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "Genome assembly" +- "quality control" +license: "MIT" +references: + doi: + - "10.1007/978-1-4939-9173-0_14" +links: + repository: "https://gitlab.com/ezlab/busco" + homepage: "https://busco.ezlab.org/" + documentation: "https://busco.ezlab.org/busco_userguide.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "busco --version | sed 's/BUSCO\\s\\(.*\\)/busco: \"\\1\"/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/busco/busco_run/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/busco/busco_run" + executable: "target/nextflow/busco/busco_run/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/busco/busco_run/main.nf b/target/nextflow/busco/busco_run/main.nf new file mode 100644 index 00000000..4ce390b1 --- /dev/null +++ b/target/nextflow/busco/busco_run/main.nf @@ -0,0 +1,3886 @@ +// busco_run main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "busco_run", + "namespace" : "busco", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "alternatives" : [ + "-i" + ], + "description" : "Input sequence file in FASTA format. Can be an assembled genome or transcriptome (DNA), or protein sequences from an annotated gene set. Also possible to use a path to a directory containing multiple input files.\n", + "example" : [ + "file.fasta" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--mode", + "alternatives" : [ + "-m" + ], + "description" : "Specify which BUSCO analysis mode to run. There are three valid modes:\n - geno or genome, for genome assemblies (DNA)\n - tran or transcriptome, for transcriptome assemblies (DNA)\n - prot or proteins, for annotated gene sets (protein)\n", + "example" : [ + "proteins" + ], + "required" : true, + "choices" : [ + "genome", + "geno", + "transcriptome", + "tran", + "proteins", + "prot" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--lineage_dataset", + "alternatives" : [ + "-l" + ], + "description" : "Specify a BUSCO lineage dataset that is most closely related to the assembly or gene set being assessed. \nThe full list of available datasets can be viewed [here](https://busco-data.ezlab.org/v5/data/lineages/) or by running the busco/busco_list_datasets component.\nWhen unsure, the \\"--auto_lineage\\" flag can be set to automatically find the optimal lineage path.\nBUSCO will automatically download the requested dataset if it is not already present in the download folder. \nYou can optionally provide a path to a local dataset instead of a name, e.g. path/to/dataset.\nDatasets can be downloaded using the busco/busco_download_dataset component.\n", + "example" : [ + "stramenopiles_odb10" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--short_summary_json", + "description" : "Output file for short summary in JSON format.\n", + "example" : [ + "short_summary.json" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--short_summary_txt", + "description" : "Output file for short summary in TXT format.\n", + "example" : [ + "short_summary.txt" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--full_table", + "description" : "Full table output in TSV format.\n", + "example" : [ + "full_table.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--missing_busco_list", + "description" : "Missing list output in TSV format.\n", + "example" : [ + "missing_busco_list.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--output_dir", + "description" : "The full output directory, if so desired.\n", + "example" : [ + "output_dir" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Resource and Run Settings", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--force", + "description" : "Force rewriting of existing files. Must be used when output files with the provided name already exist.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--quiet", + "alternatives" : [ + "-q" + ], + "description" : "Disable the info logs, displays only errors.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--restart", + "alternatives" : [ + "-r" + ], + "description" : "Continue a run that had already partially completed. Restarting skips calls to tools that have completed but performs all pre- and post-processing steps.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--tar", + "description" : "Compress some subdirectories with many files to save space.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Lineage Dataset Settings", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--auto_lineage", + "description" : "Run auto-lineage pipelilne to automatically determine BUSCO lineage dataset that is most closely related to the assembly or gene set being assessed.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--auto_lineage_euk", + "description" : "Run auto-placement just on eukaryota tree to find optimal lineage path.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--auto_lineage_prok", + "description" : "Run auto_lineage just on prokaryota trees to find optimum lineage path.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--datasets_version", + "description" : "Specify the version of BUSCO datasets\n", + "example" : [ + "odb10" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Augustus Settings", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--augustus", + "description" : "Use augustus gene predictor for eukaryote runs.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--augustus_parameters", + "description" : "Additional parameters to be passed to Augustus (see Augustus documentation: https://github.com/Gaius-Augustus/Augustus/blob/master/docs/RUNNING-AUGUSTUS.md).\nParameters should be contained within a single string, without whitespace and seperated by commas.\n", + "example" : [ + "--PARAM1=VALUE1,--PARAM2=VALUE2" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--augustus_species", + "description" : "Specify the augustus species\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--long", + "description" : "Optimize Augustus self-training mode. This adds considerably to the run time, but can improve results for some non-model organisms.\n", + "direction" : "input" + } + ] + }, + { + "name" : "BBTools Settings", + "arguments" : [ + { + "type" : "integer", + "name" : "--contig_break", + "description" : "Number of contiguous Ns to signify a break between contigs in BBTools analysis.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--limit", + "description" : "Number of candidate regions (contig or transcript) from the BLAST output to consider per BUSCO.\nThis option is only effective in pipelines using BLAST, i.e. the genome pipeline (see --augustus) or the prokaryota transcriptome pipeline.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--scaffold_composition", + "description" : "Writes ACGTN content per scaffold to a file scaffold_composition.txt.\n", + "direction" : "input" + } + ] + }, + { + "name" : "BLAST Settings", + "arguments" : [ + { + "type" : "double", + "name" : "--e_value", + "description" : "E-value cutoff for BLAST searches.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Protein Gene Prediction settings", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--miniprot", + "description" : "Use Miniprot gene predictor.\n", + "direction" : "input" + } + ] + }, + { + "name" : "MetaEuk Settings", + "arguments" : [ + { + "type" : "string", + "name" : "--metaeuk_parameters", + "description" : "Pass additional arguments to Metaeuk for the first run (see Metaeuk documentation https://github.com/soedinglab/metaeuk).\nAll parameters should be contained within a single string with no white space, with each parameter separated by a comma.\n", + "example" : [ + "--max-overlap=15,--min-exon-aa=15" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--metaeuk_rerun_parameters", + "description" : "Pass additional arguments to Metaeuk for the second run (see Metaeuk documentation https://github.com/soedinglab/metaeuk).\nAll parameters should be contained within a single string with no white space, with each parameter separated by a comma.\n", + "example" : [ + "--max-overlap=15,--min-exon-aa=15" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Assessment of genome assembly and annotation completeness with single copy orthologs", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "Genome assembly", + "quality control" + ], + "license" : "MIT", + "references" : { + "doi" : [ + "10.1007/978-1-4939-9173-0_14" + ] + }, + "links" : { + "repository" : "https://gitlab.com/ezlab/busco", + "homepage" : "https://busco.ezlab.org/", + "documentation" : "https://busco.ezlab.org/busco_userguide.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/busco:5.6.1--pyhdfd78af_0", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "busco --version | sed 's/BUSCO\\\\s\\\\(.*\\\\)/busco: \\"\\\\1\\"/' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/busco/busco_run/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/busco/busco_run", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_MODE+x} ]; then echo "${VIASH_PAR_MODE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_mode='&'#" ; else echo "# par_mode="; fi ) +$( if [ ! -z ${VIASH_PAR_LINEAGE_DATASET+x} ]; then echo "${VIASH_PAR_LINEAGE_DATASET}" | sed "s#'#'\\"'\\"'#g;s#.*#par_lineage_dataset='&'#" ; else echo "# par_lineage_dataset="; fi ) +$( if [ ! -z ${VIASH_PAR_SHORT_SUMMARY_JSON+x} ]; then echo "${VIASH_PAR_SHORT_SUMMARY_JSON}" | sed "s#'#'\\"'\\"'#g;s#.*#par_short_summary_json='&'#" ; else echo "# par_short_summary_json="; fi ) +$( if [ ! -z ${VIASH_PAR_SHORT_SUMMARY_TXT+x} ]; then echo "${VIASH_PAR_SHORT_SUMMARY_TXT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_short_summary_txt='&'#" ; else echo "# par_short_summary_txt="; fi ) +$( if [ ! -z ${VIASH_PAR_FULL_TABLE+x} ]; then echo "${VIASH_PAR_FULL_TABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_full_table='&'#" ; else echo "# par_full_table="; fi ) +$( if [ ! -z ${VIASH_PAR_MISSING_BUSCO_LIST+x} ]; then echo "${VIASH_PAR_MISSING_BUSCO_LIST}" | sed "s#'#'\\"'\\"'#g;s#.*#par_missing_busco_list='&'#" ; else echo "# par_missing_busco_list="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_DIR+x} ]; then echo "${VIASH_PAR_OUTPUT_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_dir='&'#" ; else echo "# par_output_dir="; fi ) +$( if [ ! -z ${VIASH_PAR_FORCE+x} ]; then echo "${VIASH_PAR_FORCE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_force='&'#" ; else echo "# par_force="; fi ) +$( if [ ! -z ${VIASH_PAR_QUIET+x} ]; then echo "${VIASH_PAR_QUIET}" | sed "s#'#'\\"'\\"'#g;s#.*#par_quiet='&'#" ; else echo "# par_quiet="; fi ) +$( if [ ! -z ${VIASH_PAR_RESTART+x} ]; then echo "${VIASH_PAR_RESTART}" | sed "s#'#'\\"'\\"'#g;s#.*#par_restart='&'#" ; else echo "# par_restart="; fi ) +$( if [ ! -z ${VIASH_PAR_TAR+x} ]; then echo "${VIASH_PAR_TAR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_tar='&'#" ; else echo "# par_tar="; fi ) +$( if [ ! -z ${VIASH_PAR_AUTO_LINEAGE+x} ]; then echo "${VIASH_PAR_AUTO_LINEAGE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_auto_lineage='&'#" ; else echo "# par_auto_lineage="; fi ) +$( if [ ! -z ${VIASH_PAR_AUTO_LINEAGE_EUK+x} ]; then echo "${VIASH_PAR_AUTO_LINEAGE_EUK}" | sed "s#'#'\\"'\\"'#g;s#.*#par_auto_lineage_euk='&'#" ; else echo "# par_auto_lineage_euk="; fi ) +$( if [ ! -z ${VIASH_PAR_AUTO_LINEAGE_PROK+x} ]; then echo "${VIASH_PAR_AUTO_LINEAGE_PROK}" | sed "s#'#'\\"'\\"'#g;s#.*#par_auto_lineage_prok='&'#" ; else echo "# par_auto_lineage_prok="; fi ) +$( if [ ! -z ${VIASH_PAR_DATASETS_VERSION+x} ]; then echo "${VIASH_PAR_DATASETS_VERSION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_datasets_version='&'#" ; else echo "# par_datasets_version="; fi ) +$( if [ ! -z ${VIASH_PAR_AUGUSTUS+x} ]; then echo "${VIASH_PAR_AUGUSTUS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_augustus='&'#" ; else echo "# par_augustus="; fi ) +$( if [ ! -z ${VIASH_PAR_AUGUSTUS_PARAMETERS+x} ]; then echo "${VIASH_PAR_AUGUSTUS_PARAMETERS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_augustus_parameters='&'#" ; else echo "# par_augustus_parameters="; fi ) +$( if [ ! -z ${VIASH_PAR_AUGUSTUS_SPECIES+x} ]; then echo "${VIASH_PAR_AUGUSTUS_SPECIES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_augustus_species='&'#" ; else echo "# par_augustus_species="; fi ) +$( if [ ! -z ${VIASH_PAR_LONG+x} ]; then echo "${VIASH_PAR_LONG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_long='&'#" ; else echo "# par_long="; fi ) +$( if [ ! -z ${VIASH_PAR_CONTIG_BREAK+x} ]; then echo "${VIASH_PAR_CONTIG_BREAK}" | sed "s#'#'\\"'\\"'#g;s#.*#par_contig_break='&'#" ; else echo "# par_contig_break="; fi ) +$( if [ ! -z ${VIASH_PAR_LIMIT+x} ]; then echo "${VIASH_PAR_LIMIT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_limit='&'#" ; else echo "# par_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_SCAFFOLD_COMPOSITION+x} ]; then echo "${VIASH_PAR_SCAFFOLD_COMPOSITION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_scaffold_composition='&'#" ; else echo "# par_scaffold_composition="; fi ) +$( if [ ! -z ${VIASH_PAR_E_VALUE+x} ]; then echo "${VIASH_PAR_E_VALUE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_e_value='&'#" ; else echo "# par_e_value="; fi ) +$( if [ ! -z ${VIASH_PAR_MINIPROT+x} ]; then echo "${VIASH_PAR_MINIPROT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_miniprot='&'#" ; else echo "# par_miniprot="; fi ) +$( if [ ! -z ${VIASH_PAR_METAEUK_PARAMETERS+x} ]; then echo "${VIASH_PAR_METAEUK_PARAMETERS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_metaeuk_parameters='&'#" ; else echo "# par_metaeuk_parameters="; fi ) +$( if [ ! -z ${VIASH_PAR_METAEUK_RERUN_PARAMETERS+x} ]; then echo "${VIASH_PAR_METAEUK_RERUN_PARAMETERS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_metaeuk_rerun_parameters='&'#" ; else echo "# par_metaeuk_rerun_parameters="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + + +[[ "\\$par_tar" == "false" ]] && unset par_tar +[[ "\\$par_force" == "false" ]] && unset par_force +[[ "\\$par_quiet" == "false" ]] && unset par_quiet +[[ "\\$par_restart" == "false" ]] && unset par_restart +[[ "\\$par_auto_lineage" == "false" ]] && unset par_auto_lineage +[[ "\\$par_auto_lineage_euk" == "false" ]] && unset par_auto_lineage_euk +[[ "\\$par_auto_lineage_prok" == "false" ]] && unset par_auto_lineage_prok +[[ "\\$par_augustus" == "false" ]] && unset par_augustus +[[ "\\$par_long" == "false" ]] && unset par_long +[[ "\\$par_scaffold_composition" == "false" ]] && unset par_scaffold_composition +[[ "\\$par_miniprot" == "false" ]] && unset par_miniprot + +tmp_dir=\\$(mktemp -d -p "\\$meta_temp_dir" busco_XXXXXXXXX) +prefix=\\$(openssl rand -hex 8) + +busco \\\\ + --in "\\$par_input" \\\\ + --mode "\\$par_mode" \\\\ + --out "\\$prefix" \\\\ + --out_path "\\$tmp_dir" \\\\ + --opt-out-run-stats \\\\ + \\${meta_cpus:+--cpu "\\${meta_cpus}"} \\\\ + \\${par_lineage_dataset:+--lineage_dataset "\\$par_lineage_dataset"} \\\\ + \\${par_augustus:+--augustus} \\\\ + \\${par_augustus_parameters:+--augustus_parameters "\\$par_augustus_parameters"} \\\\ + \\${par_augustus_species:+--augustus_species "\\$par_augustus_species"} \\\\ + \\${par_auto_lineage:+--auto-lineage} \\\\ + \\${par_auto_lineage_euk:+--auto-lineage-euk} \\\\ + \\${par_auto_lineage_prok:+--auto-lineage-prok} \\\\ + \\${par_contig_break:+--contig_break \\$par_contig_break} \\\\ + \\${par_datasets_version:+--datasets_version "\\$par_datasets_version"} \\\\ + \\${par_e_value:+--evalue "\\$par_e_value"} \\\\ + \\${par_force:+--force} \\\\ + \\${par_limit:+--limit "\\$par_limit"} \\\\ + \\${par_long:+--long} \\\\ + \\${par_metaeuk_parameters:+--metaeuk_parameters "\\$par_metaeuk_parameters"} \\\\ + \\${par_metaeuk_rerun_parameters:+--metaeuk_rerun_parameters "\\$par_metaeuk_rerun_parameters"} \\\\ + \\${par_miniprot:+--miniprot} \\\\ + \\${par_quiet:+--quiet} \\\\ + \\${par_restart:+--restart} \\\\ + \\${par_scaffold_composition:+--scaffold_composition} \\\\ + \\${par_tar:+--tar} \\\\ + + +out_dir=\\$(find "\\$tmp_dir/\\$prefix" -maxdepth 1 -name 'run_*') + +if [[ -n "\\$par_short_summary_json" ]]; then + cp "\\$out_dir/short_summary.json" "\\$par_short_summary_json" +fi +if [[ -n "\\$par_short_summary_txt" ]]; then + cp "\\$out_dir/short_summary.txt" "\\$par_short_summary_txt" +fi +if [[ -n "\\$par_full_table" ]]; then + cp "\\$out_dir/full_table.tsv" "\\$par_full_table" +fi +if [[ -n "\\$par_missing_busco_list" ]]; then + cp "\\$out_dir/missing_busco_list.tsv" "\\$par_missing_busco_list" +fi +if [[ -n "\\$par_output_dir" ]]; then + if [[ -d "\\$par_output_dir" ]]; then + rm -r "\\$par_output_dir" + fi + cp -r -L "\\$out_dir" "\\$par_output_dir" +fi +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/busco/busco_run", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/busco/busco_run/nextflow.config b/target/nextflow/busco/busco_run/nextflow.config new file mode 100644 index 00000000..c489aa9f --- /dev/null +++ b/target/nextflow/busco/busco_run/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'busco/busco_run' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Assessment of genome assembly and annotation completeness with single copy orthologs' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/busco/busco_run/nextflow_schema.json b/target/nextflow/busco/busco_run/nextflow_schema.json new file mode 100644 index 00000000..b1d21536 --- /dev/null +++ b/target/nextflow/busco/busco_run/nextflow_schema.json @@ -0,0 +1,460 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "busco_run", +"description": "Assessment of genome assembly and annotation completeness with single copy orthologs", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`, required, example: `file.fasta`. Input sequence file in FASTA format", + "help_text": "Type: `file`, required, example: `file.fasta`. Input sequence file in FASTA format. Can be an assembled genome or transcriptome (DNA), or protein sequences from an annotated gene set. Also possible to use a path to a directory containing multiple input files.\n" + + } + + + , + "mode": { + "type": + "string", + "description": "Type: `string`, required, example: `proteins`, choices: ``genome`, `geno`, `transcriptome`, `tran`, `proteins`, `prot``. Specify which BUSCO analysis mode to run", + "help_text": "Type: `string`, required, example: `proteins`, choices: ``genome`, `geno`, `transcriptome`, `tran`, `proteins`, `prot``. Specify which BUSCO analysis mode to run. There are three valid modes:\n - geno or genome, for genome assemblies (DNA)\n - tran or transcriptome, for transcriptome assemblies (DNA)\n - prot or proteins, for annotated gene sets (protein)\n", + "enum": ["genome", "geno", "transcriptome", "tran", "proteins", "prot"] + + + } + + + , + "lineage_dataset": { + "type": + "string", + "description": "Type: `string`, example: `stramenopiles_odb10`. Specify a BUSCO lineage dataset that is most closely related to the assembly or gene set being assessed", + "help_text": "Type: `string`, example: `stramenopiles_odb10`. Specify a BUSCO lineage dataset that is most closely related to the assembly or gene set being assessed. \nThe full list of available datasets can be viewed [here](https://busco-data.ezlab.org/v5/data/lineages/) or by running the busco/busco_list_datasets component.\nWhen unsure, the \"--auto_lineage\" flag can be set to automatically find the optimal lineage path.\nBUSCO will automatically download the requested dataset if it is not already present in the download folder. \nYou can optionally provide a path to a local dataset instead of a name, e.g. path/to/dataset.\nDatasets can be downloaded using the busco/busco_download_dataset component.\n" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "short_summary_json": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.short_summary_json.json`, example: `short_summary.json`. Output file for short summary in JSON format", + "help_text": "Type: `file`, default: `$id.$key.short_summary_json.json`, example: `short_summary.json`. Output file for short summary in JSON format.\n" + , + "default": "$id.$key.short_summary_json.json" + } + + + , + "short_summary_txt": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.short_summary_txt.txt`, example: `short_summary.txt`. Output file for short summary in TXT format", + "help_text": "Type: `file`, default: `$id.$key.short_summary_txt.txt`, example: `short_summary.txt`. Output file for short summary in TXT format.\n" + , + "default": "$id.$key.short_summary_txt.txt" + } + + + , + "full_table": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.full_table.tsv`, example: `full_table.tsv`. Full table output in TSV format", + "help_text": "Type: `file`, default: `$id.$key.full_table.tsv`, example: `full_table.tsv`. Full table output in TSV format.\n" + , + "default": "$id.$key.full_table.tsv" + } + + + , + "missing_busco_list": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.missing_busco_list.tsv`, example: `missing_busco_list.tsv`. Missing list output in TSV format", + "help_text": "Type: `file`, default: `$id.$key.missing_busco_list.tsv`, example: `missing_busco_list.tsv`. Missing list output in TSV format.\n" + , + "default": "$id.$key.missing_busco_list.tsv" + } + + + , + "output_dir": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.output_dir.output_dir`, example: `output_dir/`. The full output directory, if so desired", + "help_text": "Type: `file`, default: `$id.$key.output_dir.output_dir`, example: `output_dir/`. The full output directory, if so desired.\n" + , + "default": "$id.$key.output_dir.output_dir" + } + + +} +}, + + + "resource and run settings" : { + "title": "Resource and Run Settings", + "type": "object", + "description": "No description", + "properties": { + + + "force": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Force rewriting of existing files", + "help_text": "Type: `boolean_true`, default: `false`. Force rewriting of existing files. Must be used when output files with the provided name already exist.\n" + , + "default": "False" + } + + + , + "quiet": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Disable the info logs, displays only errors", + "help_text": "Type: `boolean_true`, default: `false`. Disable the info logs, displays only errors.\n" + , + "default": "False" + } + + + , + "restart": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Continue a run that had already partially completed", + "help_text": "Type: `boolean_true`, default: `false`. Continue a run that had already partially completed. Restarting skips calls to tools that have completed but performs all pre- and post-processing steps.\n" + , + "default": "False" + } + + + , + "tar": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Compress some subdirectories with many files to save space", + "help_text": "Type: `boolean_true`, default: `false`. Compress some subdirectories with many files to save space.\n" + , + "default": "False" + } + + +} +}, + + + "lineage dataset settings" : { + "title": "Lineage Dataset Settings", + "type": "object", + "description": "No description", + "properties": { + + + "auto_lineage": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Run auto-lineage pipelilne to automatically determine BUSCO lineage dataset that is most closely related to the assembly or gene set being assessed", + "help_text": "Type: `boolean_true`, default: `false`. Run auto-lineage pipelilne to automatically determine BUSCO lineage dataset that is most closely related to the assembly or gene set being assessed.\n" + , + "default": "False" + } + + + , + "auto_lineage_euk": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Run auto-placement just on eukaryota tree to find optimal lineage path", + "help_text": "Type: `boolean_true`, default: `false`. Run auto-placement just on eukaryota tree to find optimal lineage path.\n" + , + "default": "False" + } + + + , + "auto_lineage_prok": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Run auto_lineage just on prokaryota trees to find optimum lineage path", + "help_text": "Type: `boolean_true`, default: `false`. Run auto_lineage just on prokaryota trees to find optimum lineage path.\n" + , + "default": "False" + } + + + , + "datasets_version": { + "type": + "string", + "description": "Type: `string`, example: `odb10`. Specify the version of BUSCO datasets\n", + "help_text": "Type: `string`, example: `odb10`. Specify the version of BUSCO datasets\n" + + } + + +} +}, + + + "augustus settings" : { + "title": "Augustus Settings", + "type": "object", + "description": "No description", + "properties": { + + + "augustus": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use augustus gene predictor for eukaryote runs", + "help_text": "Type: `boolean_true`, default: `false`. Use augustus gene predictor for eukaryote runs.\n" + , + "default": "False" + } + + + , + "augustus_parameters": { + "type": + "string", + "description": "Type: `string`, example: `--PARAM1=VALUE1,--PARAM2=VALUE2`. Additional parameters to be passed to Augustus (see Augustus documentation: https://github", + "help_text": "Type: `string`, example: `--PARAM1=VALUE1,--PARAM2=VALUE2`. Additional parameters to be passed to Augustus (see Augustus documentation: https://github.com/Gaius-Augustus/Augustus/blob/master/docs/RUNNING-AUGUSTUS.md).\nParameters should be contained within a single string, without whitespace and seperated by commas.\n" + + } + + + , + "augustus_species": { + "type": + "string", + "description": "Type: `string`. Specify the augustus species\n", + "help_text": "Type: `string`. Specify the augustus species\n" + + } + + + , + "long": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Optimize Augustus self-training mode", + "help_text": "Type: `boolean_true`, default: `false`. Optimize Augustus self-training mode. This adds considerably to the run time, but can improve results for some non-model organisms.\n" + , + "default": "False" + } + + +} +}, + + + "bbtools settings" : { + "title": "BBTools Settings", + "type": "object", + "description": "No description", + "properties": { + + + "contig_break": { + "type": + "integer", + "description": "Type: `integer`. Number of contiguous Ns to signify a break between contigs in BBTools analysis", + "help_text": "Type: `integer`. Number of contiguous Ns to signify a break between contigs in BBTools analysis.\n" + + } + + + , + "limit": { + "type": + "integer", + "description": "Type: `integer`. Number of candidate regions (contig or transcript) from the BLAST output to consider per BUSCO", + "help_text": "Type: `integer`. Number of candidate regions (contig or transcript) from the BLAST output to consider per BUSCO.\nThis option is only effective in pipelines using BLAST, i.e. the genome pipeline (see --augustus) or the prokaryota transcriptome pipeline.\n" + + } + + + , + "scaffold_composition": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Writes ACGTN content per scaffold to a file scaffold_composition", + "help_text": "Type: `boolean_true`, default: `false`. Writes ACGTN content per scaffold to a file scaffold_composition.txt.\n" + , + "default": "False" + } + + +} +}, + + + "blast settings" : { + "title": "BLAST Settings", + "type": "object", + "description": "No description", + "properties": { + + + "e_value": { + "type": + "number", + "description": "Type: `double`. E-value cutoff for BLAST searches", + "help_text": "Type: `double`. E-value cutoff for BLAST searches.\n" + + } + + +} +}, + + + "protein gene prediction settings" : { + "title": "Protein Gene Prediction settings", + "type": "object", + "description": "No description", + "properties": { + + + "miniprot": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use Miniprot gene predictor", + "help_text": "Type: `boolean_true`, default: `false`. Use Miniprot gene predictor.\n" + , + "default": "False" + } + + +} +}, + + + "metaeuk settings" : { + "title": "MetaEuk Settings", + "type": "object", + "description": "No description", + "properties": { + + + "metaeuk_parameters": { + "type": + "string", + "description": "Type: `string`, example: `--max-overlap=15,--min-exon-aa=15`. Pass additional arguments to Metaeuk for the first run (see Metaeuk documentation https://github", + "help_text": "Type: `string`, example: `--max-overlap=15,--min-exon-aa=15`. Pass additional arguments to Metaeuk for the first run (see Metaeuk documentation https://github.com/soedinglab/metaeuk).\nAll parameters should be contained within a single string with no white space, with each parameter separated by a comma.\n" + + } + + + , + "metaeuk_rerun_parameters": { + "type": + "string", + "description": "Type: `string`, example: `--max-overlap=15,--min-exon-aa=15`. Pass additional arguments to Metaeuk for the second run (see Metaeuk documentation https://github", + "help_text": "Type: `string`, example: `--max-overlap=15,--min-exon-aa=15`. Pass additional arguments to Metaeuk for the second run (see Metaeuk documentation https://github.com/soedinglab/metaeuk).\nAll parameters should be contained within a single string with no white space, with each parameter separated by a comma.\n" + + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/resource and run settings" + }, + + { + "$ref": "#/definitions/lineage dataset settings" + }, + + { + "$ref": "#/definitions/augustus settings" + }, + + { + "$ref": "#/definitions/bbtools settings" + }, + + { + "$ref": "#/definitions/blast settings" + }, + + { + "$ref": "#/definitions/protein gene prediction settings" + }, + + { + "$ref": "#/definitions/metaeuk settings" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/cutadapt/.config.vsh.yaml b/target/nextflow/cutadapt/.config.vsh.yaml new file mode 100644 index 00000000..0656b87b --- /dev/null +++ b/target/nextflow/cutadapt/.config.vsh.yaml @@ -0,0 +1,733 @@ +name: "cutadapt" +version: "main" +argument_groups: +- name: "Specify Adapters for R1" + arguments: + - type: "string" + name: "--adapter" + alternatives: + - "-a" + description: "Sequence of an adapter ligated to the 3' end (paired data:\nof the\ + \ first read). The adapter and subsequent bases are\ntrimmed. If a '$' character\ + \ is appended ('anchoring'), the\nadapter is only found if it is a suffix of\ + \ the read.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--front" + alternatives: + - "-g" + description: "Sequence of an adapter ligated to the 5' end (paired data:\nof the\ + \ first read). The adapter and any preceding bases\nare trimmed. Partial matches\ + \ at the 5' end are allowed. If\na '^' character is prepended ('anchoring'),\ + \ the adapter is\nonly found if it is a prefix of the read.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--anywhere" + alternatives: + - "-b" + description: "Sequence of an adapter that may be ligated to the 5' or 3'\nend\ + \ (paired data: of the first read). Both types of\nmatches as described under\ + \ -a and -g are allowed. If the\nfirst base of the read is part of the match,\ + \ the behavior\nis as with -g, otherwise as with -a. This option is mostly\n\ + for rescuing failed library preparations - do not use if\nyou know which end\ + \ your adapter was ligated to!\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Specify Adapters using Fasta files for R1" + arguments: + - type: "file" + name: "--adapter_fasta" + description: "Fasta file containing sequences of an adapter ligated to the 3'\ + \ end (paired data:\nof the first read). The adapter and subsequent bases are\n\ + trimmed. If a '$' character is appended ('anchoring'), the\nadapter is only\ + \ found if it is a suffix of the read.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--front_fasta" + description: "Fasta file containing sequences of an adapter ligated to the 5'\ + \ end (paired data:\nof the first read). The adapter and any preceding bases\n\ + are trimmed. Partial matches at the 5' end are allowed. If\na '^' character\ + \ is prepended ('anchoring'), the adapter is\nonly found if it is a prefix of\ + \ the read.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--anywhere_fasta" + description: "Fasta file containing sequences of an adapter that may be ligated\ + \ to the 5' or 3'\nend (paired data: of the first read). Both types of\nmatches\ + \ as described under -a and -g are allowed. If the\nfirst base of the read is\ + \ part of the match, the behavior\nis as with -g, otherwise as with -a. This\ + \ option is mostly\nfor rescuing failed library preparations - do not use if\n\ + you know which end your adapter was ligated to!\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Specify Adapters for R2" + arguments: + - type: "string" + name: "--adapter_r2" + alternatives: + - "-A" + description: "Sequence of an adapter ligated to the 3' end (paired data:\nof the\ + \ first read). The adapter and subsequent bases are\ntrimmed. If a '$' character\ + \ is appended ('anchoring'), the\nadapter is only found if it is a suffix of\ + \ the read.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--front_r2" + alternatives: + - "-G" + description: "Sequence of an adapter ligated to the 5' end (paired data:\nof the\ + \ first read). The adapter and any preceding bases\nare trimmed. Partial matches\ + \ at the 5' end are allowed. If\na '^' character is prepended ('anchoring'),\ + \ the adapter is\nonly found if it is a prefix of the read.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--anywhere_r2" + alternatives: + - "-B" + description: "Sequence of an adapter that may be ligated to the 5' or 3'\nend\ + \ (paired data: of the first read). Both types of\nmatches as described under\ + \ -a and -g are allowed. If the\nfirst base of the read is part of the match,\ + \ the behavior\nis as with -g, otherwise as with -a. This option is mostly\n\ + for rescuing failed library preparations - do not use if\nyou know which end\ + \ your adapter was ligated to!\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Specify Adapters using Fasta files for R2" + arguments: + - type: "file" + name: "--adapter_r2_fasta" + description: "Fasta file containing sequences of an adapter ligated to the 3'\ + \ end (paired data:\nof the first read). The adapter and subsequent bases are\n\ + trimmed. If a '$' character is appended ('anchoring'), the\nadapter is only\ + \ found if it is a suffix of the read.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--front_r2_fasta" + description: "Fasta file containing sequences of an adapter ligated to the 5'\ + \ end (paired data:\nof the first read). The adapter and any preceding bases\n\ + are trimmed. Partial matches at the 5' end are allowed. If\na '^' character\ + \ is prepended ('anchoring'), the adapter is\nonly found if it is a prefix of\ + \ the read.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--anywhere_r2_fasta" + description: "Fasta file containing sequences of an adapter that may be ligated\ + \ to the 5' or 3'\nend (paired data: of the first read). Both types of\nmatches\ + \ as described under -a and -g are allowed. If the\nfirst base of the read is\ + \ part of the match, the behavior\nis as with -g, otherwise as with -a. This\ + \ option is mostly\nfor rescuing failed library preparations - do not use if\n\ + you know which end your adapter was ligated to!\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Paired-end options" + arguments: + - type: "boolean_true" + name: "--pair_adapters" + description: "Treat adapters given with -a/-A etc. as pairs. Either both\nor none\ + \ are removed from each read pair.\n" + info: null + direction: "input" + - type: "string" + name: "--pair_filter" + description: "Which of the reads in a paired-end read have to match the\nfiltering\ + \ criterion in order for the pair to be filtered.\n" + info: null + required: false + choices: + - "any" + - "both" + - "first" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--interleaved" + description: "Read and/or write interleaved paired-end reads.\n" + info: null + direction: "input" +- name: "Input parameters" + arguments: + - type: "file" + name: "--input" + description: "Input fastq file for single-end reads or R1 for paired-end reads.\n" + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--input_r2" + description: "Input fastq file for R2 in the case of paired-end reads.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--error_rate" + alternatives: + - "-E" + - "--errors" + description: "Maximum allowed error rate (if 0 <= E < 1), or absolute\nnumber\ + \ of errors for full-length adapter match (if E is an\ninteger >= 1). Error\ + \ rate = no. of errors divided by\nlength of matching region. Default: 0.1 (10%).\n" + info: null + example: + - 0.1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_false" + name: "--no_indels" + description: "Allow only mismatches in alignments.\n" + info: null + direction: "input" + - type: "integer" + name: "--times" + alternatives: + - "-n" + description: "Remove up to COUNT adapters from each read. Default: 1.\n" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--overlap" + alternatives: + - "-O" + description: "Require MINLENGTH overlap between read and adapter for an\nadapter\ + \ to be found. The default is 3.\n" + info: null + example: + - 3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--match_read_wildcards" + description: "Interpret IUPAC wildcards in reads.\n" + info: null + direction: "input" + - type: "boolean_false" + name: "--no_match_adapter_wildcards" + description: "Do not interpret IUPAC wildcards in adapters.\n" + info: null + direction: "input" + - type: "string" + name: "--action" + description: "What to do if a match was found. trim: trim adapter and\nup- or\ + \ downstream sequence; retain: trim, but retain\nadapter; mask: replace with\ + \ 'N' characters; lowercase:\nconvert to lowercase; none: leave unchanged.\n\ + The default is trim.\n" + info: null + example: + - "trim" + required: false + choices: + - "trim" + - "retain" + - "mask" + - "lowercase" + - "none" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--revcomp" + alternatives: + - "--rc" + description: "Check both the read and its reverse complement for adapter\nmatches.\ + \ If match is on reverse-complemented version,\noutput that one.\n" + info: null + direction: "input" +- name: "Read modifications" + arguments: + - type: "integer" + name: "--cut" + alternatives: + - "-u" + description: "Remove LEN bases from each read (or R1 if paired; use --cut_r2\n\ + option for R2). If LEN is positive, remove bases from the\nbeginning. If LEN\ + \ is negative, remove bases from the end.\nCan be used twice if LENs have different\ + \ signs. Applied\n*before* adapter trimming.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--cut_r2" + description: "Remove LEN bases from each read (for R2). If LEN is positive, remove\ + \ bases from the\nbeginning. If LEN is negative, remove bases from the end.\n\ + Can be used twice if LENs have different signs. Applied\n*before* adapter trimming.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--nextseq_trim" + description: "NextSeq-specific quality trimming (each read). Trims also\ndark\ + \ cycles appearing as high-quality G bases.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--quality_cutoff" + alternatives: + - "-q" + description: "Trim low-quality bases from 5' and/or 3' ends of each read\nbefore\ + \ adapter removal. Applied to both reads if data is\npaired. If one value is\ + \ given, only the 3' end is trimmed.\nIf two comma-separated cutoffs are given,\ + \ the 5' end is\ntrimmed with the first cutoff, the 3' end with the second.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--quality_cutoff_r2" + alternatives: + - "-Q" + description: "Quality-trimming cutoff for R2. Default: same as for R1\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--quality_base" + description: "Assume that quality values in FASTQ are encoded as\nascii(quality\ + \ + N). This needs to be set to 64 for some\nold Illumina FASTQ files. The default\ + \ is 33.\n" + info: null + example: + - 33 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--poly_a" + description: "Trim poly-A tails" + info: null + direction: "input" + - type: "integer" + name: "--length" + alternatives: + - "-l" + description: "Shorten reads to LENGTH. Positive values remove bases at\nthe end\ + \ while negative ones remove bases at the beginning.\nThis and the following\ + \ modifications are applied after\nadapter trimming.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--trim_n" + description: "Trim N's on ends of reads." + info: null + direction: "input" + - type: "string" + name: "--length_tag" + description: "Search for TAG followed by a decimal number in the\ndescription\ + \ field of the read. Replace the decimal number\nwith the correct length of\ + \ the trimmed read. For example,\nuse --length-tag 'length=' to correct fields\ + \ like\n'length=123'.\n" + info: null + example: + - "length=" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--strip_suffix" + description: "Remove this suffix from read names if present. Can be\ngiven multiple\ + \ times.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--prefix" + alternatives: + - "-x" + description: "Add this prefix to read names. Use {name} to insert the\nname of\ + \ the matching adapter.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--suffix" + alternatives: + - "-y" + description: "Add this suffix to read names; can also include {name}\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--rename" + description: "Rename reads using TEMPLATE containing variables such as\n{id},\ + \ {adapter_name} etc. (see documentation)\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--zero_cap" + alternatives: + - "-z" + description: "Change negative quality values to zero." + info: null + direction: "input" +- name: "Filtering of processed reads" + description: "Filters are applied after above read modifications. Paired-end reads\ + \ are\nalways discarded pairwise (see also --pair_filter).\n" + arguments: + - type: "string" + name: "--minimum_length" + alternatives: + - "-m" + description: "Discard reads shorter than LEN. Default is 0.\nWhen trimming paired-end\ + \ reads, the minimum lengths for R1 and R2 can be specified separately by separating\ + \ them with a colon (:).\nIf the colon syntax is not used, the same minimum\ + \ length applies to both reads, as discussed above.\nAlso, one of the values\ + \ can be omitted to impose no restrictions.\nFor example, with -m 17:, the length\ + \ of R1 must be at least 17, but the length of R2 is ignored.\n" + info: null + example: + - "0" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--maximum_length" + alternatives: + - "-M" + description: "Discard reads longer than LEN. Default: no limit.\nFor paired reads,\ + \ see the remark for --minimum_length\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--max_n" + description: "Discard reads with more than COUNT 'N' bases. If COUNT is\na number\ + \ between 0 and 1, it is interpreted as a fraction\nof the read length.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "long" + name: "--max_expected_errors" + alternatives: + - "--max_ee" + description: "Discard reads whose expected number of errors (computed\nfrom quality\ + \ values) exceeds ERRORS.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "long" + name: "--max_average_error_rate" + alternatives: + - "--max_aer" + description: "as --max_expected_errors (see above), but divided by\nlength to\ + \ account for reads of varying length.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--discard_trimmed" + alternatives: + - "--discard" + description: "Discard reads that contain an adapter. Use also -O to\navoid discarding\ + \ too many randomly matching reads.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--discard_untrimmed" + alternatives: + - "--trimmed_only" + description: "Discard reads that do not contain an adapter.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--discard_casava" + description: "Discard reads that did not pass CASAVA filtering (header\nhas :Y:).\n" + info: null + direction: "input" +- name: "Output parameters" + arguments: + - type: "string" + name: "--report" + description: "Which type of report to print: 'full' (default) or 'minimal'.\n" + info: null + example: + - "full" + required: false + choices: + - "full" + - "minimal" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--json" + description: "Write report in JSON format to this file.\n" + info: null + direction: "input" + - type: "file" + name: "--output" + description: "Glob pattern for matching the expected output files.\nShould include\ + \ `$output_dir`.\n" + info: null + example: + - "fastq/*_001.fast[a,q]" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: true + multiple_sep: ";" + - type: "boolean_true" + name: "--fasta" + description: "Output FASTA to standard output even on FASTQ input.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--info_file" + description: "Write information about each read and its adapter matches\ninto\ + \ info.txt in the output directory.\nSee the documentation for the file format.\n" + info: null + direction: "input" +- name: "Debug" + arguments: + - type: "boolean_true" + name: "--debug" + description: "Print debug information" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Cutadapt removes adapter sequences from high-throughput sequencing reads.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "RNA-seq" +- "scRNA-seq" +- "high-throughput" +license: "MIT" +references: + doi: + - "10.14806/ej.17.1.200" +links: + repository: "https://github.com/marcelm/cutadapt" + homepage: "https://cutadapt.readthedocs.io" + documentation: "https://cutadapt.readthedocs.io" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "python:3.12" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "python" + user: false + pip: + - "cutadapt" + upgrade: true + - type: "docker" + run: + - "cutadapt --version | sed 's/\\(.*\\)/cutadapt: \"\\1\"/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/cutadapt/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/cutadapt" + executable: "target/nextflow/cutadapt/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/cutadapt/main.nf b/target/nextflow/cutadapt/main.nf new file mode 100644 index 00000000..e96ec886 --- /dev/null +++ b/target/nextflow/cutadapt/main.nf @@ -0,0 +1,4371 @@ +// cutadapt main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "cutadapt", + "version" : "main", + "argument_groups" : [ + { + "name" : "Specify Adapters for R1", + "arguments" : [ + { + "type" : "string", + "name" : "--adapter", + "alternatives" : [ + "-a" + ], + "description" : "Sequence of an adapter ligated to the 3' end (paired data:\nof the first read). The adapter and subsequent bases are\ntrimmed. If a '$' character is appended ('anchoring'), the\nadapter is only found if it is a suffix of the read.\n", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--front", + "alternatives" : [ + "-g" + ], + "description" : "Sequence of an adapter ligated to the 5' end (paired data:\nof the first read). The adapter and any preceding bases\nare trimmed. Partial matches at the 5' end are allowed. If\na '^' character is prepended ('anchoring'), the adapter is\nonly found if it is a prefix of the read.\n", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--anywhere", + "alternatives" : [ + "-b" + ], + "description" : "Sequence of an adapter that may be ligated to the 5' or 3'\nend (paired data: of the first read). Both types of\nmatches as described under -a and -g are allowed. If the\nfirst base of the read is part of the match, the behavior\nis as with -g, otherwise as with -a. This option is mostly\nfor rescuing failed library preparations - do not use if\nyou know which end your adapter was ligated to!\n", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Specify Adapters using Fasta files for R1", + "arguments" : [ + { + "type" : "file", + "name" : "--adapter_fasta", + "description" : "Fasta file containing sequences of an adapter ligated to the 3' end (paired data:\nof the first read). The adapter and subsequent bases are\ntrimmed. If a '$' character is appended ('anchoring'), the\nadapter is only found if it is a suffix of the read.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--front_fasta", + "description" : "Fasta file containing sequences of an adapter ligated to the 5' end (paired data:\nof the first read). The adapter and any preceding bases\nare trimmed. Partial matches at the 5' end are allowed. If\na '^' character is prepended ('anchoring'), the adapter is\nonly found if it is a prefix of the read.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--anywhere_fasta", + "description" : "Fasta file containing sequences of an adapter that may be ligated to the 5' or 3'\nend (paired data: of the first read). Both types of\nmatches as described under -a and -g are allowed. If the\nfirst base of the read is part of the match, the behavior\nis as with -g, otherwise as with -a. This option is mostly\nfor rescuing failed library preparations - do not use if\nyou know which end your adapter was ligated to!\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Specify Adapters for R2", + "arguments" : [ + { + "type" : "string", + "name" : "--adapter_r2", + "alternatives" : [ + "-A" + ], + "description" : "Sequence of an adapter ligated to the 3' end (paired data:\nof the first read). The adapter and subsequent bases are\ntrimmed. If a '$' character is appended ('anchoring'), the\nadapter is only found if it is a suffix of the read.\n", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--front_r2", + "alternatives" : [ + "-G" + ], + "description" : "Sequence of an adapter ligated to the 5' end (paired data:\nof the first read). The adapter and any preceding bases\nare trimmed. Partial matches at the 5' end are allowed. If\na '^' character is prepended ('anchoring'), the adapter is\nonly found if it is a prefix of the read.\n", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--anywhere_r2", + "alternatives" : [ + "-B" + ], + "description" : "Sequence of an adapter that may be ligated to the 5' or 3'\nend (paired data: of the first read). Both types of\nmatches as described under -a and -g are allowed. If the\nfirst base of the read is part of the match, the behavior\nis as with -g, otherwise as with -a. This option is mostly\nfor rescuing failed library preparations - do not use if\nyou know which end your adapter was ligated to!\n", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Specify Adapters using Fasta files for R2", + "arguments" : [ + { + "type" : "file", + "name" : "--adapter_r2_fasta", + "description" : "Fasta file containing sequences of an adapter ligated to the 3' end (paired data:\nof the first read). The adapter and subsequent bases are\ntrimmed. If a '$' character is appended ('anchoring'), the\nadapter is only found if it is a suffix of the read.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--front_r2_fasta", + "description" : "Fasta file containing sequences of an adapter ligated to the 5' end (paired data:\nof the first read). The adapter and any preceding bases\nare trimmed. Partial matches at the 5' end are allowed. If\na '^' character is prepended ('anchoring'), the adapter is\nonly found if it is a prefix of the read.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--anywhere_r2_fasta", + "description" : "Fasta file containing sequences of an adapter that may be ligated to the 5' or 3'\nend (paired data: of the first read). Both types of\nmatches as described under -a and -g are allowed. If the\nfirst base of the read is part of the match, the behavior\nis as with -g, otherwise as with -a. This option is mostly\nfor rescuing failed library preparations - do not use if\nyou know which end your adapter was ligated to!\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Paired-end options", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--pair_adapters", + "description" : "Treat adapters given with -a/-A etc. as pairs. Either both\nor none are removed from each read pair.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--pair_filter", + "description" : "Which of the reads in a paired-end read have to match the\nfiltering criterion in order for the pair to be filtered.\n", + "required" : false, + "choices" : [ + "any", + "both", + "first" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--interleaved", + "description" : "Read and/or write interleaved paired-end reads.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Input parameters", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "Input fastq file for single-end reads or R1 for paired-end reads.\n", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--input_r2", + "description" : "Input fastq file for R2 in the case of paired-end reads.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--error_rate", + "alternatives" : [ + "-E", + "--errors" + ], + "description" : "Maximum allowed error rate (if 0 <= E < 1), or absolute\nnumber of errors for full-length adapter match (if E is an\ninteger >= 1). Error rate = no. of errors divided by\nlength of matching region. Default: 0.1 (10%).\n", + "example" : [ + 0.1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_false", + "name" : "--no_indels", + "description" : "Allow only mismatches in alignments.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--times", + "alternatives" : [ + "-n" + ], + "description" : "Remove up to COUNT adapters from each read. Default: 1.\n", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--overlap", + "alternatives" : [ + "-O" + ], + "description" : "Require MINLENGTH overlap between read and adapter for an\nadapter to be found. The default is 3.\n", + "example" : [ + 3 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--match_read_wildcards", + "description" : "Interpret IUPAC wildcards in reads.\n", + "direction" : "input" + }, + { + "type" : "boolean_false", + "name" : "--no_match_adapter_wildcards", + "description" : "Do not interpret IUPAC wildcards in adapters.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--action", + "description" : "What to do if a match was found. trim: trim adapter and\nup- or downstream sequence; retain: trim, but retain\nadapter; mask: replace with 'N' characters; lowercase:\nconvert to lowercase; none: leave unchanged.\nThe default is trim.\n", + "example" : [ + "trim" + ], + "required" : false, + "choices" : [ + "trim", + "retain", + "mask", + "lowercase", + "none" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--revcomp", + "alternatives" : [ + "--rc" + ], + "description" : "Check both the read and its reverse complement for adapter\nmatches. If match is on reverse-complemented version,\noutput that one.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Read modifications", + "arguments" : [ + { + "type" : "integer", + "name" : "--cut", + "alternatives" : [ + "-u" + ], + "description" : "Remove LEN bases from each read (or R1 if paired; use --cut_r2\noption for R2). If LEN is positive, remove bases from the\nbeginning. If LEN is negative, remove bases from the end.\nCan be used twice if LENs have different signs. Applied\n*before* adapter trimming.\n", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cut_r2", + "description" : "Remove LEN bases from each read (for R2). If LEN is positive, remove bases from the\nbeginning. If LEN is negative, remove bases from the end.\nCan be used twice if LENs have different signs. Applied\n*before* adapter trimming.\n", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--nextseq_trim", + "description" : "NextSeq-specific quality trimming (each read). Trims also\ndark cycles appearing as high-quality G bases.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--quality_cutoff", + "alternatives" : [ + "-q" + ], + "description" : "Trim low-quality bases from 5' and/or 3' ends of each read\nbefore adapter removal. Applied to both reads if data is\npaired. If one value is given, only the 3' end is trimmed.\nIf two comma-separated cutoffs are given, the 5' end is\ntrimmed with the first cutoff, the 3' end with the second.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--quality_cutoff_r2", + "alternatives" : [ + "-Q" + ], + "description" : "Quality-trimming cutoff for R2. Default: same as for R1\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--quality_base", + "description" : "Assume that quality values in FASTQ are encoded as\nascii(quality + N). This needs to be set to 64 for some\nold Illumina FASTQ files. The default is 33.\n", + "example" : [ + 33 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--poly_a", + "description" : "Trim poly-A tails", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--length", + "alternatives" : [ + "-l" + ], + "description" : "Shorten reads to LENGTH. Positive values remove bases at\nthe end while negative ones remove bases at the beginning.\nThis and the following modifications are applied after\nadapter trimming.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--trim_n", + "description" : "Trim N's on ends of reads.", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--length_tag", + "description" : "Search for TAG followed by a decimal number in the\ndescription field of the read. Replace the decimal number\nwith the correct length of the trimmed read. For example,\nuse --length-tag 'length=' to correct fields like\n'length=123'.\n", + "example" : [ + "length=" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--strip_suffix", + "description" : "Remove this suffix from read names if present. Can be\ngiven multiple times.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--prefix", + "alternatives" : [ + "-x" + ], + "description" : "Add this prefix to read names. Use {name} to insert the\nname of the matching adapter.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--suffix", + "alternatives" : [ + "-y" + ], + "description" : "Add this suffix to read names; can also include {name}\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--rename", + "description" : "Rename reads using TEMPLATE containing variables such as\n{id}, {adapter_name} etc. (see documentation)\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--zero_cap", + "alternatives" : [ + "-z" + ], + "description" : "Change negative quality values to zero.", + "direction" : "input" + } + ] + }, + { + "name" : "Filtering of processed reads", + "description" : "Filters are applied after above read modifications. Paired-end reads are\nalways discarded pairwise (see also --pair_filter).\n", + "arguments" : [ + { + "type" : "string", + "name" : "--minimum_length", + "alternatives" : [ + "-m" + ], + "description" : "Discard reads shorter than LEN. Default is 0.\nWhen trimming paired-end reads, the minimum lengths for R1 and R2 can be specified separately by separating them with a colon (:).\nIf the colon syntax is not used, the same minimum length applies to both reads, as discussed above.\nAlso, one of the values can be omitted to impose no restrictions.\nFor example, with -m 17:, the length of R1 must be at least 17, but the length of R2 is ignored.\n", + "example" : [ + "0" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--maximum_length", + "alternatives" : [ + "-M" + ], + "description" : "Discard reads longer than LEN. Default: no limit.\nFor paired reads, see the remark for --minimum_length\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--max_n", + "description" : "Discard reads with more than COUNT 'N' bases. If COUNT is\na number between 0 and 1, it is interpreted as a fraction\nof the read length.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "long", + "name" : "--max_expected_errors", + "alternatives" : [ + "--max_ee" + ], + "description" : "Discard reads whose expected number of errors (computed\nfrom quality values) exceeds ERRORS.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "long", + "name" : "--max_average_error_rate", + "alternatives" : [ + "--max_aer" + ], + "description" : "as --max_expected_errors (see above), but divided by\nlength to account for reads of varying length.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--discard_trimmed", + "alternatives" : [ + "--discard" + ], + "description" : "Discard reads that contain an adapter. Use also -O to\navoid discarding too many randomly matching reads.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--discard_untrimmed", + "alternatives" : [ + "--trimmed_only" + ], + "description" : "Discard reads that do not contain an adapter.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--discard_casava", + "description" : "Discard reads that did not pass CASAVA filtering (header\nhas :Y:).\n", + "direction" : "input" + } + ] + }, + { + "name" : "Output parameters", + "arguments" : [ + { + "type" : "string", + "name" : "--report", + "description" : "Which type of report to print: 'full' (default) or 'minimal'.\n", + "example" : [ + "full" + ], + "required" : false, + "choices" : [ + "full", + "minimal" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--json", + "description" : "Write report in JSON format to this file.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--output", + "description" : "Glob pattern for matching the expected output files.\nShould include `$output_dir`.\n", + "example" : [ + "fastq/*_001.fast[a,q]" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--fasta", + "description" : "Output FASTA to standard output even on FASTQ input.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--info_file", + "description" : "Write information about each read and its adapter matches\ninto info.txt in the output directory.\nSee the documentation for the file format.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Debug", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--debug", + "description" : "Print debug information", + "direction" : "input" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Cutadapt removes adapter sequences from high-throughput sequencing reads.\n", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "RNA-seq", + "scRNA-seq", + "high-throughput" + ], + "license" : "MIT", + "references" : { + "doi" : [ + "10.14806/ej.17.1.200" + ] + }, + "links" : { + "repository" : "https://github.com/marcelm/cutadapt", + "homepage" : "https://cutadapt.readthedocs.io", + "documentation" : "https://cutadapt.readthedocs.io" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "python:3.12", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "python", + "user" : false, + "pip" : [ + "cutadapt" + ], + "upgrade" : true + }, + { + "type" : "docker", + "run" : [ + "cutadapt --version | sed 's/\\\\(.*\\\\)/cutadapt: \\"\\\\1\\"/' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/cutadapt/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/cutadapt", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_ADAPTER+x} ]; then echo "${VIASH_PAR_ADAPTER}" | sed "s#'#'\\"'\\"'#g;s#.*#par_adapter='&'#" ; else echo "# par_adapter="; fi ) +$( if [ ! -z ${VIASH_PAR_FRONT+x} ]; then echo "${VIASH_PAR_FRONT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_front='&'#" ; else echo "# par_front="; fi ) +$( if [ ! -z ${VIASH_PAR_ANYWHERE+x} ]; then echo "${VIASH_PAR_ANYWHERE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_anywhere='&'#" ; else echo "# par_anywhere="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_FASTA+x} ]; then echo "${VIASH_PAR_ADAPTER_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_adapter_fasta='&'#" ; else echo "# par_adapter_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_FRONT_FASTA+x} ]; then echo "${VIASH_PAR_FRONT_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_front_fasta='&'#" ; else echo "# par_front_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_ANYWHERE_FASTA+x} ]; then echo "${VIASH_PAR_ANYWHERE_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_anywhere_fasta='&'#" ; else echo "# par_anywhere_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_R2+x} ]; then echo "${VIASH_PAR_ADAPTER_R2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_adapter_r2='&'#" ; else echo "# par_adapter_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_FRONT_R2+x} ]; then echo "${VIASH_PAR_FRONT_R2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_front_r2='&'#" ; else echo "# par_front_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_ANYWHERE_R2+x} ]; then echo "${VIASH_PAR_ANYWHERE_R2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_anywhere_r2='&'#" ; else echo "# par_anywhere_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_R2_FASTA+x} ]; then echo "${VIASH_PAR_ADAPTER_R2_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_adapter_r2_fasta='&'#" ; else echo "# par_adapter_r2_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_FRONT_R2_FASTA+x} ]; then echo "${VIASH_PAR_FRONT_R2_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_front_r2_fasta='&'#" ; else echo "# par_front_r2_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_ANYWHERE_R2_FASTA+x} ]; then echo "${VIASH_PAR_ANYWHERE_R2_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_anywhere_r2_fasta='&'#" ; else echo "# par_anywhere_r2_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_PAIR_ADAPTERS+x} ]; then echo "${VIASH_PAR_PAIR_ADAPTERS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_pair_adapters='&'#" ; else echo "# par_pair_adapters="; fi ) +$( if [ ! -z ${VIASH_PAR_PAIR_FILTER+x} ]; then echo "${VIASH_PAR_PAIR_FILTER}" | sed "s#'#'\\"'\\"'#g;s#.*#par_pair_filter='&'#" ; else echo "# par_pair_filter="; fi ) +$( if [ ! -z ${VIASH_PAR_INTERLEAVED+x} ]; then echo "${VIASH_PAR_INTERLEAVED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_interleaved='&'#" ; else echo "# par_interleaved="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_R2+x} ]; then echo "${VIASH_PAR_INPUT_R2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input_r2='&'#" ; else echo "# par_input_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_ERROR_RATE+x} ]; then echo "${VIASH_PAR_ERROR_RATE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_error_rate='&'#" ; else echo "# par_error_rate="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_INDELS+x} ]; then echo "${VIASH_PAR_NO_INDELS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_indels='&'#" ; else echo "# par_no_indels="; fi ) +$( if [ ! -z ${VIASH_PAR_TIMES+x} ]; then echo "${VIASH_PAR_TIMES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_times='&'#" ; else echo "# par_times="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAP+x} ]; then echo "${VIASH_PAR_OVERLAP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_overlap='&'#" ; else echo "# par_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_MATCH_READ_WILDCARDS+x} ]; then echo "${VIASH_PAR_MATCH_READ_WILDCARDS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_match_read_wildcards='&'#" ; else echo "# par_match_read_wildcards="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_MATCH_ADAPTER_WILDCARDS+x} ]; then echo "${VIASH_PAR_NO_MATCH_ADAPTER_WILDCARDS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_match_adapter_wildcards='&'#" ; else echo "# par_no_match_adapter_wildcards="; fi ) +$( if [ ! -z ${VIASH_PAR_ACTION+x} ]; then echo "${VIASH_PAR_ACTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_action='&'#" ; else echo "# par_action="; fi ) +$( if [ ! -z ${VIASH_PAR_REVCOMP+x} ]; then echo "${VIASH_PAR_REVCOMP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_revcomp='&'#" ; else echo "# par_revcomp="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT+x} ]; then echo "${VIASH_PAR_CUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut='&'#" ; else echo "# par_cut="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_R2+x} ]; then echo "${VIASH_PAR_CUT_R2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_r2='&'#" ; else echo "# par_cut_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_NEXTSEQ_TRIM+x} ]; then echo "${VIASH_PAR_NEXTSEQ_TRIM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_nextseq_trim='&'#" ; else echo "# par_nextseq_trim="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALITY_CUTOFF+x} ]; then echo "${VIASH_PAR_QUALITY_CUTOFF}" | sed "s#'#'\\"'\\"'#g;s#.*#par_quality_cutoff='&'#" ; else echo "# par_quality_cutoff="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALITY_CUTOFF_R2+x} ]; then echo "${VIASH_PAR_QUALITY_CUTOFF_R2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_quality_cutoff_r2='&'#" ; else echo "# par_quality_cutoff_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALITY_BASE+x} ]; then echo "${VIASH_PAR_QUALITY_BASE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_quality_base='&'#" ; else echo "# par_quality_base="; fi ) +$( if [ ! -z ${VIASH_PAR_POLY_A+x} ]; then echo "${VIASH_PAR_POLY_A}" | sed "s#'#'\\"'\\"'#g;s#.*#par_poly_a='&'#" ; else echo "# par_poly_a="; fi ) +$( if [ ! -z ${VIASH_PAR_LENGTH+x} ]; then echo "${VIASH_PAR_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_length='&'#" ; else echo "# par_length="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_N+x} ]; then echo "${VIASH_PAR_TRIM_N}" | sed "s#'#'\\"'\\"'#g;s#.*#par_trim_n='&'#" ; else echo "# par_trim_n="; fi ) +$( if [ ! -z ${VIASH_PAR_LENGTH_TAG+x} ]; then echo "${VIASH_PAR_LENGTH_TAG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_length_tag='&'#" ; else echo "# par_length_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_STRIP_SUFFIX+x} ]; then echo "${VIASH_PAR_STRIP_SUFFIX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_strip_suffix='&'#" ; else echo "# par_strip_suffix="; fi ) +$( if [ ! -z ${VIASH_PAR_PREFIX+x} ]; then echo "${VIASH_PAR_PREFIX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_prefix='&'#" ; else echo "# par_prefix="; fi ) +$( if [ ! -z ${VIASH_PAR_SUFFIX+x} ]; then echo "${VIASH_PAR_SUFFIX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_suffix='&'#" ; else echo "# par_suffix="; fi ) +$( if [ ! -z ${VIASH_PAR_RENAME+x} ]; then echo "${VIASH_PAR_RENAME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_rename='&'#" ; else echo "# par_rename="; fi ) +$( if [ ! -z ${VIASH_PAR_ZERO_CAP+x} ]; then echo "${VIASH_PAR_ZERO_CAP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_zero_cap='&'#" ; else echo "# par_zero_cap="; fi ) +$( if [ ! -z ${VIASH_PAR_MINIMUM_LENGTH+x} ]; then echo "${VIASH_PAR_MINIMUM_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_minimum_length='&'#" ; else echo "# par_minimum_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MAXIMUM_LENGTH+x} ]; then echo "${VIASH_PAR_MAXIMUM_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_maximum_length='&'#" ; else echo "# par_maximum_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_N+x} ]; then echo "${VIASH_PAR_MAX_N}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_n='&'#" ; else echo "# par_max_n="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_EXPECTED_ERRORS+x} ]; then echo "${VIASH_PAR_MAX_EXPECTED_ERRORS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_expected_errors='&'#" ; else echo "# par_max_expected_errors="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_AVERAGE_ERROR_RATE+x} ]; then echo "${VIASH_PAR_MAX_AVERAGE_ERROR_RATE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_average_error_rate='&'#" ; else echo "# par_max_average_error_rate="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARD_TRIMMED+x} ]; then echo "${VIASH_PAR_DISCARD_TRIMMED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_discard_trimmed='&'#" ; else echo "# par_discard_trimmed="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARD_UNTRIMMED+x} ]; then echo "${VIASH_PAR_DISCARD_UNTRIMMED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_discard_untrimmed='&'#" ; else echo "# par_discard_untrimmed="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARD_CASAVA+x} ]; then echo "${VIASH_PAR_DISCARD_CASAVA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_discard_casava='&'#" ; else echo "# par_discard_casava="; fi ) +$( if [ ! -z ${VIASH_PAR_REPORT+x} ]; then echo "${VIASH_PAR_REPORT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_report='&'#" ; else echo "# par_report="; fi ) +$( if [ ! -z ${VIASH_PAR_JSON+x} ]; then echo "${VIASH_PAR_JSON}" | sed "s#'#'\\"'\\"'#g;s#.*#par_json='&'#" ; else echo "# par_json="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_FASTA+x} ]; then echo "${VIASH_PAR_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fasta='&'#" ; else echo "# par_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_INFO_FILE+x} ]; then echo "${VIASH_PAR_INFO_FILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_info_file='&'#" ; else echo "# par_info_file="; fi ) +$( if [ ! -z ${VIASH_PAR_DEBUG+x} ]; then echo "${VIASH_PAR_DEBUG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_debug='&'#" ; else echo "# par_debug="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +function debug { + [[ "\\$par_debug" == "true" ]] && echo "DEBUG: \\$@" +} + +output_dir=\\$(dirname \\$par_output) +[[ ! -d \\$output_dir ]] && mkdir -p \\$output_dir + +# Init +########################################################### + +echo ">> Paired-end data or not?" + +mode="" +if [[ -z \\$par_input_r2 ]]; then + mode="se" + echo " Single end" + input="\\$par_input" +else + echo " Paired end" + mode="pe" + input="\\$par_input \\$par_input_r2" +fi + +# Adapter arguments +# - paired and single-end +# - string and fasta +########################################################### + +function add_flags { + local arg=\\$1 + local flag=\\$2 + local prefix=\\$3 + [[ -z \\$prefix ]] && prefix="" + + # This function should not be called if the input is empty + # but check for it just in case + if [[ -z \\$arg ]]; then + return + fi + + local output="" + IFS=';' read -r -a array <<< "\\$arg" + for a in "\\${array[@]}"; do + output="\\$output \\$flag \\$prefix\\$a" + done + echo \\$output +} + +debug ">> Parsing arguments dealing with adapters" +adapter_args=\\$(echo \\\\ + \\${par_adapter:+\\$(add_flags "\\$par_adapter" "--adapter")} \\\\ + \\${par_adapter_fasta:+\\$(add_flags "\\$par_adapter_fasta" "--adapter" "file:")} \\\\ + \\${par_front:+\\$(add_flags "\\$par_front" "--front")} \\\\ + \\${par_front_fasta:+\\$(add_flags "\\$par_front_fasta" "--front" "file:")} \\\\ + \\${par_anywhere:+\\$(add_flags "\\$par_anywhere" "--anywhere")} \\\\ + \\${par_anywhere_fasta:+\\$(add_flags "\\$par_anywhere_fasta" "--anywhere" "file:")} \\\\ + \\${par_adapter_r2:+\\$(add_flags "\\$par_adapter_r2" "-A")} \\\\ + \\${par_adapter_fasta_r2:+\\$(add_flags "\\$par_adapter_fasta_r2" "-A" "file:")} \\\\ + \\${par_front_r2:+\\$(add_flags "\\$par_front_r2" "-G")} \\\\ + \\${par_front_fasta_r2:+\\$(add_flags "\\$par_front_fasta_r2" "-G" "file:")} \\\\ + \\${par_anywhere_r2:+\\$(add_flags "\\$par_anywhere_r2" "-B")} \\\\ + \\${par_anywhere_fasta_r2:+\\$(add_flags "\\$par_anywhere_fasta_r2" "-B" "file:")} \\\\ +) + +debug "Arguments to cutadapt:" +debug "\\$adapter_args" +debug + +# Paired-end options +########################################################### +echo ">> Parsing arguments for paired-end reads" +[[ "\\$par_pair_adapters" == "false" ]] && unset par_pair_adapters +[[ "\\$par_interleaved" == "false" ]] && unset par_interleaved + +paired_args=\\$(echo \\\\ + \\${par_pair_adapters:+--pair-adapters} \\\\ + \\${par_pair_filter:+--pair-filter "\\${par_pair_filter}"} \\\\ + \\${par_interleaved:+--interleaved} +) +debug "Arguments to cutadapt:" +debug \\$paired_args +debug + +# Input arguments +########################################################### +echo ">> Parsing input arguments" +[[ "\\$par_no_indels" == "true" ]] && unset par_no_indels +[[ "\\$par_match_read_wildcards" == "false" ]] && unset par_match_read_wildcards +[[ "\\$par_no_match_adapter_wildcards" == "true" ]] && unset par_no_match_adapter_wildcards +[[ "\\$par_revcomp" == "false" ]] && unset par_revcomp + +input_args=\\$(echo \\\\ + \\${par_error_rate:+--error-rate "\\${par_error_rate}"} \\\\ + \\${par_no_indels:+--no-indels} \\\\ + \\${par_times:+--times "\\${par_times}"} \\\\ + \\${par_overlap:+--overlap "\\${par_overlap}"} \\\\ + \\${par_match_read_wildcards:+--match-read-wildcards} \\\\ + \\${par_no_match_adapter_wildcards:+--no-match-adapter-wildcards} \\\\ + \\${par_action:+--action "\\${par_action}"} \\\\ + \\${par_revcomp:+--revcomp} \\\\ +) +debug "Arguments to cutadapt:" +debug \\$input_args +debug + +# Read modifications +########################################################### +echo ">> Parsing read modification arguments" +[[ "\\$par_poly_a" == "false" ]] && unset par_poly_a +[[ "\\$par_trim_n" == "false" ]] && unset par_trim_n +[[ "\\$par_zero_cap" == "false" ]] && unset par_zero_cap + +mod_args=\\$(echo \\\\ + \\${par_cut:+--cut "\\${par_cut}"} \\\\ + \\${par_cut_r2:+--cut_r2 "\\${par_cut_r2}"} \\\\ + \\${par_nextseq_trim:+--nextseq-trim "\\${par_nextseq_trim}"} \\\\ + \\${par_quality_cutoff:+--quality-cutoff "\\${par_quality_cutoff}"} \\\\ + \\${par_quality_cutoff_r2:+--quality-cutoff_r2 "\\${par_quality_cutoff_r2}"} \\\\ + \\${par_quality_base:+--quality-base "\\${par_quality_base}"} \\\\ + \\${par_poly_a:+--poly-a} \\\\ + \\${par_length:+--length "\\${par_length}"} \\\\ + \\${par_trim_n:+--trim-n} \\\\ + \\${par_length_tag:+--length-tag "\\${par_length_tag}"} \\\\ + \\${par_strip_suffix:+--strip-suffix "\\${par_strip_suffix}"} \\\\ + \\${par_prefix:+--prefix "\\${par_prefix}"} \\\\ + \\${par_suffix:+--suffix "\\${par_suffix}"} \\\\ + \\${par_rename:+--rename "\\${par_rename}"} \\\\ + \\${par_zero_cap:+--zero-cap} \\\\ +) +debug "Arguments to cutadapt:" +debug \\$mod_args +debug + +# Filtering of processed reads arguments +########################################################### +echo ">> Filtering of processed reads arguments" +[[ "\\$par_discard_trimmed" == "false" ]] && unset par_discard_trimmed +[[ "\\$par_discard_untrimmed" == "false" ]] && unset par_discard_untrimmed +[[ "\\$par_discard_casava" == "false" ]] && unset par_discard_casava + +# Parse and transform the minimum and maximum length arguments +[[ -z \\$par_minimum_length ]] + +filter_args=\\$(echo \\\\ + \\${par_minimum_length:+--minimum-length "\\${par_minimum_length}"} \\\\ + \\${par_maximum_length:+--maximum-length "\\${par_maximum_length}"} \\\\ + \\${par_max_n:+--max-n "\\${par_max_n}"} \\\\ + \\${par_max_expected_errors:+--max-expected-errors "\\${par_max_expected_errors}"} \\\\ + \\${par_max_average_error_rate:+--max-average-error-rate "\\${par_max_average_error_rate}"} \\\\ + \\${par_discard_trimmed:+--discard-trimmed} \\\\ + \\${par_discard_untrimmed:+--discard-untrimmed} \\\\ + \\${par_discard_casava:+--discard-casava} \\\\ +) +debug "Arguments to cutadapt:" +debug \\$filter_args +debug + +# Optional output arguments +########################################################### +echo ">> Optional arguments" +[[ "\\$par_json" == "false" ]] && unset par_json +[[ "\\$par_fasta" == "false" ]] && unset par_fasta +[[ "\\$par_info_file" == "false" ]] && unset par_info_file + +optional_output_args=\\$(echo \\\\ + \\${par_report:+--report "\\${par_report}"} \\\\ + \\${par_json:+--json "report.json"} \\\\ + \\${par_fasta:+--fasta} \\\\ + \\${par_info_file:+--info-file "info.txt"} \\\\ +) + +debug "Arguments to cutadapt:" +debug \\$optional_output_args +debug + +# Output arguments +# We write the output to a directory rather than +# individual files. +########################################################### + +if [[ -z \\$par_fasta ]]; then + ext="fastq" +else + ext="fasta" +fi + +if [ \\$mode = "se" ]; then + output_args=\\$(echo \\\\ + --output "\\$output_dir/{name}_001.\\$ext" \\\\ + ) +else + output_args=\\$(echo \\\\ + --output "\\$output_dir/{name}_R1_001.\\$ext" \\\\ + --paired-output "\\$output_dir/{name}_R2_001.\\$ext" \\\\ + ) +fi + +debug "Arguments to cutadapt:" +debug \\$output_args +debug + +# Full CLI +# Set the --cores argument to 0 unless meta_cpus is set +########################################################### +echo ">> Running cutadapt" +par_cpus=0 +[[ ! -z \\$meta_cpus ]] && par_cpus=\\$meta_cpus + +cli=\\$(echo \\\\ + \\$input \\\\ + \\$adapter_args \\\\ + \\$paired_args \\\\ + \\$input_args \\\\ + \\$mod_args \\\\ + \\$filter_args \\\\ + \\$optional_output_args \\\\ + \\$output_args \\\\ + --cores \\$par_cpus +) + +debug ">> Full CLI to be run:" +debug cutadapt \\$cli | sed -e 's/--/\\\\r\\\\n --/g' +debug + +cutadapt \\$cli +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/cutadapt", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/cutadapt/nextflow.config b/target/nextflow/cutadapt/nextflow.config new file mode 100644 index 00000000..1ce86bac --- /dev/null +++ b/target/nextflow/cutadapt/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'cutadapt' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Cutadapt removes adapter sequences from high-throughput sequencing reads.\n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/cutadapt/nextflow_schema.json b/target/nextflow/cutadapt/nextflow_schema.json new file mode 100644 index 00000000..2438aca0 --- /dev/null +++ b/target/nextflow/cutadapt/nextflow_schema.json @@ -0,0 +1,749 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "cutadapt", +"description": "Cutadapt removes adapter sequences from high-throughput sequencing reads.\n", +"type": "object", +"definitions": { + + + + "specify adapters for r1" : { + "title": "Specify Adapters for R1", + "type": "object", + "description": "No description", + "properties": { + + + "adapter": { + "type": + "string", + "description": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter ligated to the 3\u0027 end (paired data:\nof the first read)", + "help_text": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter ligated to the 3\u0027 end (paired data:\nof the first read). The adapter and subsequent bases are\ntrimmed. If a \u0027$\u0027 character is appended (\u0027anchoring\u0027), the\nadapter is only found if it is a suffix of the read.\n" + + } + + + , + "front": { + "type": + "string", + "description": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter ligated to the 5\u0027 end (paired data:\nof the first read)", + "help_text": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter ligated to the 5\u0027 end (paired data:\nof the first read). The adapter and any preceding bases\nare trimmed. Partial matches at the 5\u0027 end are allowed. If\na \u0027^\u0027 character is prepended (\u0027anchoring\u0027), the adapter is\nonly found if it is a prefix of the read.\n" + + } + + + , + "anywhere": { + "type": + "string", + "description": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter that may be ligated to the 5\u0027 or 3\u0027\nend (paired data: of the first read)", + "help_text": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter that may be ligated to the 5\u0027 or 3\u0027\nend (paired data: of the first read). Both types of\nmatches as described under -a and -g are allowed. If the\nfirst base of the read is part of the match, the behavior\nis as with -g, otherwise as with -a. This option is mostly\nfor rescuing failed library preparations - do not use if\nyou know which end your adapter was ligated to!\n" + + } + + +} +}, + + + "specify adapters using fasta files for r1" : { + "title": "Specify Adapters using Fasta files for R1", + "type": "object", + "description": "No description", + "properties": { + + + "adapter_fasta": { + "type": + "string", + "description": "Type: List of `file`, multiple_sep: `\":\"`. Fasta file containing sequences of an adapter ligated to the 3\u0027 end (paired data:\nof the first read)", + "help_text": "Type: List of `file`, multiple_sep: `\":\"`. Fasta file containing sequences of an adapter ligated to the 3\u0027 end (paired data:\nof the first read). The adapter and subsequent bases are\ntrimmed. If a \u0027$\u0027 character is appended (\u0027anchoring\u0027), the\nadapter is only found if it is a suffix of the read.\n" + + } + + + , + "front_fasta": { + "type": + "string", + "description": "Type: `file`. Fasta file containing sequences of an adapter ligated to the 5\u0027 end (paired data:\nof the first read)", + "help_text": "Type: `file`. Fasta file containing sequences of an adapter ligated to the 5\u0027 end (paired data:\nof the first read). The adapter and any preceding bases\nare trimmed. Partial matches at the 5\u0027 end are allowed. If\na \u0027^\u0027 character is prepended (\u0027anchoring\u0027), the adapter is\nonly found if it is a prefix of the read.\n" + + } + + + , + "anywhere_fasta": { + "type": + "string", + "description": "Type: `file`. Fasta file containing sequences of an adapter that may be ligated to the 5\u0027 or 3\u0027\nend (paired data: of the first read)", + "help_text": "Type: `file`. Fasta file containing sequences of an adapter that may be ligated to the 5\u0027 or 3\u0027\nend (paired data: of the first read). Both types of\nmatches as described under -a and -g are allowed. If the\nfirst base of the read is part of the match, the behavior\nis as with -g, otherwise as with -a. This option is mostly\nfor rescuing failed library preparations - do not use if\nyou know which end your adapter was ligated to!\n" + + } + + +} +}, + + + "specify adapters for r2" : { + "title": "Specify Adapters for R2", + "type": "object", + "description": "No description", + "properties": { + + + "adapter_r2": { + "type": + "string", + "description": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter ligated to the 3\u0027 end (paired data:\nof the first read)", + "help_text": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter ligated to the 3\u0027 end (paired data:\nof the first read). The adapter and subsequent bases are\ntrimmed. If a \u0027$\u0027 character is appended (\u0027anchoring\u0027), the\nadapter is only found if it is a suffix of the read.\n" + + } + + + , + "front_r2": { + "type": + "string", + "description": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter ligated to the 5\u0027 end (paired data:\nof the first read)", + "help_text": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter ligated to the 5\u0027 end (paired data:\nof the first read). The adapter and any preceding bases\nare trimmed. Partial matches at the 5\u0027 end are allowed. If\na \u0027^\u0027 character is prepended (\u0027anchoring\u0027), the adapter is\nonly found if it is a prefix of the read.\n" + + } + + + , + "anywhere_r2": { + "type": + "string", + "description": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter that may be ligated to the 5\u0027 or 3\u0027\nend (paired data: of the first read)", + "help_text": "Type: List of `string`, multiple_sep: `\":\"`. Sequence of an adapter that may be ligated to the 5\u0027 or 3\u0027\nend (paired data: of the first read). Both types of\nmatches as described under -a and -g are allowed. If the\nfirst base of the read is part of the match, the behavior\nis as with -g, otherwise as with -a. This option is mostly\nfor rescuing failed library preparations - do not use if\nyou know which end your adapter was ligated to!\n" + + } + + +} +}, + + + "specify adapters using fasta files for r2" : { + "title": "Specify Adapters using Fasta files for R2", + "type": "object", + "description": "No description", + "properties": { + + + "adapter_r2_fasta": { + "type": + "string", + "description": "Type: `file`. Fasta file containing sequences of an adapter ligated to the 3\u0027 end (paired data:\nof the first read)", + "help_text": "Type: `file`. Fasta file containing sequences of an adapter ligated to the 3\u0027 end (paired data:\nof the first read). The adapter and subsequent bases are\ntrimmed. If a \u0027$\u0027 character is appended (\u0027anchoring\u0027), the\nadapter is only found if it is a suffix of the read.\n" + + } + + + , + "front_r2_fasta": { + "type": + "string", + "description": "Type: `file`. Fasta file containing sequences of an adapter ligated to the 5\u0027 end (paired data:\nof the first read)", + "help_text": "Type: `file`. Fasta file containing sequences of an adapter ligated to the 5\u0027 end (paired data:\nof the first read). The adapter and any preceding bases\nare trimmed. Partial matches at the 5\u0027 end are allowed. If\na \u0027^\u0027 character is prepended (\u0027anchoring\u0027), the adapter is\nonly found if it is a prefix of the read.\n" + + } + + + , + "anywhere_r2_fasta": { + "type": + "string", + "description": "Type: `file`. Fasta file containing sequences of an adapter that may be ligated to the 5\u0027 or 3\u0027\nend (paired data: of the first read)", + "help_text": "Type: `file`. Fasta file containing sequences of an adapter that may be ligated to the 5\u0027 or 3\u0027\nend (paired data: of the first read). Both types of\nmatches as described under -a and -g are allowed. If the\nfirst base of the read is part of the match, the behavior\nis as with -g, otherwise as with -a. This option is mostly\nfor rescuing failed library preparations - do not use if\nyou know which end your adapter was ligated to!\n" + + } + + +} +}, + + + "paired-end options" : { + "title": "Paired-end options", + "type": "object", + "description": "No description", + "properties": { + + + "pair_adapters": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Treat adapters given with -a/-A etc", + "help_text": "Type: `boolean_true`, default: `false`. Treat adapters given with -a/-A etc. as pairs. Either both\nor none are removed from each read pair.\n" + , + "default": "False" + } + + + , + "pair_filter": { + "type": + "string", + "description": "Type: `string`, choices: ``any`, `both`, `first``. Which of the reads in a paired-end read have to match the\nfiltering criterion in order for the pair to be filtered", + "help_text": "Type: `string`, choices: ``any`, `both`, `first``. Which of the reads in a paired-end read have to match the\nfiltering criterion in order for the pair to be filtered.\n", + "enum": ["any", "both", "first"] + + + } + + + , + "interleaved": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Read and/or write interleaved paired-end reads", + "help_text": "Type: `boolean_true`, default: `false`. Read and/or write interleaved paired-end reads.\n" + , + "default": "False" + } + + +} +}, + + + "input parameters" : { + "title": "Input parameters", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`, required. Input fastq file for single-end reads or R1 for paired-end reads", + "help_text": "Type: `file`, required. Input fastq file for single-end reads or R1 for paired-end reads.\n" + + } + + + , + "input_r2": { + "type": + "string", + "description": "Type: `file`. Input fastq file for R2 in the case of paired-end reads", + "help_text": "Type: `file`. Input fastq file for R2 in the case of paired-end reads.\n" + + } + + + , + "error_rate": { + "type": + "number", + "description": "Type: `double`, example: `0.1`. Maximum allowed error rate (if 0 \u003c= E \u003c 1), or absolute\nnumber of errors for full-length adapter match (if E is an\ninteger \u003e= 1)", + "help_text": "Type: `double`, example: `0.1`. Maximum allowed error rate (if 0 \u003c= E \u003c 1), or absolute\nnumber of errors for full-length adapter match (if E is an\ninteger \u003e= 1). Error rate = no. of errors divided by\nlength of matching region. Default: 0.1 (10%).\n" + + } + + + , + "no_indels": { + "type": + "boolean", + "description": "Type: `boolean_false`, default: `true`. Allow only mismatches in alignments", + "help_text": "Type: `boolean_false`, default: `true`. Allow only mismatches in alignments.\n" + , + "default": "True" + } + + + , + "times": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. Remove up to COUNT adapters from each read", + "help_text": "Type: `integer`, example: `1`. Remove up to COUNT adapters from each read. Default: 1.\n" + + } + + + , + "overlap": { + "type": + "integer", + "description": "Type: `integer`, example: `3`. Require MINLENGTH overlap between read and adapter for an\nadapter to be found", + "help_text": "Type: `integer`, example: `3`. Require MINLENGTH overlap between read and adapter for an\nadapter to be found. The default is 3.\n" + + } + + + , + "match_read_wildcards": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Interpret IUPAC wildcards in reads", + "help_text": "Type: `boolean_true`, default: `false`. Interpret IUPAC wildcards in reads.\n" + , + "default": "False" + } + + + , + "no_match_adapter_wildcards": { + "type": + "boolean", + "description": "Type: `boolean_false`, default: `true`. Do not interpret IUPAC wildcards in adapters", + "help_text": "Type: `boolean_false`, default: `true`. Do not interpret IUPAC wildcards in adapters.\n" + , + "default": "True" + } + + + , + "action": { + "type": + "string", + "description": "Type: `string`, example: `trim`, choices: ``trim`, `retain`, `mask`, `lowercase`, `none``. What to do if a match was found", + "help_text": "Type: `string`, example: `trim`, choices: ``trim`, `retain`, `mask`, `lowercase`, `none``. What to do if a match was found. trim: trim adapter and\nup- or downstream sequence; retain: trim, but retain\nadapter; mask: replace with \u0027N\u0027 characters; lowercase:\nconvert to lowercase; none: leave unchanged.\nThe default is trim.\n", + "enum": ["trim", "retain", "mask", "lowercase", "none"] + + + } + + + , + "revcomp": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Check both the read and its reverse complement for adapter\nmatches", + "help_text": "Type: `boolean_true`, default: `false`. Check both the read and its reverse complement for adapter\nmatches. If match is on reverse-complemented version,\noutput that one.\n" + , + "default": "False" + } + + +} +}, + + + "read modifications" : { + "title": "Read modifications", + "type": "object", + "description": "No description", + "properties": { + + + "cut": { + "type": + "string", + "description": "Type: List of `integer`, multiple_sep: `\":\"`. Remove LEN bases from each read (or R1 if paired; use --cut_r2\noption for R2)", + "help_text": "Type: List of `integer`, multiple_sep: `\":\"`. Remove LEN bases from each read (or R1 if paired; use --cut_r2\noption for R2). If LEN is positive, remove bases from the\nbeginning. If LEN is negative, remove bases from the end.\nCan be used twice if LENs have different signs. Applied\n*before* adapter trimming.\n" + + } + + + , + "cut_r2": { + "type": + "string", + "description": "Type: List of `integer`, multiple_sep: `\":\"`. Remove LEN bases from each read (for R2)", + "help_text": "Type: List of `integer`, multiple_sep: `\":\"`. Remove LEN bases from each read (for R2). If LEN is positive, remove bases from the\nbeginning. If LEN is negative, remove bases from the end.\nCan be used twice if LENs have different signs. Applied\n*before* adapter trimming.\n" + + } + + + , + "nextseq_trim": { + "type": + "string", + "description": "Type: `string`. NextSeq-specific quality trimming (each read)", + "help_text": "Type: `string`. NextSeq-specific quality trimming (each read). Trims also\ndark cycles appearing as high-quality G bases.\n" + + } + + + , + "quality_cutoff": { + "type": + "string", + "description": "Type: `string`. Trim low-quality bases from 5\u0027 and/or 3\u0027 ends of each read\nbefore adapter removal", + "help_text": "Type: `string`. Trim low-quality bases from 5\u0027 and/or 3\u0027 ends of each read\nbefore adapter removal. Applied to both reads if data is\npaired. If one value is given, only the 3\u0027 end is trimmed.\nIf two comma-separated cutoffs are given, the 5\u0027 end is\ntrimmed with the first cutoff, the 3\u0027 end with the second.\n" + + } + + + , + "quality_cutoff_r2": { + "type": + "string", + "description": "Type: `string`. Quality-trimming cutoff for R2", + "help_text": "Type: `string`. Quality-trimming cutoff for R2. Default: same as for R1\n" + + } + + + , + "quality_base": { + "type": + "integer", + "description": "Type: `integer`, example: `33`. Assume that quality values in FASTQ are encoded as\nascii(quality + N)", + "help_text": "Type: `integer`, example: `33`. Assume that quality values in FASTQ are encoded as\nascii(quality + N). This needs to be set to 64 for some\nold Illumina FASTQ files. The default is 33.\n" + + } + + + , + "poly_a": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Trim poly-A tails", + "help_text": "Type: `boolean_true`, default: `false`. Trim poly-A tails" + , + "default": "False" + } + + + , + "length": { + "type": + "integer", + "description": "Type: `integer`. Shorten reads to LENGTH", + "help_text": "Type: `integer`. Shorten reads to LENGTH. Positive values remove bases at\nthe end while negative ones remove bases at the beginning.\nThis and the following modifications are applied after\nadapter trimming.\n" + + } + + + , + "trim_n": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Trim N\u0027s on ends of reads", + "help_text": "Type: `boolean_true`, default: `false`. Trim N\u0027s on ends of reads." + , + "default": "False" + } + + + , + "length_tag": { + "type": + "string", + "description": "Type: `string`, example: `length=`. Search for TAG followed by a decimal number in the\ndescription field of the read", + "help_text": "Type: `string`, example: `length=`. Search for TAG followed by a decimal number in the\ndescription field of the read. Replace the decimal number\nwith the correct length of the trimmed read. For example,\nuse --length-tag \u0027length=\u0027 to correct fields like\n\u0027length=123\u0027.\n" + + } + + + , + "strip_suffix": { + "type": + "string", + "description": "Type: `string`. Remove this suffix from read names if present", + "help_text": "Type: `string`. Remove this suffix from read names if present. Can be\ngiven multiple times.\n" + + } + + + , + "prefix": { + "type": + "string", + "description": "Type: `string`. Add this prefix to read names", + "help_text": "Type: `string`. Add this prefix to read names. Use {name} to insert the\nname of the matching adapter.\n" + + } + + + , + "suffix": { + "type": + "string", + "description": "Type: `string`. Add this suffix to read names; can also include {name}\n", + "help_text": "Type: `string`. Add this suffix to read names; can also include {name}\n" + + } + + + , + "rename": { + "type": + "string", + "description": "Type: `string`. Rename reads using TEMPLATE containing variables such as\n{id}, {adapter_name} etc", + "help_text": "Type: `string`. Rename reads using TEMPLATE containing variables such as\n{id}, {adapter_name} etc. (see documentation)\n" + + } + + + , + "zero_cap": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Change negative quality values to zero", + "help_text": "Type: `boolean_true`, default: `false`. Change negative quality values to zero." + , + "default": "False" + } + + +} +}, + + + "filtering of processed reads" : { + "title": "Filtering of processed reads", + "type": "object", + "description": "Filters are applied after above read modifications. Paired-end reads are\nalways discarded pairwise (see also --pair_filter).\n", + "properties": { + + + "minimum_length": { + "type": + "string", + "description": "Type: `string`, example: `0`. Discard reads shorter than LEN", + "help_text": "Type: `string`, example: `0`. Discard reads shorter than LEN. Default is 0.\nWhen trimming paired-end reads, the minimum lengths for R1 and R2 can be specified separately by separating them with a colon (:).\nIf the colon syntax is not used, the same minimum length applies to both reads, as discussed above.\nAlso, one of the values can be omitted to impose no restrictions.\nFor example, with -m 17:, the length of R1 must be at least 17, but the length of R2 is ignored.\n" + + } + + + , + "maximum_length": { + "type": + "string", + "description": "Type: `string`. Discard reads longer than LEN", + "help_text": "Type: `string`. Discard reads longer than LEN. Default: no limit.\nFor paired reads, see the remark for --minimum_length\n" + + } + + + , + "max_n": { + "type": + "string", + "description": "Type: `string`. Discard reads with more than COUNT \u0027N\u0027 bases", + "help_text": "Type: `string`. Discard reads with more than COUNT \u0027N\u0027 bases. If COUNT is\na number between 0 and 1, it is interpreted as a fraction\nof the read length.\n" + + } + + + , + "max_expected_errors": { + "type": + "string", + "description": "Type: `long`. Discard reads whose expected number of errors (computed\nfrom quality values) exceeds ERRORS", + "help_text": "Type: `long`. Discard reads whose expected number of errors (computed\nfrom quality values) exceeds ERRORS.\n" + + } + + + , + "max_average_error_rate": { + "type": + "string", + "description": "Type: `long`. as --max_expected_errors (see above), but divided by\nlength to account for reads of varying length", + "help_text": "Type: `long`. as --max_expected_errors (see above), but divided by\nlength to account for reads of varying length.\n" + + } + + + , + "discard_trimmed": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Discard reads that contain an adapter", + "help_text": "Type: `boolean_true`, default: `false`. Discard reads that contain an adapter. Use also -O to\navoid discarding too many randomly matching reads.\n" + , + "default": "False" + } + + + , + "discard_untrimmed": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Discard reads that do not contain an adapter", + "help_text": "Type: `boolean_true`, default: `false`. Discard reads that do not contain an adapter.\n" + , + "default": "False" + } + + + , + "discard_casava": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Discard reads that did not pass CASAVA filtering (header\nhas :Y:)", + "help_text": "Type: `boolean_true`, default: `false`. Discard reads that did not pass CASAVA filtering (header\nhas :Y:).\n" + , + "default": "False" + } + + +} +}, + + + "output parameters" : { + "title": "Output parameters", + "type": "object", + "description": "No description", + "properties": { + + + "report": { + "type": + "string", + "description": "Type: `string`, example: `full`, choices: ``full`, `minimal``. Which type of report to print: \u0027full\u0027 (default) or \u0027minimal\u0027", + "help_text": "Type: `string`, example: `full`, choices: ``full`, `minimal``. Which type of report to print: \u0027full\u0027 (default) or \u0027minimal\u0027.\n", + "enum": ["full", "minimal"] + + + } + + + , + "json": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Write report in JSON format to this file", + "help_text": "Type: `boolean_true`, default: `false`. Write report in JSON format to this file.\n" + , + "default": "False" + } + + + , + "output": { + "type": + "string", + "description": "Type: List of `file`, required, default: `$id.$key.output_*.fast[a,q]`, example: `fastq/*_001.fast[a,q]`, multiple_sep: `\":\"`. Glob pattern for matching the expected output files", + "help_text": "Type: List of `file`, required, default: `$id.$key.output_*.fast[a,q]`, example: `fastq/*_001.fast[a,q]`, multiple_sep: `\":\"`. Glob pattern for matching the expected output files.\nShould include `$output_dir`.\n" + , + "default": "$id.$key.output_*.fast[a,q]" + } + + + , + "fasta": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output FASTA to standard output even on FASTQ input", + "help_text": "Type: `boolean_true`, default: `false`. Output FASTA to standard output even on FASTQ input.\n" + , + "default": "False" + } + + + , + "info_file": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Write information about each read and its adapter matches\ninto info", + "help_text": "Type: `boolean_true`, default: `false`. Write information about each read and its adapter matches\ninto info.txt in the output directory.\nSee the documentation for the file format.\n" + , + "default": "False" + } + + +} +}, + + + "debug" : { + "title": "Debug", + "type": "object", + "description": "No description", + "properties": { + + + "debug": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Print debug information", + "help_text": "Type: `boolean_true`, default: `false`. Print debug information" + , + "default": "False" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/specify adapters for r1" + }, + + { + "$ref": "#/definitions/specify adapters using fasta files for r1" + }, + + { + "$ref": "#/definitions/specify adapters for r2" + }, + + { + "$ref": "#/definitions/specify adapters using fasta files for r2" + }, + + { + "$ref": "#/definitions/paired-end options" + }, + + { + "$ref": "#/definitions/input parameters" + }, + + { + "$ref": "#/definitions/read modifications" + }, + + { + "$ref": "#/definitions/filtering of processed reads" + }, + + { + "$ref": "#/definitions/output parameters" + }, + + { + "$ref": "#/definitions/debug" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/falco/.config.vsh.yaml b/target/nextflow/falco/.config.vsh.yaml new file mode 100644 index 00000000..5bab198b --- /dev/null +++ b/target/nextflow/falco/.config.vsh.yaml @@ -0,0 +1,330 @@ +name: "falco" +version: "main" +argument_groups: +- name: "Input arguments" + arguments: + - type: "file" + name: "--input" + description: "input fastq files" + info: null + example: + - "input1.fastq;input2.fastq" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Run arguments" + arguments: + - type: "boolean_true" + name: "--nogroup" + description: "Disable grouping of bases for reads >50bp. \nAll reports will show\ + \ data for every base in \nthe read. WARNING: When using this option, \nyour\ + \ plots may end up a ridiculous size. You \nhave been warned!\n" + info: null + direction: "input" + - type: "file" + name: "--contaminents" + description: "Specifies a non-default file which contains \nthe list of contaminants\ + \ to screen \noverrepresented sequences against. The file \nmust contain sets\ + \ of named contaminants in \nthe form name[tab]sequence. Lines prefixed \nwith\ + \ a hash will be ignored. Default: \nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/contaminant_list.txt\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--adapters" + description: "Specifies a non-default file which contains \nthe list of adapter\ + \ sequences which will be \nexplicity searched against the library. The \nfile\ + \ must contain sets of named adapters in \nthe form name[tab]sequence. Lines\ + \ prefixed \nwith a hash will be ignored. Default:\nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/adapter_list.txt\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--limits" + description: "Specifies a non-default file which contains \na set of criteria\ + \ which will be used to \ndetermine the warn/error limits for the \nvarious\ + \ modules. This file can also be used \nto selectively remove some modules from\ + \ the \noutput all together. The format needs to \nmirror the default limits.txt\ + \ file found in \nthe Configuration folder. Default: \nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/limits.txt\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--subsample" + alternatives: + - "-s" + description: "[Falco only] makes falco faster (but \npossibly less accurate) by\ + \ only processing \nreads that are a multiple of this value (using \n0-based\ + \ indexing to number reads).\n" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--bisulfite" + alternatives: + - "-b" + description: "[Falco only] reads are whole genome \nbisulfite sequencing, and\ + \ more Ts and fewer \nCs are therefore expected and will be \naccounted for\ + \ in base content.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--reverse_complliment" + alternatives: + - "-r" + description: "[Falco only] The input is a \nreverse-complement. All modules will\ + \ be \ntested by swapping A/T and C/G\n" + info: null + direction: "input" +- name: "Output arguments" + arguments: + - type: "file" + name: "--outdir" + alternatives: + - "-o" + description: "Create all output files in the specified \noutput directory. FALCO-SPECIFIC:\ + \ If the \ndirectory does not exists, the program will \ncreate it.\n" + info: null + example: + - "output" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--format" + alternatives: + - "-f" + description: "Bypasses the normal sequence file format \ndetection and forces\ + \ the program to use the \nspecified format. Validformats are bam, sam, \nbam_mapped,\ + \ sam_mapped, fastq, fq, fastq.gz \nor fq.gz.\n" + info: null + required: false + choices: + - "bam" + - "sam" + - "bam_mapped" + - "sam_mapped" + - "fastq" + - "fq" + - "fastq.gz" + - "fq.gz" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--data_filename" + alternatives: + - "-D" + description: "[Falco only] Specify filename for FastQC \ndata output (TXT). If\ + \ not specified, it will \nbe called fastq_data.txt in either the input \nfile's\ + \ directory or the one specified in the \n--output flag. Only available when\ + \ running \nfalco with a single input.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--report_filename" + alternatives: + - "-R" + description: "[Falco only] Specify filename for FastQC \nreport output (HTML).\ + \ If not specified, it \nwill be called fastq_report.html in either \nthe input\ + \ file's directory or the one \nspecified in the --output flag. Only \navailable\ + \ when running falco with a single \ninput.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--summary_filename" + alternatives: + - "-S" + description: "[Falco only] Specify filename for the short \nsummary output (TXT).\ + \ If not specified, it \nwill be called fastq_report.html in either \nthe input\ + \ file's directory or the one \nspecified in the --output flag. Only \navailable\ + \ when running falco with a single \ninput.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "A C++ drop-in replacement of FastQC to assess the quality of sequence\ + \ read data" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "qc" +- "fastqc" +- "sequencing" +license: "GPL-3.0" +references: + doi: + - "10.12688/f1000research.21142.2" +links: + repository: "https://github.com/smithlabcode/falco" + documentation: "https://falco.readthedocs.io/en/latest/" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "debian:trixie-slim" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "apt" + packages: + - "wget" + - "build-essential" + - "g++" + - "zlib1g-dev" + - "procps" + interactive: false + - type: "docker" + run: + - "wget https://github.com/smithlabcode/falco/releases/download/v1.2.2/falco-1.2.2.tar.gz\ + \ -O /tmp/falco.tar.gz && \\\ncd /tmp && \\\ntar xvf falco.tar.gz && \\\ncd\ + \ falco-1.2.2 && \\\n./configure && \\\nmake all && \\\nmake install\n" + - type: "docker" + run: + - "echo \"falco: \\\"$(falco -v | sed -n 's/^falco //p')\\\"\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/falco/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/falco" + executable: "target/nextflow/falco/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/falco/main.nf b/target/nextflow/falco/main.nf new file mode 100644 index 00000000..6ca5a5a4 --- /dev/null +++ b/target/nextflow/falco/main.nf @@ -0,0 +1,3696 @@ +// falco main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "falco", + "version" : "main", + "argument_groups" : [ + { + "name" : "Input arguments", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "input fastq files", + "example" : [ + "input1.fastq;input2.fastq" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Run arguments", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--nogroup", + "description" : "Disable grouping of bases for reads >50bp. \nAll reports will show data for every base in \nthe read. WARNING: When using this option, \nyour plots may end up a ridiculous size. You \nhave been warned!\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--contaminents", + "description" : "Specifies a non-default file which contains \nthe list of contaminants to screen \noverrepresented sequences against. The file \nmust contain sets of named contaminants in \nthe form name[tab]sequence. Lines prefixed \nwith a hash will be ignored. Default: \nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/contaminant_list.txt\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--adapters", + "description" : "Specifies a non-default file which contains \nthe list of adapter sequences which will be \nexplicity searched against the library. The \nfile must contain sets of named adapters in \nthe form name[tab]sequence. Lines prefixed \nwith a hash will be ignored. Default:\nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/adapter_list.txt\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--limits", + "description" : "Specifies a non-default file which contains \na set of criteria which will be used to \ndetermine the warn/error limits for the \nvarious modules. This file can also be used \nto selectively remove some modules from the \noutput all together. The format needs to \nmirror the default limits.txt file found in \nthe Configuration folder. Default: \nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/limits.txt\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--subsample", + "alternatives" : [ + "-s" + ], + "description" : "[Falco only] makes falco faster (but \npossibly less accurate) by only processing \nreads that are a multiple of this value (using \n0-based indexing to number reads).\n", + "example" : [ + 10 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--bisulfite", + "alternatives" : [ + "-b" + ], + "description" : "[Falco only] reads are whole genome \nbisulfite sequencing, and more Ts and fewer \nCs are therefore expected and will be \naccounted for in base content.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--reverse_complliment", + "alternatives" : [ + "-r" + ], + "description" : "[Falco only] The input is a \nreverse-complement. All modules will be \ntested by swapping A/T and C/G\n", + "direction" : "input" + } + ] + }, + { + "name" : "Output arguments", + "arguments" : [ + { + "type" : "file", + "name" : "--outdir", + "alternatives" : [ + "-o" + ], + "description" : "Create all output files in the specified \noutput directory. FALCO-SPECIFIC: If the \ndirectory does not exists, the program will \ncreate it.\n", + "example" : [ + "output" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--format", + "alternatives" : [ + "-f" + ], + "description" : "Bypasses the normal sequence file format \ndetection and forces the program to use the \nspecified format. Validformats are bam, sam, \nbam_mapped, sam_mapped, fastq, fq, fastq.gz \nor fq.gz.\n", + "required" : false, + "choices" : [ + "bam", + "sam", + "bam_mapped", + "sam_mapped", + "fastq", + "fq", + "fastq.gz", + "fq.gz" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--data_filename", + "alternatives" : [ + "-D" + ], + "description" : "[Falco only] Specify filename for FastQC \ndata output (TXT). If not specified, it will \nbe called fastq_data.txt in either the input \nfile's directory or the one specified in the \n--output flag. Only available when running \nfalco with a single input.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--report_filename", + "alternatives" : [ + "-R" + ], + "description" : "[Falco only] Specify filename for FastQC \nreport output (HTML). If not specified, it \nwill be called fastq_report.html in either \nthe input file's directory or the one \nspecified in the --output flag. Only \navailable when running falco with a single \ninput.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--summary_filename", + "alternatives" : [ + "-S" + ], + "description" : "[Falco only] Specify filename for the short \nsummary output (TXT). If not specified, it \nwill be called fastq_report.html in either \nthe input file's directory or the one \nspecified in the --output flag. Only \navailable when running falco with a single \ninput.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "A C++ drop-in replacement of FastQC to assess the quality of sequence read data", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "qc", + "fastqc", + "sequencing" + ], + "license" : "GPL-3.0", + "references" : { + "doi" : [ + "10.12688/f1000research.21142.2" + ] + }, + "links" : { + "repository" : "https://github.com/smithlabcode/falco", + "documentation" : "https://falco.readthedocs.io/en/latest/" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "debian:trixie-slim", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "apt", + "packages" : [ + "wget", + "build-essential", + "g++", + "zlib1g-dev", + "procps" + ], + "interactive" : false + }, + { + "type" : "docker", + "run" : [ + "wget https://github.com/smithlabcode/falco/releases/download/v1.2.2/falco-1.2.2.tar.gz -O /tmp/falco.tar.gz && \\\\\ncd /tmp && \\\\\ntar xvf falco.tar.gz && \\\\\ncd falco-1.2.2 && \\\\\n./configure && \\\\\nmake all && \\\\\nmake install\n" + ] + }, + { + "type" : "docker", + "run" : [ + "echo \\"falco: \\\\\\"$(falco -v | sed -n 's/^falco //p')\\\\\\"\\" > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/falco/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/falco", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_NOGROUP+x} ]; then echo "${VIASH_PAR_NOGROUP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_nogroup='&'#" ; else echo "# par_nogroup="; fi ) +$( if [ ! -z ${VIASH_PAR_CONTAMINENTS+x} ]; then echo "${VIASH_PAR_CONTAMINENTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_contaminents='&'#" ; else echo "# par_contaminents="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTERS+x} ]; then echo "${VIASH_PAR_ADAPTERS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_adapters='&'#" ; else echo "# par_adapters="; fi ) +$( if [ ! -z ${VIASH_PAR_LIMITS+x} ]; then echo "${VIASH_PAR_LIMITS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_limits='&'#" ; else echo "# par_limits="; fi ) +$( if [ ! -z ${VIASH_PAR_SUBSAMPLE+x} ]; then echo "${VIASH_PAR_SUBSAMPLE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_subsample='&'#" ; else echo "# par_subsample="; fi ) +$( if [ ! -z ${VIASH_PAR_BISULFITE+x} ]; then echo "${VIASH_PAR_BISULFITE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bisulfite='&'#" ; else echo "# par_bisulfite="; fi ) +$( if [ ! -z ${VIASH_PAR_REVERSE_COMPLLIMENT+x} ]; then echo "${VIASH_PAR_REVERSE_COMPLLIMENT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_reverse_complliment='&'#" ; else echo "# par_reverse_complliment="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTDIR+x} ]; then echo "${VIASH_PAR_OUTDIR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_outdir='&'#" ; else echo "# par_outdir="; fi ) +$( if [ ! -z ${VIASH_PAR_FORMAT+x} ]; then echo "${VIASH_PAR_FORMAT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_format='&'#" ; else echo "# par_format="; fi ) +$( if [ ! -z ${VIASH_PAR_DATA_FILENAME+x} ]; then echo "${VIASH_PAR_DATA_FILENAME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_data_filename='&'#" ; else echo "# par_data_filename="; fi ) +$( if [ ! -z ${VIASH_PAR_REPORT_FILENAME+x} ]; then echo "${VIASH_PAR_REPORT_FILENAME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_report_filename='&'#" ; else echo "# par_report_filename="; fi ) +$( if [ ! -z ${VIASH_PAR_SUMMARY_FILENAME+x} ]; then echo "${VIASH_PAR_SUMMARY_FILENAME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_summary_filename='&'#" ; else echo "# par_summary_filename="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END +#!/bin/bash + +set -eo pipefail + +[[ "\\$par_nogroup" == "false" ]] && unset par_nogroup +[[ "\\$par_bisulfite" == "false" ]] && unset par_bisulfite +[[ "\\$par_reverse_compliment" == "false" ]] && unset par_reverse_compliment + +IFS=";" read -ra input <<< \\$par_input + +\\$(which falco) \\\\ + \\${par_nogroup:+--nogroup} \\\\ + \\${par_contaminants:+--contaminants "\\$par_contaminants"} \\\\ + \\${par_adapters:+--adapters "\\$par_adapters"} \\\\ + \\${par_limits:+--limits "\\$par_limits"} \\\\ + \\${par_subsample:+-subsample \\$par_subsample} \\\\ + \\${par_bisulfite:+-bisulfite} \\\\ + \\${par_reverse_compliment:+-reverse-compliment} \\\\ + \\${par_outdir:+--outdir "\\$par_outdir"} \\\\ + \\${par_format:+--format "\\$par_format"} \\\\ + \\${par_data_filename:+-data-filename "\\$par_data_filename"} \\\\ + \\${par_report_filename:+-report-filename "\\$par_report_filename"} \\\\ + \\${par_summary_filename:+-summary-filename "\\$par_summary_filename"} \\\\ + \\${input[*]} +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/falco", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/falco/nextflow.config b/target/nextflow/falco/nextflow.config new file mode 100644 index 00000000..db77af38 --- /dev/null +++ b/target/nextflow/falco/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'falco' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'A C++ drop-in replacement of FastQC to assess the quality of sequence read data' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/falco/nextflow_schema.json b/target/nextflow/falco/nextflow_schema.json new file mode 100644 index 00000000..71b5872f --- /dev/null +++ b/target/nextflow/falco/nextflow_schema.json @@ -0,0 +1,227 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "falco", +"description": "A C++ drop-in replacement of FastQC to assess the quality of sequence read data", +"type": "object", +"definitions": { + + + + "input arguments" : { + "title": "Input arguments", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: List of `file`, required, example: `input1.fastq;input2.fastq`, multiple_sep: `\":\"`. input fastq files", + "help_text": "Type: List of `file`, required, example: `input1.fastq;input2.fastq`, multiple_sep: `\":\"`. input fastq files" + + } + + +} +}, + + + "run arguments" : { + "title": "Run arguments", + "type": "object", + "description": "No description", + "properties": { + + + "nogroup": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Disable grouping of bases for reads \u003e50bp", + "help_text": "Type: `boolean_true`, default: `false`. Disable grouping of bases for reads \u003e50bp. \nAll reports will show data for every base in \nthe read. WARNING: When using this option, \nyour plots may end up a ridiculous size. You \nhave been warned!\n" + , + "default": "False" + } + + + , + "contaminents": { + "type": + "string", + "description": "Type: `file`. Specifies a non-default file which contains \nthe list of contaminants to screen \noverrepresented sequences against", + "help_text": "Type: `file`. Specifies a non-default file which contains \nthe list of contaminants to screen \noverrepresented sequences against. The file \nmust contain sets of named contaminants in \nthe form name[tab]sequence. Lines prefixed \nwith a hash will be ignored. Default: \nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/contaminant_list.txt\n" + + } + + + , + "adapters": { + "type": + "string", + "description": "Type: `file`. Specifies a non-default file which contains \nthe list of adapter sequences which will be \nexplicity searched against the library", + "help_text": "Type: `file`. Specifies a non-default file which contains \nthe list of adapter sequences which will be \nexplicity searched against the library. The \nfile must contain sets of named adapters in \nthe form name[tab]sequence. Lines prefixed \nwith a hash will be ignored. Default:\nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/adapter_list.txt\n" + + } + + + , + "limits": { + "type": + "string", + "description": "Type: `file`. Specifies a non-default file which contains \na set of criteria which will be used to \ndetermine the warn/error limits for the \nvarious modules", + "help_text": "Type: `file`. Specifies a non-default file which contains \na set of criteria which will be used to \ndetermine the warn/error limits for the \nvarious modules. This file can also be used \nto selectively remove some modules from the \noutput all together. The format needs to \nmirror the default limits.txt file found in \nthe Configuration folder. Default: \nhttps://github.com/smithlabcode/falco/blob/v1.2.2/Configuration/limits.txt\n" + + } + + + , + "subsample": { + "type": + "integer", + "description": "Type: `integer`, example: `10`. [Falco only] makes falco faster (but \npossibly less accurate) by only processing \nreads that are a multiple of this value (using \n0-based indexing to number reads)", + "help_text": "Type: `integer`, example: `10`. [Falco only] makes falco faster (but \npossibly less accurate) by only processing \nreads that are a multiple of this value (using \n0-based indexing to number reads).\n" + + } + + + , + "bisulfite": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [Falco only] reads are whole genome \nbisulfite sequencing, and more Ts and fewer \nCs are therefore expected and will be \naccounted for in base content", + "help_text": "Type: `boolean_true`, default: `false`. [Falco only] reads are whole genome \nbisulfite sequencing, and more Ts and fewer \nCs are therefore expected and will be \naccounted for in base content.\n" + , + "default": "False" + } + + + , + "reverse_complliment": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [Falco only] The input is a \nreverse-complement", + "help_text": "Type: `boolean_true`, default: `false`. [Falco only] The input is a \nreverse-complement. All modules will be \ntested by swapping A/T and C/G\n" + , + "default": "False" + } + + +} +}, + + + "output arguments" : { + "title": "Output arguments", + "type": "object", + "description": "No description", + "properties": { + + + "outdir": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.outdir.outdir`, example: `output`. Create all output files in the specified \noutput directory", + "help_text": "Type: `file`, required, default: `$id.$key.outdir.outdir`, example: `output`. Create all output files in the specified \noutput directory. FALCO-SPECIFIC: If the \ndirectory does not exists, the program will \ncreate it.\n" + , + "default": "$id.$key.outdir.outdir" + } + + + , + "format": { + "type": + "string", + "description": "Type: `string`, choices: ``bam`, `sam`, `bam_mapped`, `sam_mapped`, `fastq`, `fq`, `fastq.gz`, `fq.gz``. Bypasses the normal sequence file format \ndetection and forces the program to use the \nspecified format", + "help_text": "Type: `string`, choices: ``bam`, `sam`, `bam_mapped`, `sam_mapped`, `fastq`, `fq`, `fastq.gz`, `fq.gz``. Bypasses the normal sequence file format \ndetection and forces the program to use the \nspecified format. Validformats are bam, sam, \nbam_mapped, sam_mapped, fastq, fq, fastq.gz \nor fq.gz.\n", + "enum": ["bam", "sam", "bam_mapped", "sam_mapped", "fastq", "fq", "fastq.gz", "fq.gz"] + + + } + + + , + "data_filename": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.data_filename.data_filename`. [Falco only] Specify filename for FastQC \ndata output (TXT)", + "help_text": "Type: `file`, default: `$id.$key.data_filename.data_filename`. [Falco only] Specify filename for FastQC \ndata output (TXT). If not specified, it will \nbe called fastq_data.txt in either the input \nfile\u0027s directory or the one specified in the \n--output flag. Only available when running \nfalco with a single input.\n" + , + "default": "$id.$key.data_filename.data_filename" + } + + + , + "report_filename": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.report_filename.report_filename`. [Falco only] Specify filename for FastQC \nreport output (HTML)", + "help_text": "Type: `file`, default: `$id.$key.report_filename.report_filename`. [Falco only] Specify filename for FastQC \nreport output (HTML). If not specified, it \nwill be called fastq_report.html in either \nthe input file\u0027s directory or the one \nspecified in the --output flag. Only \navailable when running falco with a single \ninput.\n" + , + "default": "$id.$key.report_filename.report_filename" + } + + + , + "summary_filename": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.summary_filename.summary_filename`. [Falco only] Specify filename for the short \nsummary output (TXT)", + "help_text": "Type: `file`, default: `$id.$key.summary_filename.summary_filename`. [Falco only] Specify filename for the short \nsummary output (TXT). If not specified, it \nwill be called fastq_report.html in either \nthe input file\u0027s directory or the one \nspecified in the --output flag. Only \navailable when running falco with a single \ninput.\n" + , + "default": "$id.$key.summary_filename.summary_filename" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/input arguments" + }, + + { + "$ref": "#/definitions/run arguments" + }, + + { + "$ref": "#/definitions/output arguments" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/fastp/.config.vsh.yaml b/target/nextflow/fastp/.config.vsh.yaml new file mode 100644 index 00000000..bee26ba8 --- /dev/null +++ b/target/nextflow/fastp/.config.vsh.yaml @@ -0,0 +1,1091 @@ +name: "fastp" +version: "main" +argument_groups: +- name: "Inputs" + description: "`fastp` supports both single-end (SE) and paired-end (PE) input.\n\ + \n- for SE data, you only have to specify read1 input by `-i` or `--in1`.\n- for\ + \ PE data, you should also specify read2 input by `-I` or `--in2`.\n" + arguments: + - type: "file" + name: "--in1" + alternatives: + - "-i" + description: "Input FastQ file. Must be single-end or paired-end R1. Can be gzipped." + info: null + example: + - "in.R1.fq.gz" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--in2" + alternatives: + - "-I" + description: "Input FastQ file. Must be paired-end R2. Can be gzipped." + info: null + example: + - "in.R2.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + description: "\n- for SE data, you only have to specify read1 output by `-o` or\ + \ `--out1`.\n- for PE data, you should also specify read2 output by `-O` or `--out2`.\n\ + - if you don't specify the output file names, no output files will be written,\ + \ but the QC will still be done for both data before and after filtering.\n- the\ + \ output will be gzip-compressed if its file name ends with `.gz`\n" + arguments: + - type: "file" + name: "--out1" + alternatives: + - "-o" + description: "The single-end or paired-end R1 reads that pass QC. Will be gzipped\ + \ if its file name ends with `.gz`." + info: null + example: + - "out.R1.fq.gz" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--out2" + alternatives: + - "-O" + description: "The paired-end R2 reads that pass QC. Will be gzipped if its file\ + \ name ends with `.gz`." + info: null + example: + - "out.R2.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unpaired1" + description: "Store the reads that `read1` passes filters but its paired `read2`\ + \ doesn't." + info: null + example: + - "unpaired.R1.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unpaired2" + description: "Store the reads that `read2` passes filters but its paired `read1`\ + \ doesn't." + info: null + example: + - "unpaired.R2.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--failed_out" + description: "Store the reads that fail filters.\n\nIf one read failed and is\ + \ written to --failed_out, its failure reason will be appended to its read name.\ + \ For example, failed_quality_filter, failed_too_short etc.\nFor PE data, if\ + \ unpaired reads are not stored (by giving --unpaired1 or --unpaired2), the\ + \ failed pair of reads will be put together. If one read passes the filters\ + \ but its pair doesn't, the failure reason will be paired_read_is_failing.\n" + info: null + example: + - "failed.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--overlapped_out" + description: "For each read pair, output the overlapped region if it has no any\ + \ mismatched base.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Report output arguments" + arguments: + - type: "file" + name: "--json" + alternatives: + - "-j" + description: "The json format report file name\n" + info: null + example: + - "out.json" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--html" + description: "The html format report file name\n" + info: null + example: + - "out.html" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--report_title" + description: "The title of the html report, default is \"fastp report\".\n" + info: null + example: + - "fastp report" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Adapter trimming" + description: "Adapter trimming is enabled by default, but you can disable it by\ + \ `-A` or `--disable_adapter_trimming`. Adapter sequences can be automatically\ + \ detected for both PE/SE data.\n\n- For SE data, the adapters are evaluated by\ + \ analyzing the tails of first ~1M reads. This evaluation may be inacurrate, and\ + \ you can specify the adapter sequence by `-a` or `--adapter_sequence` option.\ + \ If adapter sequence is specified, the auto detection for SE data will be disabled.\n\ + - For PE data, the adapters can be detected by per-read overlap analysis, which\ + \ seeks for the overlap of each pair of reads. This method is robust and fast,\ + \ so normally you don't have to input the adapter sequence even you know it. But\ + \ you can still specify the adapter sequences for read1 by `--adapter_sequence`,\ + \ and for read2 by `--adapter_sequence_r2`. If `fastp` fails to find an overlap\ + \ (i.e. due to low quality bases), it will use these sequences to trim adapters\ + \ for read1 and read2 respectively.\n- For PE data, the adapter sequence auto-detection\ + \ is disabled by default since the adapters can be trimmed by overlap analysis.\ + \ However, you can specify `--detect_adapter_for_pe` to enable it.\n- For PE data,\ + \ `fastp` will run a little slower if you specify the sequence adapters or enable\ + \ adapter auto-detection, but usually result in a slightly cleaner output, since\ + \ the overlap analysis may fail due to sequencing errors or adapter dimers.\n\ + - The most widely used adapter is the Illumina TruSeq adapters. If your data is\ + \ from the TruSeq library, you can add `--adapter_sequence=AGATCGGAAGAGCACACGTCTGAACTCCAGTCA\ + \ --adapter_sequence_r2=AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT` to your command lines,\ + \ or enable auto detection for PE data by specifing `detect_adapter_for_pe`.\n\ + - `fastp` contains some built-in known adapter sequences for better auto-detection.\ + \ If you want to make some adapters to be a part of the built-in adapters, please\ + \ file an issue.\n\nYou can also specify --adapter_fasta to give a FASTA file\ + \ to tell fastp to trim multiple adapters in this FASTA file. Here is a sample\ + \ of such adapter FASTA file:\n\n```\n>Illumina TruSeq Adapter Read 1\nAGATCGGAAGAGCACACGTCTGAACTCCAGTCA\n\ + >Illumina TruSeq Adapter Read 2\nAGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT\n>polyA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n\ + ```\n\nThe adapter sequence in this file should be at least 6bp long, otherwise\ + \ it will be skipped. And you can give whatever you want to trim, rather than\ + \ regular sequencing adapters (i.e. polyA).\n\n`fastp` first trims the auto-detected\ + \ adapter or the adapter sequences given by `--adapter_sequence | --adapter_sequence_r2`,\ + \ then trims the adapters given by `--adapter_fasta` one by one.\n\nThe sequence\ + \ distribution of trimmed adapters can be found at the HTML/JSON reports.\n" + arguments: + - type: "boolean_true" + name: "--disable_adapter_trimming" + alternatives: + - "-A" + description: "Disable adapter trimming.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--detect_adapter_for_pe" + description: "By default, the auto-detection for adapter is for SE data input\ + \ only, turn on this option to enable it for PE data.\n" + info: null + direction: "input" + - type: "string" + name: "--adapter_sequence" + alternatives: + - "-a" + description: "The adapter sequences to be trimmed. For SE data, if not specified,\ + \ the adapters will be auto-detected. For PE data, this is used if R1/R2 are\ + \ found not overlapped\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--adapter_sequence_r2" + description: "The adapter sequences to be trimmed for R2. This is used for PE\ + \ data if R1/R2 are found overlapped.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--adapter_fasta" + description: "A FASTA file containing all the adapter sequences to be trimmed.\ + \ For SE data, if not specified, the adapters will be auto-detected. For PE\ + \ data, this is used if R1/R2 are found not overlapped.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Base trimming" + arguments: + - type: "integer" + name: "--trim_front1" + alternatives: + - "-f" + description: "Trimming how many bases in front for read1, default is 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--trim_tail1" + alternatives: + - "-t" + description: "Trimming how many bases in tail for read1, default is 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_len1" + alternatives: + - "-b" + description: "If read1 is longer than max_len1, then trim read1 at its tail to\ + \ make it as long as max_len1. Default 0 means no limitation.\n" + info: null + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--trim_front2" + alternatives: + - "-F" + description: "Trimming how many bases in front for read2, default is 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--trim_tail2" + alternatives: + - "-T" + description: "Trimming how many bases in tail for read2, default is 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_len2" + alternatives: + - "-B" + description: "If read2 is longer than max_len2, then trim read2 at its tail to\ + \ make it as long as max_len2. Default 0 means no limitation.\n" + info: null + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Merging mode" + description: "Allows merging paired-end reads into a single longer read if they\ + \ are overlapping." + arguments: + - type: "boolean_true" + name: "--merge" + alternatives: + - "-m" + description: "For paired-end input, merge each pair of reads into a single read\ + \ if they are overlapped. The merged reads will be written to the file given\ + \ by --merged_out, the unmerged reads will be written to the files specified\ + \ by --out1 and --out2. The merging mode is disabled by default.\n" + info: null + direction: "input" + - type: "file" + name: "--merged_out" + description: "In the merging mode, specify the file name to store merged output,\ + \ or specify --stdout to stream the merged output.\n" + info: null + example: + - "merged.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--include_unmerged" + description: "In the merging mode, write the unmerged or unpaired reads to the\ + \ file specified by --merge. Disabled by default.\n" + info: null + direction: "input" +- name: "Additional input arguments" + description: "Affects how the input is read." + arguments: + - type: "boolean_true" + name: "--interleaved_in" + description: "Indicate that is an interleaved FASTQ which contains both\ + \ read1 and read2. Disabled by default.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--fix_mgi_id" + description: "The MGI FASTQ ID format is not compatible with many BAM operation\ + \ tools, enable this option to fix it.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--phred64" + alternatives: + - "-6" + description: "Indicate the input is using phred64 scoring (it'll be converted\ + \ to phred33, so the output will still be phred33)\n" + info: null + direction: "input" +- name: "Additional output arguments" + description: "Affects how the output is written." + arguments: + - type: "integer" + name: "--compression" + alternatives: + - "-z" + description: "Compression level for gzip output (1 ~ 9). 1 is fastest, 9 is smallest,\ + \ default is 4.\n" + info: null + example: + - 4 + required: false + min: 1 + max: 9 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--dont_overwrite" + description: "Don't overwrite existing files. Overwritting is allowed by default.\n" + info: null + direction: "input" +- name: "Logging arguments" + arguments: + - type: "boolean_true" + name: "--verbose" + alternatives: + - "-V" + description: "Output verbose log information (i.e. when every 1M reads are processed)." + info: null + direction: "input" +- name: "Processing arguments" + arguments: + - type: "long" + name: "--reads_to_process" + description: "Specify how many reads/pairs to be processed. Default 0 means process\ + \ all reads.\n" + info: null + example: + - 1000000 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Deduplication arguments" + arguments: + - type: "boolean_true" + name: "--dedup" + description: "Enable deduplication to drop the duplicated reads/pairs\n" + info: null + direction: "input" + - type: "integer" + name: "--dup_calc_accuracy" + description: "Accuracy level to calculate duplication (1~6). Higher level uses\ + \ more memory (1G, 2G, 4G, 8G, 16G, 24G). Default 1 for no-dedup mode, and 3\ + \ for dedup mode.\n" + info: null + example: + - 3 + required: false + min: 1 + max: 6 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--dont_eval_duplication" + description: "Don't evaluate duplication rate to save time and use less memory.\n" + info: null + direction: "input" +- name: "PolyG tail trimming arguments" + arguments: + - type: "boolean_true" + name: "--trim_poly_g" + alternatives: + - "-g" + description: "Force polyG tail trimming, by default trimming is automatically\ + \ enabled for Illumina NextSeq/NovaSeq data\n" + info: null + direction: "input" + - type: "integer" + name: "--poly_g_min_len" + description: "The minimum length to detect polyG in the read tail. 10 by default.\n" + info: null + example: + - 10 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--disable_trim_poly_g" + alternatives: + - "-G" + description: "Disable polyG tail trimming, by default trimming is automatically\ + \ enabled for Illumina NextSeq/NovaSeq data\n" + info: null + direction: "input" +- name: "PolyX tail trimming arguments" + arguments: + - type: "boolean_true" + name: "--trim_poly_x" + alternatives: + - "-x" + description: "Enable polyX trimming in 3' ends.\n" + info: null + direction: "input" + - type: "integer" + name: "--poly_x_min_len" + description: "The minimum length to detect polyX in the read tail. 10 by default.\n" + info: null + example: + - 10 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Cut arguments" + arguments: + - type: "integer" + name: "--cut_front" + alternatives: + - "-5" + description: "Move a sliding window from front (5') to tail, drop the bases in\ + \ the window if its mean quality < threshold, stop otherwise.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_tail" + alternatives: + - "-3" + description: "Move a sliding window from tail (3') to front, drop the bases in\ + \ the window if its mean quality < threshold, stop otherwise.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_right" + alternatives: + - "-r" + description: "Move a sliding window from front to tail, if meet one window with\ + \ mean quality < threshold, drop the bases in the window and the right part,\ + \ and then stop.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_window_size" + alternatives: + - "-W" + description: "The window size option shared by cut_front, cut_tail or cut_sliding.\ + \ Range: 1~1000, default: 4.\n" + info: null + example: + - 4 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_mean_quality" + alternatives: + - "-M" + description: "The mean quality requirement option shared by cut_front, cut_tail\ + \ or cut_sliding. Range: 1~36 default: 20 (Q20)\n" + info: null + example: + - 20 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_front_window_size" + description: "The window size option of cut_front, default to cut_window_size\ + \ if not specified.\n" + info: null + example: + - 4 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_front_mean_quality" + description: "The mean quality requirement option of cut_front, default to cut_mean_quality\ + \ if not specified.\n" + info: null + example: + - 20 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_tail_window_size" + description: "The window size option of cut_tail, default to cut_window_size if\ + \ not specified.\n" + info: null + example: + - 4 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_tail_mean_quality" + description: "The mean quality requirement option of cut_tail, default to cut_mean_quality\ + \ if not specified.\n" + info: null + example: + - 20 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_right_window_size" + description: "The window size option of cut_right, default to cut_window_size\ + \ if not specified.\n" + info: null + example: + - 4 + required: false + min: 1 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cut_right_mean_quality" + description: "The mean quality requirement option of cut_right, default to cut_mean_quality\ + \ if not specified.\n" + info: null + example: + - 20 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Quality filtering arguments" + arguments: + - type: "boolean_true" + name: "--disable_quality_filtering" + alternatives: + - "-Q" + description: "Quality filtering is enabled by default. If this option is specified,\ + \ quality filtering is disabled.\n" + info: null + direction: "input" + - type: "integer" + name: "--qualified_quality_phred" + alternatives: + - "-q" + description: "The quality value that a base is qualified. Default 15 means phred\ + \ quality >=Q15 is qualified.\n" + info: null + example: + - 15 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--unqualified_percent_limit" + alternatives: + - "-u" + description: "How many percents of bases are allowed to be unqualified (0~100).\ + \ Default 40 means 40%.\n" + info: null + example: + - 40 + required: false + min: 0 + max: 100 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--n_base_limit" + alternatives: + - "-n" + description: "If one read's number of N base is >n_base_limit, then this read/pair\ + \ is discarded. Default is 5.\n" + info: null + example: + - 5 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--average_qual" + alternatives: + - "-e" + description: "If one read's average quality score &1 | sed 's# #: \"#;s#$#\"#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/fastp/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/fastp" + executable: "target/nextflow/fastp/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/fastp/main.nf b/target/nextflow/fastp/main.nf new file mode 100644 index 00000000..503263af --- /dev/null +++ b/target/nextflow/fastp/main.nf @@ -0,0 +1,4684 @@ +// fastp main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "fastp", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "description" : "`fastp` supports both single-end (SE) and paired-end (PE) input.\n\n- for SE data, you only have to specify read1 input by `-i` or `--in1`.\n- for PE data, you should also specify read2 input by `-I` or `--in2`.\n", + "arguments" : [ + { + "type" : "file", + "name" : "--in1", + "alternatives" : [ + "-i" + ], + "description" : "Input FastQ file. Must be single-end or paired-end R1. Can be gzipped.", + "example" : [ + "in.R1.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--in2", + "alternatives" : [ + "-I" + ], + "description" : "Input FastQ file. Must be paired-end R2. Can be gzipped.", + "example" : [ + "in.R2.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "description" : "\n- for SE data, you only have to specify read1 output by `-o` or `--out1`.\n- for PE data, you should also specify read2 output by `-O` or `--out2`.\n- if you don't specify the output file names, no output files will be written, but the QC will still be done for both data before and after filtering.\n- the output will be gzip-compressed if its file name ends with `.gz`\n", + "arguments" : [ + { + "type" : "file", + "name" : "--out1", + "alternatives" : [ + "-o" + ], + "description" : "The single-end or paired-end R1 reads that pass QC. Will be gzipped if its file name ends with `.gz`.", + "example" : [ + "out.R1.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--out2", + "alternatives" : [ + "-O" + ], + "description" : "The paired-end R2 reads that pass QC. Will be gzipped if its file name ends with `.gz`.", + "example" : [ + "out.R2.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--unpaired1", + "description" : "Store the reads that `read1` passes filters but its paired `read2` doesn't.", + "example" : [ + "unpaired.R1.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--unpaired2", + "description" : "Store the reads that `read2` passes filters but its paired `read1` doesn't.", + "example" : [ + "unpaired.R2.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--failed_out", + "description" : "Store the reads that fail filters.\n\nIf one read failed and is written to --failed_out, its failure reason will be appended to its read name. For example, failed_quality_filter, failed_too_short etc.\nFor PE data, if unpaired reads are not stored (by giving --unpaired1 or --unpaired2), the failed pair of reads will be put together. If one read passes the filters but its pair doesn't, the failure reason will be paired_read_is_failing.\n", + "example" : [ + "failed.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--overlapped_out", + "description" : "For each read pair, output the overlapped region if it has no any mismatched base.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Report output arguments", + "arguments" : [ + { + "type" : "file", + "name" : "--json", + "alternatives" : [ + "-j" + ], + "description" : "The json format report file name\n", + "example" : [ + "out.json" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--html", + "description" : "The html format report file name\n", + "example" : [ + "out.html" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--report_title", + "description" : "The title of the html report, default is \\"fastp report\\".\n", + "example" : [ + "fastp report" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Adapter trimming", + "description" : "Adapter trimming is enabled by default, but you can disable it by `-A` or `--disable_adapter_trimming`. Adapter sequences can be automatically detected for both PE/SE data.\n\n- For SE data, the adapters are evaluated by analyzing the tails of first ~1M reads. This evaluation may be inacurrate, and you can specify the adapter sequence by `-a` or `--adapter_sequence` option. If adapter sequence is specified, the auto detection for SE data will be disabled.\n- For PE data, the adapters can be detected by per-read overlap analysis, which seeks for the overlap of each pair of reads. This method is robust and fast, so normally you don't have to input the adapter sequence even you know it. But you can still specify the adapter sequences for read1 by `--adapter_sequence`, and for read2 by `--adapter_sequence_r2`. If `fastp` fails to find an overlap (i.e. due to low quality bases), it will use these sequences to trim adapters for read1 and read2 respectively.\n- For PE data, the adapter sequence auto-detection is disabled by default since the adapters can be trimmed by overlap analysis. However, you can specify `--detect_adapter_for_pe` to enable it.\n- For PE data, `fastp` will run a little slower if you specify the sequence adapters or enable adapter auto-detection, but usually result in a slightly cleaner output, since the overlap analysis may fail due to sequencing errors or adapter dimers.\n- The most widely used adapter is the Illumina TruSeq adapters. If your data is from the TruSeq library, you can add `--adapter_sequence=AGATCGGAAGAGCACACGTCTGAACTCCAGTCA --adapter_sequence_r2=AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT` to your command lines, or enable auto detection for PE data by specifing `detect_adapter_for_pe`.\n- `fastp` contains some built-in known adapter sequences for better auto-detection. If you want to make some adapters to be a part of the built-in adapters, please file an issue.\n\nYou can also specify --adapter_fasta to give a FASTA file to tell fastp to trim multiple adapters in this FASTA file. Here is a sample of such adapter FASTA file:\n\n```\n>Illumina TruSeq Adapter Read 1\nAGATCGGAAGAGCACACGTCTGAACTCCAGTCA\n>Illumina TruSeq Adapter Read 2\nAGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT\n>polyA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n```\n\nThe adapter sequence in this file should be at least 6bp long, otherwise it will be skipped. And you can give whatever you want to trim, rather than regular sequencing adapters (i.e. polyA).\n\n`fastp` first trims the auto-detected adapter or the adapter sequences given by `--adapter_sequence | --adapter_sequence_r2`, then trims the adapters given by `--adapter_fasta` one by one.\n\nThe sequence distribution of trimmed adapters can be found at the HTML/JSON reports.\n", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--disable_adapter_trimming", + "alternatives" : [ + "-A" + ], + "description" : "Disable adapter trimming.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--detect_adapter_for_pe", + "description" : "By default, the auto-detection for adapter is for SE data input only, turn on this option to enable it for PE data.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--adapter_sequence", + "alternatives" : [ + "-a" + ], + "description" : "The adapter sequences to be trimmed. For SE data, if not specified, the adapters will be auto-detected. For PE data, this is used if R1/R2 are found not overlapped\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--adapter_sequence_r2", + "description" : "The adapter sequences to be trimmed for R2. This is used for PE data if R1/R2 are found overlapped.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--adapter_fasta", + "description" : "A FASTA file containing all the adapter sequences to be trimmed. For SE data, if not specified, the adapters will be auto-detected. For PE data, this is used if R1/R2 are found not overlapped.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Base trimming", + "arguments" : [ + { + "type" : "integer", + "name" : "--trim_front1", + "alternatives" : [ + "-f" + ], + "description" : "Trimming how many bases in front for read1, default is 0.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--trim_tail1", + "alternatives" : [ + "-t" + ], + "description" : "Trimming how many bases in tail for read1, default is 0.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--max_len1", + "alternatives" : [ + "-b" + ], + "description" : "If read1 is longer than max_len1, then trim read1 at its tail to make it as long as max_len1. Default 0 means no limitation.\n", + "required" : false, + "min" : 0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--trim_front2", + "alternatives" : [ + "-F" + ], + "description" : "Trimming how many bases in front for read2, default is 0.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--trim_tail2", + "alternatives" : [ + "-T" + ], + "description" : "Trimming how many bases in tail for read2, default is 0.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--max_len2", + "alternatives" : [ + "-B" + ], + "description" : "If read2 is longer than max_len2, then trim read2 at its tail to make it as long as max_len2. Default 0 means no limitation.\n", + "required" : false, + "min" : 0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Merging mode", + "description" : "Allows merging paired-end reads into a single longer read if they are overlapping.", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--merge", + "alternatives" : [ + "-m" + ], + "description" : "For paired-end input, merge each pair of reads into a single read if they are overlapped. The merged reads will be written to the file given by --merged_out, the unmerged reads will be written to the files specified by --out1 and --out2. The merging mode is disabled by default.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--merged_out", + "description" : "In the merging mode, specify the file name to store merged output, or specify --stdout to stream the merged output.\n", + "example" : [ + "merged.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--include_unmerged", + "description" : "In the merging mode, write the unmerged or unpaired reads to the file specified by --merge. Disabled by default.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Additional input arguments", + "description" : "Affects how the input is read.", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--interleaved_in", + "description" : "Indicate that is an interleaved FASTQ which contains both read1 and read2. Disabled by default.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--fix_mgi_id", + "description" : "The MGI FASTQ ID format is not compatible with many BAM operation tools, enable this option to fix it.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--phred64", + "alternatives" : [ + "-6" + ], + "description" : "Indicate the input is using phred64 scoring (it'll be converted to phred33, so the output will still be phred33)\n", + "direction" : "input" + } + ] + }, + { + "name" : "Additional output arguments", + "description" : "Affects how the output is written.", + "arguments" : [ + { + "type" : "integer", + "name" : "--compression", + "alternatives" : [ + "-z" + ], + "description" : "Compression level for gzip output (1 ~ 9). 1 is fastest, 9 is smallest, default is 4.\n", + "example" : [ + 4 + ], + "required" : false, + "min" : 1, + "max" : 9, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--dont_overwrite", + "description" : "Don't overwrite existing files. Overwritting is allowed by default.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Logging arguments", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--verbose", + "alternatives" : [ + "-V" + ], + "description" : "Output verbose log information (i.e. when every 1M reads are processed).", + "direction" : "input" + } + ] + }, + { + "name" : "Processing arguments", + "arguments" : [ + { + "type" : "long", + "name" : "--reads_to_process", + "description" : "Specify how many reads/pairs to be processed. Default 0 means process all reads.\n", + "example" : [ + 1000000 + ], + "required" : false, + "min" : 0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Deduplication arguments", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--dedup", + "description" : "Enable deduplication to drop the duplicated reads/pairs\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--dup_calc_accuracy", + "description" : "Accuracy level to calculate duplication (1~6). Higher level uses more memory (1G, 2G, 4G, 8G, 16G, 24G). Default 1 for no-dedup mode, and 3 for dedup mode.\n", + "example" : [ + 3 + ], + "required" : false, + "min" : 1, + "max" : 6, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--dont_eval_duplication", + "description" : "Don't evaluate duplication rate to save time and use less memory.\n", + "direction" : "input" + } + ] + }, + { + "name" : "PolyG tail trimming arguments", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--trim_poly_g", + "alternatives" : [ + "-g" + ], + "description" : "Force polyG tail trimming, by default trimming is automatically enabled for Illumina NextSeq/NovaSeq data\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--poly_g_min_len", + "description" : "The minimum length to detect polyG in the read tail. 10 by default.\n", + "example" : [ + 10 + ], + "required" : false, + "min" : 1, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--disable_trim_poly_g", + "alternatives" : [ + "-G" + ], + "description" : "Disable polyG tail trimming, by default trimming is automatically enabled for Illumina NextSeq/NovaSeq data\n", + "direction" : "input" + } + ] + }, + { + "name" : "PolyX tail trimming arguments", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--trim_poly_x", + "alternatives" : [ + "-x" + ], + "description" : "Enable polyX trimming in 3' ends.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--poly_x_min_len", + "description" : "The minimum length to detect polyX in the read tail. 10 by default.\n", + "example" : [ + 10 + ], + "required" : false, + "min" : 1, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Cut arguments", + "arguments" : [ + { + "type" : "integer", + "name" : "--cut_front", + "alternatives" : [ + "-5" + ], + "description" : "Move a sliding window from front (5') to tail, drop the bases in the window if its mean quality < threshold, stop otherwise.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cut_tail", + "alternatives" : [ + "-3" + ], + "description" : "Move a sliding window from tail (3') to front, drop the bases in the window if its mean quality < threshold, stop otherwise.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cut_right", + "alternatives" : [ + "-r" + ], + "description" : "Move a sliding window from front to tail, if meet one window with mean quality < threshold, drop the bases in the window and the right part, and then stop.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cut_window_size", + "alternatives" : [ + "-W" + ], + "description" : "The window size option shared by cut_front, cut_tail or cut_sliding. Range: 1~1000, default: 4.\n", + "example" : [ + 4 + ], + "required" : false, + "min" : 1, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cut_mean_quality", + "alternatives" : [ + "-M" + ], + "description" : "The mean quality requirement option shared by cut_front, cut_tail or cut_sliding. Range: 1~36 default: 20 (Q20)\n", + "example" : [ + 20 + ], + "required" : false, + "min" : 0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cut_front_window_size", + "description" : "The window size option of cut_front, default to cut_window_size if not specified.\n", + "example" : [ + 4 + ], + "required" : false, + "min" : 1, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cut_front_mean_quality", + "description" : "The mean quality requirement option of cut_front, default to cut_mean_quality if not specified.\n", + "example" : [ + 20 + ], + "required" : false, + "min" : 0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cut_tail_window_size", + "description" : "The window size option of cut_tail, default to cut_window_size if not specified.\n", + "example" : [ + 4 + ], + "required" : false, + "min" : 1, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cut_tail_mean_quality", + "description" : "The mean quality requirement option of cut_tail, default to cut_mean_quality if not specified.\n", + "example" : [ + 20 + ], + "required" : false, + "min" : 0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cut_right_window_size", + "description" : "The window size option of cut_right, default to cut_window_size if not specified.\n", + "example" : [ + 4 + ], + "required" : false, + "min" : 1, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cut_right_mean_quality", + "description" : "The mean quality requirement option of cut_right, default to cut_mean_quality if not specified.\n", + "example" : [ + 20 + ], + "required" : false, + "min" : 0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Quality filtering arguments", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--disable_quality_filtering", + "alternatives" : [ + "-Q" + ], + "description" : "Quality filtering is enabled by default. If this option is specified, quality filtering is disabled.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--qualified_quality_phred", + "alternatives" : [ + "-q" + ], + "description" : "The quality value that a base is qualified. Default 15 means phred quality >=Q15 is qualified.\n", + "example" : [ + 15 + ], + "required" : false, + "min" : 0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--unqualified_percent_limit", + "alternatives" : [ + "-u" + ], + "description" : "How many percents of bases are allowed to be unqualified (0~100). Default 40 means 40%.\n", + "example" : [ + 40 + ], + "required" : false, + "min" : 0, + "max" : 100, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--n_base_limit", + "alternatives" : [ + "-n" + ], + "description" : "If one read's number of N base is >n_base_limit, then this read/pair is discarded. Default is 5.\n", + "example" : [ + 5 + ], + "required" : false, + "min" : 0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--average_qual", + "alternatives" : [ + "-e" + ], + "description" : "If one read's average quality score &1 | sed 's# #: \\"#;s#$#\\"#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/fastp/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/fastp", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_IN1+x} ]; then echo "${VIASH_PAR_IN1}" | sed "s#'#'\\"'\\"'#g;s#.*#par_in1='&'#" ; else echo "# par_in1="; fi ) +$( if [ ! -z ${VIASH_PAR_IN2+x} ]; then echo "${VIASH_PAR_IN2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_in2='&'#" ; else echo "# par_in2="; fi ) +$( if [ ! -z ${VIASH_PAR_OUT1+x} ]; then echo "${VIASH_PAR_OUT1}" | sed "s#'#'\\"'\\"'#g;s#.*#par_out1='&'#" ; else echo "# par_out1="; fi ) +$( if [ ! -z ${VIASH_PAR_OUT2+x} ]; then echo "${VIASH_PAR_OUT2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_out2='&'#" ; else echo "# par_out2="; fi ) +$( if [ ! -z ${VIASH_PAR_UNPAIRED1+x} ]; then echo "${VIASH_PAR_UNPAIRED1}" | sed "s#'#'\\"'\\"'#g;s#.*#par_unpaired1='&'#" ; else echo "# par_unpaired1="; fi ) +$( if [ ! -z ${VIASH_PAR_UNPAIRED2+x} ]; then echo "${VIASH_PAR_UNPAIRED2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_unpaired2='&'#" ; else echo "# par_unpaired2="; fi ) +$( if [ ! -z ${VIASH_PAR_FAILED_OUT+x} ]; then echo "${VIASH_PAR_FAILED_OUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_failed_out='&'#" ; else echo "# par_failed_out="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAPPED_OUT+x} ]; then echo "${VIASH_PAR_OVERLAPPED_OUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_overlapped_out='&'#" ; else echo "# par_overlapped_out="; fi ) +$( if [ ! -z ${VIASH_PAR_JSON+x} ]; then echo "${VIASH_PAR_JSON}" | sed "s#'#'\\"'\\"'#g;s#.*#par_json='&'#" ; else echo "# par_json="; fi ) +$( if [ ! -z ${VIASH_PAR_HTML+x} ]; then echo "${VIASH_PAR_HTML}" | sed "s#'#'\\"'\\"'#g;s#.*#par_html='&'#" ; else echo "# par_html="; fi ) +$( if [ ! -z ${VIASH_PAR_REPORT_TITLE+x} ]; then echo "${VIASH_PAR_REPORT_TITLE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_report_title='&'#" ; else echo "# par_report_title="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_ADAPTER_TRIMMING+x} ]; then echo "${VIASH_PAR_DISABLE_ADAPTER_TRIMMING}" | sed "s#'#'\\"'\\"'#g;s#.*#par_disable_adapter_trimming='&'#" ; else echo "# par_disable_adapter_trimming="; fi ) +$( if [ ! -z ${VIASH_PAR_DETECT_ADAPTER_FOR_PE+x} ]; then echo "${VIASH_PAR_DETECT_ADAPTER_FOR_PE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_detect_adapter_for_pe='&'#" ; else echo "# par_detect_adapter_for_pe="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_SEQUENCE+x} ]; then echo "${VIASH_PAR_ADAPTER_SEQUENCE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_adapter_sequence='&'#" ; else echo "# par_adapter_sequence="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_SEQUENCE_R2+x} ]; then echo "${VIASH_PAR_ADAPTER_SEQUENCE_R2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_adapter_sequence_r2='&'#" ; else echo "# par_adapter_sequence_r2="; fi ) +$( if [ ! -z ${VIASH_PAR_ADAPTER_FASTA+x} ]; then echo "${VIASH_PAR_ADAPTER_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_adapter_fasta='&'#" ; else echo "# par_adapter_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_FRONT1+x} ]; then echo "${VIASH_PAR_TRIM_FRONT1}" | sed "s#'#'\\"'\\"'#g;s#.*#par_trim_front1='&'#" ; else echo "# par_trim_front1="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_TAIL1+x} ]; then echo "${VIASH_PAR_TRIM_TAIL1}" | sed "s#'#'\\"'\\"'#g;s#.*#par_trim_tail1='&'#" ; else echo "# par_trim_tail1="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_LEN1+x} ]; then echo "${VIASH_PAR_MAX_LEN1}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_len1='&'#" ; else echo "# par_max_len1="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_FRONT2+x} ]; then echo "${VIASH_PAR_TRIM_FRONT2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_trim_front2='&'#" ; else echo "# par_trim_front2="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_TAIL2+x} ]; then echo "${VIASH_PAR_TRIM_TAIL2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_trim_tail2='&'#" ; else echo "# par_trim_tail2="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_LEN2+x} ]; then echo "${VIASH_PAR_MAX_LEN2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_len2='&'#" ; else echo "# par_max_len2="; fi ) +$( if [ ! -z ${VIASH_PAR_MERGE+x} ]; then echo "${VIASH_PAR_MERGE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_merge='&'#" ; else echo "# par_merge="; fi ) +$( if [ ! -z ${VIASH_PAR_MERGED_OUT+x} ]; then echo "${VIASH_PAR_MERGED_OUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_merged_out='&'#" ; else echo "# par_merged_out="; fi ) +$( if [ ! -z ${VIASH_PAR_INCLUDE_UNMERGED+x} ]; then echo "${VIASH_PAR_INCLUDE_UNMERGED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_include_unmerged='&'#" ; else echo "# par_include_unmerged="; fi ) +$( if [ ! -z ${VIASH_PAR_INTERLEAVED_IN+x} ]; then echo "${VIASH_PAR_INTERLEAVED_IN}" | sed "s#'#'\\"'\\"'#g;s#.*#par_interleaved_in='&'#" ; else echo "# par_interleaved_in="; fi ) +$( if [ ! -z ${VIASH_PAR_FIX_MGI_ID+x} ]; then echo "${VIASH_PAR_FIX_MGI_ID}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fix_mgi_id='&'#" ; else echo "# par_fix_mgi_id="; fi ) +$( if [ ! -z ${VIASH_PAR_PHRED64+x} ]; then echo "${VIASH_PAR_PHRED64}" | sed "s#'#'\\"'\\"'#g;s#.*#par_phred64='&'#" ; else echo "# par_phred64="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPRESSION+x} ]; then echo "${VIASH_PAR_COMPRESSION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_compression='&'#" ; else echo "# par_compression="; fi ) +$( if [ ! -z ${VIASH_PAR_DONT_OVERWRITE+x} ]; then echo "${VIASH_PAR_DONT_OVERWRITE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_dont_overwrite='&'#" ; else echo "# par_dont_overwrite="; fi ) +$( if [ ! -z ${VIASH_PAR_VERBOSE+x} ]; then echo "${VIASH_PAR_VERBOSE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_verbose='&'#" ; else echo "# par_verbose="; fi ) +$( if [ ! -z ${VIASH_PAR_READS_TO_PROCESS+x} ]; then echo "${VIASH_PAR_READS_TO_PROCESS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_reads_to_process='&'#" ; else echo "# par_reads_to_process="; fi ) +$( if [ ! -z ${VIASH_PAR_DEDUP+x} ]; then echo "${VIASH_PAR_DEDUP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_dedup='&'#" ; else echo "# par_dedup="; fi ) +$( if [ ! -z ${VIASH_PAR_DUP_CALC_ACCURACY+x} ]; then echo "${VIASH_PAR_DUP_CALC_ACCURACY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_dup_calc_accuracy='&'#" ; else echo "# par_dup_calc_accuracy="; fi ) +$( if [ ! -z ${VIASH_PAR_DONT_EVAL_DUPLICATION+x} ]; then echo "${VIASH_PAR_DONT_EVAL_DUPLICATION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_dont_eval_duplication='&'#" ; else echo "# par_dont_eval_duplication="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_POLY_G+x} ]; then echo "${VIASH_PAR_TRIM_POLY_G}" | sed "s#'#'\\"'\\"'#g;s#.*#par_trim_poly_g='&'#" ; else echo "# par_trim_poly_g="; fi ) +$( if [ ! -z ${VIASH_PAR_POLY_G_MIN_LEN+x} ]; then echo "${VIASH_PAR_POLY_G_MIN_LEN}" | sed "s#'#'\\"'\\"'#g;s#.*#par_poly_g_min_len='&'#" ; else echo "# par_poly_g_min_len="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_TRIM_POLY_G+x} ]; then echo "${VIASH_PAR_DISABLE_TRIM_POLY_G}" | sed "s#'#'\\"'\\"'#g;s#.*#par_disable_trim_poly_g='&'#" ; else echo "# par_disable_trim_poly_g="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_POLY_X+x} ]; then echo "${VIASH_PAR_TRIM_POLY_X}" | sed "s#'#'\\"'\\"'#g;s#.*#par_trim_poly_x='&'#" ; else echo "# par_trim_poly_x="; fi ) +$( if [ ! -z ${VIASH_PAR_POLY_X_MIN_LEN+x} ]; then echo "${VIASH_PAR_POLY_X_MIN_LEN}" | sed "s#'#'\\"'\\"'#g;s#.*#par_poly_x_min_len='&'#" ; else echo "# par_poly_x_min_len="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_FRONT+x} ]; then echo "${VIASH_PAR_CUT_FRONT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_front='&'#" ; else echo "# par_cut_front="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_TAIL+x} ]; then echo "${VIASH_PAR_CUT_TAIL}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_tail='&'#" ; else echo "# par_cut_tail="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_RIGHT+x} ]; then echo "${VIASH_PAR_CUT_RIGHT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_right='&'#" ; else echo "# par_cut_right="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_WINDOW_SIZE+x} ]; then echo "${VIASH_PAR_CUT_WINDOW_SIZE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_window_size='&'#" ; else echo "# par_cut_window_size="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_MEAN_QUALITY+x} ]; then echo "${VIASH_PAR_CUT_MEAN_QUALITY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_mean_quality='&'#" ; else echo "# par_cut_mean_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_FRONT_WINDOW_SIZE+x} ]; then echo "${VIASH_PAR_CUT_FRONT_WINDOW_SIZE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_front_window_size='&'#" ; else echo "# par_cut_front_window_size="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_FRONT_MEAN_QUALITY+x} ]; then echo "${VIASH_PAR_CUT_FRONT_MEAN_QUALITY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_front_mean_quality='&'#" ; else echo "# par_cut_front_mean_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_TAIL_WINDOW_SIZE+x} ]; then echo "${VIASH_PAR_CUT_TAIL_WINDOW_SIZE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_tail_window_size='&'#" ; else echo "# par_cut_tail_window_size="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_TAIL_MEAN_QUALITY+x} ]; then echo "${VIASH_PAR_CUT_TAIL_MEAN_QUALITY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_tail_mean_quality='&'#" ; else echo "# par_cut_tail_mean_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_RIGHT_WINDOW_SIZE+x} ]; then echo "${VIASH_PAR_CUT_RIGHT_WINDOW_SIZE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_right_window_size='&'#" ; else echo "# par_cut_right_window_size="; fi ) +$( if [ ! -z ${VIASH_PAR_CUT_RIGHT_MEAN_QUALITY+x} ]; then echo "${VIASH_PAR_CUT_RIGHT_MEAN_QUALITY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cut_right_mean_quality='&'#" ; else echo "# par_cut_right_mean_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_QUALITY_FILTERING+x} ]; then echo "${VIASH_PAR_DISABLE_QUALITY_FILTERING}" | sed "s#'#'\\"'\\"'#g;s#.*#par_disable_quality_filtering='&'#" ; else echo "# par_disable_quality_filtering="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALIFIED_QUALITY_PHRED+x} ]; then echo "${VIASH_PAR_QUALIFIED_QUALITY_PHRED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_qualified_quality_phred='&'#" ; else echo "# par_qualified_quality_phred="; fi ) +$( if [ ! -z ${VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT+x} ]; then echo "${VIASH_PAR_UNQUALIFIED_PERCENT_LIMIT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_unqualified_percent_limit='&'#" ; else echo "# par_unqualified_percent_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_N_BASE_LIMIT+x} ]; then echo "${VIASH_PAR_N_BASE_LIMIT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_n_base_limit='&'#" ; else echo "# par_n_base_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_AVERAGE_QUAL+x} ]; then echo "${VIASH_PAR_AVERAGE_QUAL}" | sed "s#'#'\\"'\\"'#g;s#.*#par_average_qual='&'#" ; else echo "# par_average_qual="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_LENGTH_FILTERING+x} ]; then echo "${VIASH_PAR_DISABLE_LENGTH_FILTERING}" | sed "s#'#'\\"'\\"'#g;s#.*#par_disable_length_filtering='&'#" ; else echo "# par_disable_length_filtering="; fi ) +$( if [ ! -z ${VIASH_PAR_LENGTH_REQUIRED+x} ]; then echo "${VIASH_PAR_LENGTH_REQUIRED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_length_required='&'#" ; else echo "# par_length_required="; fi ) +$( if [ ! -z ${VIASH_PAR_LENGTH_LIMIT+x} ]; then echo "${VIASH_PAR_LENGTH_LIMIT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_length_limit='&'#" ; else echo "# par_length_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_LOW_COMPLEXITY_FILTER+x} ]; then echo "${VIASH_PAR_LOW_COMPLEXITY_FILTER}" | sed "s#'#'\\"'\\"'#g;s#.*#par_low_complexity_filter='&'#" ; else echo "# par_low_complexity_filter="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPLEXITY_THRESHOLD+x} ]; then echo "${VIASH_PAR_COMPLEXITY_THRESHOLD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_complexity_threshold='&'#" ; else echo "# par_complexity_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTER_BY_INDEX1+x} ]; then echo "${VIASH_PAR_FILTER_BY_INDEX1}" | sed "s#'#'\\"'\\"'#g;s#.*#par_filter_by_index1='&'#" ; else echo "# par_filter_by_index1="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTER_BY_INDEX2+x} ]; then echo "${VIASH_PAR_FILTER_BY_INDEX2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_filter_by_index2='&'#" ; else echo "# par_filter_by_index2="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTER_BY_INDEX_THRESHOLD+x} ]; then echo "${VIASH_PAR_FILTER_BY_INDEX_THRESHOLD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_filter_by_index_threshold='&'#" ; else echo "# par_filter_by_index_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_CORRECTION+x} ]; then echo "${VIASH_PAR_CORRECTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_correction='&'#" ; else echo "# par_correction="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAP_LEN_REQUIRE+x} ]; then echo "${VIASH_PAR_OVERLAP_LEN_REQUIRE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_overlap_len_require='&'#" ; else echo "# par_overlap_len_require="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAP_DIFF_LIMIT+x} ]; then echo "${VIASH_PAR_OVERLAP_DIFF_LIMIT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_overlap_diff_limit='&'#" ; else echo "# par_overlap_diff_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT+x} ]; then echo "${VIASH_PAR_OVERLAP_DIFF_PERCENT_LIMIT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_overlap_diff_percent_limit='&'#" ; else echo "# par_overlap_diff_percent_limit="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI+x} ]; then echo "${VIASH_PAR_UMI}" | sed "s#'#'\\"'\\"'#g;s#.*#par_umi='&'#" ; else echo "# par_umi="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI_LOC+x} ]; then echo "${VIASH_PAR_UMI_LOC}" | sed "s#'#'\\"'\\"'#g;s#.*#par_umi_loc='&'#" ; else echo "# par_umi_loc="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI_LEN+x} ]; then echo "${VIASH_PAR_UMI_LEN}" | sed "s#'#'\\"'\\"'#g;s#.*#par_umi_len='&'#" ; else echo "# par_umi_len="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI_PREFIX+x} ]; then echo "${VIASH_PAR_UMI_PREFIX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_umi_prefix='&'#" ; else echo "# par_umi_prefix="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI_SKIP+x} ]; then echo "${VIASH_PAR_UMI_SKIP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_umi_skip='&'#" ; else echo "# par_umi_skip="; fi ) +$( if [ ! -z ${VIASH_PAR_UMI_DELIM+x} ]; then echo "${VIASH_PAR_UMI_DELIM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_umi_delim='&'#" ; else echo "# par_umi_delim="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERREPRESENTATION_ANALYSIS+x} ]; then echo "${VIASH_PAR_OVERREPRESENTATION_ANALYSIS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_overrepresentation_analysis='&'#" ; else echo "# par_overrepresentation_analysis="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERREPRESENTATION_SAMPLING+x} ]; then echo "${VIASH_PAR_OVERREPRESENTATION_SAMPLING}" | sed "s#'#'\\"'\\"'#g;s#.*#par_overrepresentation_sampling='&'#" ; else echo "# par_overrepresentation_sampling="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# disable flags +[[ "\\$par_disable_adapter_trimming" == "false" ]] && unset par_disable_adapter_trimming +[[ "\\$par_detect_adapter_for_pe" == "false" ]] && unset par_detect_adapter_for_pe +[[ "\\$par_merge" == "false" ]] && unset par_merge +[[ "\\$par_include_unmerged" == "false" ]] && unset par_include_unmerged +[[ "\\$par_interleaved_in" == "false" ]] && unset par_interleaved_in +[[ "\\$par_fix_mgi_id" == "false" ]] && unset par_fix_mgi_id +[[ "\\$par_phred64" == "false" ]] && unset par_phred64 +[[ "\\$par_dont_overwrite" == "false" ]] && unset par_dont_overwrite +[[ "\\$par_verbose" == "false" ]] && unset par_verbose +[[ "\\$par_dedup" == "false" ]] && unset par_dedup +[[ "\\$par_dont_eval_duplication" == "false" ]] && unset par_dont_eval_duplication +[[ "\\$par_trim_poly_g" == "false" ]] && unset par_trim_poly_g +[[ "\\$par_disable_trim_poly_g" == "false" ]] && unset par_disable_trim_poly_g +[[ "\\$par_trim_poly_x" == "false" ]] && unset par_trim_poly_x +[[ "\\$par_disable_quality_filtering" == "false" ]] && unset par_disable_quality_filtering +[[ "\\$par_disable_length_filtering" == "false" ]] && unset par_disable_length_filtering +[[ "\\$par_low_complexity_filter" == "false" ]] && unset par_low_complexity_filter +[[ "\\$par_umi" == "false" ]] && unset par_umi +[[ "\\$par_overrepresentation_analysis" == "false" ]] && unset par_overrepresentation_analysis + +# run command +fastp \\\\ + -i "\\$par_in1" \\\\ + -o "\\$par_out1" \\\\ + \\${par_in2:+--in2 "\\${par_in2}"} \\\\ + \\${par_out2:+--out2 "\\${par_out2}"} \\\\ + \\${par_unpaired1:+--unpaired1 "\\${par_unpaired1}"} \\\\ + \\${par_unpaired2:+--unpaired2 "\\${par_unpaired2}"} \\\\ + \\${par_failed_out:+--failed_out "\\${par_failed_out}"} \\\\ + \\${par_overlapped_out:+--overlapped_out "\\${par_overlapped_out}"} \\\\ + \\${par_json:+--json "\\${par_json}"} \\\\ + \\${par_html:+--html "\\${par_html}"} \\\\ + \\${par_report_title:+--report_title "\\${par_report_title}"} \\\\ + \\${par_disable_adapter_trimming:+--disable_adapter_trimming} \\\\ + \\${par_detect_adapter_for_pe:+--detect_adapter_for_pe} \\\\ + \\${par_adapter_sequence:+--adapter_sequence "\\${par_adapter_sequence}"} \\\\ + \\${par_adapter_sequence_r2:+--adapter_sequence_r2 "\\${par_adapter_sequence_r2}"} \\\\ + \\${par_adapter_fasta:+--adapter_fasta "\\${par_adapter_fasta}"} \\\\ + \\${par_trim_front1:+--trim_front1 "\\${par_trim_front1}"} \\\\ + \\${par_trim_tail1:+--trim_tail1 "\\${par_trim_tail1}"} \\\\ + \\${par_max_len1:+--max_len1 "\\${par_max_len1}"} \\\\ + \\${par_trim_front2:+--trim_front2 "\\${par_trim_front2}"} \\\\ + \\${par_trim_tail2:+--trim_tail2 "\\${par_trim_tail2}"} \\\\ + \\${par_max_len2:+--max_len2 "\\${par_max_len2}"} \\\\ + \\${par_merge:+--merge} \\\\ + \\${par_merged_out:+--merged_out "\\${par_merged_out}"} \\\\ + \\${par_include_unmerged:+--include_unmerged} \\\\ + \\${par_interleaved_in:+--interleaved_in} \\\\ + \\${par_fix_mgi_id:+--fix_mgi_id} \\\\ + \\${par_phred64:+--phred64} \\\\ + \\${par_compression:+--compression "\\${par_compression}"} \\\\ + \\${par_dont_overwrite:+--dont_overwrite} \\\\ + \\${par_verbose:+--verbose} \\\\ + \\${par_reads_to_process:+--reads_to_process "\\${par_reads_to_process}"} \\\\ + \\${par_dedup:+--dedup} \\\\ + \\${par_dup_calc_accuracy:+--dup_calc_accuracy "\\${par_dup_calc_accuracy}"} \\\\ + \\${par_dont_eval_duplication:+--dont_eval_duplication} \\\\ + \\${par_trim_poly_g:+--trim_poly_g} \\\\ + \\${par_poly_g_min_len:+--poly_g_min_len "\\${par_poly_g_min_len}"} \\\\ + \\${par_disable_trim_poly_g:+--disable_trim_poly_g} \\\\ + \\${par_trim_poly_x:+--trim_poly_x} \\\\ + \\${par_poly_x_min_len:+--poly_x_min_len "\\${par_poly_x_min_len}"} \\\\ + \\${par_cut_front:+--cut_front "\\${par_cut_front}"} \\\\ + \\${par_cut_tail:+--cut_tail "\\${par_cut_tail}"} \\\\ + \\${par_cut_right:+--cut_right "\\${par_cut_right}"} \\\\ + \\${par_cut_window_size:+--cut_window_size "\\${par_cut_window_size}"} \\\\ + \\${par_cut_mean_quality:+--cut_mean_quality "\\${par_cut_mean_quality}"} \\\\ + \\${par_cut_front_window_size:+--cut_front_window_size "\\${par_cut_front_window_size}"} \\\\ + \\${par_cut_front_mean_quality:+--cut_front_mean_quality "\\${par_cut_front_mean_quality}"} \\\\ + \\${par_cut_tail_window_size:+--cut_tail_window_size "\\${par_cut_tail_window_size}"} \\\\ + \\${par_cut_tail_mean_quality:+--cut_tail_mean_quality "\\${par_cut_tail_mean_quality}"} \\\\ + \\${par_cut_right_window_size:+--cut_right_window_size "\\${par_cut_right_window_size}"} \\\\ + \\${par_cut_right_mean_quality:+--cut_right_mean_quality "\\${par_cut_right_mean_quality}"} \\\\ + \\${par_disable_quality_filtering:+--disable_quality_filtering} \\\\ + \\${par_qualified_quality_phred:+--qualified_quality_phred "\\${par_qualified_quality_phred}"} \\\\ + \\${par_unqualified_percent_limit:+--unqualified_percent_limit "\\${par_unqualified_percent_limit}"} \\\\ + \\${par_n_base_limit:+--n_base_limit "\\${par_n_base_limit}"} \\\\ + \\${par_average_qual:+--average_qual "\\${par_average_qual}"} \\\\ + \\${par_disable_length_filtering:+--disable_length_filtering} \\\\ + \\${par_length_required:+--length_required "\\${par_length_required}"} \\\\ + \\${par_length_limit:+--length_limit "\\${par_length_limit}"} \\\\ + \\${par_low_complexity_filter:+--low_complexity_filter} \\\\ + \\${par_complexity_threshold:+--complexity_threshold "\\${par_complexity_threshold}"} \\\\ + \\${par_filter_by_index1:+--filter_by_index1 "\\${par_filter_by_index1}"} \\\\ + \\${par_filter_by_index2:+--filter_by_index2 "\\${par_filter_by_index2}"} \\\\ + \\${par_filter_by_index_threshold:+--filter_by_index_threshold "\\${par_filter_by_index_threshold}"} \\\\ + \\${par_correction:+--correction} \\\\ + \\${par_overlap_len_require:+--overlap_len_require "\\${par_overlap_len_require}"} \\\\ + \\${par_overlap_diff_limit:+--overlap_diff_limit "\\${par_overlap_diff_limit}"} \\\\ + \\${par_overlap_diff_percent_limit:+--overlap_diff_percent_limit "\\${par_overlap_diff_percent_limit}"} \\\\ + \\${par_umi:+--umi} \\\\ + \\${par_umi_loc:+--umi_loc "\\${par_umi_loc}"} \\\\ + \\${par_umi_len:+--umi_len "\\${par_umi_len}"} \\\\ + \\${par_umi_prefix:+--umi_prefix "\\${par_umi_prefix}"} \\\\ + \\${par_umi_skip:+--umi_skip "\\${par_umi_skip}"} \\\\ + \\${par_umi_delim:+--umi_delim "\\${par_umi_delim}"} \\\\ + \\${par_overrepresentation_analysis:+--overrepresentation_analysis} \\\\ + \\${par_overrepresentation_sampling:+--overrepresentation_sampling "\\${par_overrepresentation_sampling}"} \\\\ + \\${meta_cpus:+--thread "\\${meta_cpus}"} +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/fastp", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/fastp/nextflow.config b/target/nextflow/fastp/nextflow.config new file mode 100644 index 00000000..271ae3de --- /dev/null +++ b/target/nextflow/fastp/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'fastp' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'An ultra-fast all-in-one FASTQ preprocessor (QC/adapters/trimming/filtering/splitting/merging...).\n\nFeatures:\n\n - comprehensive quality profiling for both before and after filtering data (quality curves, base contents, KMER, Q20/Q30, GC Ratio, duplication, adapter contents...)\n - filter out bad reads (too low quality, too short, or too many N...)\n - cut low quality bases for per read in its 5\' and 3\' by evaluating the mean quality from a sliding window (like Trimmomatic but faster).\n - trim all reads in front and tail\n - cut adapters. Adapter sequences can be automatically detected, which means you don\'t have to input the adapter sequences to trim them.\n - correct mismatched base pairs in overlapped regions of paired end reads, if one base is with high quality while the other is with ultra low quality\n - trim polyG in 3\' ends, which is commonly seen in NovaSeq/NextSeq data. Trim polyX in 3\' ends to remove unwanted polyX tailing (i.e. polyA tailing for mRNA-Seq data)\n - preprocess unique molecular identifier (UMI) enabled data, shift UMI to sequence name.\n - report JSON format result for further interpreting.\n - visualize quality control and filtering results on a single HTML page (like FASTQC but faster and more informative).\n - split the output to multiple files (0001.R1.gz, 0002.R1.gz...) to support parallel processing. Two modes can be used, limiting the total split file number, or limitting the lines of each split file.\n - support long reads (data from PacBio / Nanopore devices).\n - support reading from STDIN and writing to STDOUT\n - support interleaved input\n - support ultra-fast FASTQ-level deduplication\n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/fastp/nextflow_schema.json b/target/nextflow/fastp/nextflow_schema.json new file mode 100644 index 00000000..bbeb7338 --- /dev/null +++ b/target/nextflow/fastp/nextflow_schema.json @@ -0,0 +1,1131 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "fastp", +"description": "An ultra-fast all-in-one FASTQ preprocessor (QC/adapters/trimming/filtering/splitting/merging...).\n\nFeatures:\n\n - comprehensive quality profiling for both before and after filtering data (quality curves, base contents, KMER, Q20/Q30, GC Ratio, duplication, adapter contents...)\n - filter out bad reads (too low quality, too short, or too many N...)\n - cut low quality bases for per read in its 5\u0027 and 3\u0027 by evaluating the mean quality from a sliding window (like Trimmomatic but faster).\n - trim all reads in front and tail\n - cut adapters. Adapter sequences can be automatically detected, which means you don\u0027t have to input the adapter sequences to trim them.\n - correct mismatched base pairs in overlapped regions of paired end reads, if one base is with high quality while the other is with ultra low quality\n - trim polyG in 3\u0027 ends, which is commonly seen in NovaSeq/NextSeq data. Trim polyX in 3\u0027 ends to remove unwanted polyX tailing (i.e. polyA tailing for mRNA-Seq data)\n - preprocess unique molecular identifier (UMI) enabled data, shift UMI to sequence name.\n - report JSON format result for further interpreting.\n - visualize quality control and filtering results on a single HTML page (like FASTQC but faster and more informative).\n - split the output to multiple files (0001.R1.gz, 0002.R1.gz...) to support parallel processing. Two modes can be used, limiting the total split file number, or limitting the lines of each split file.\n - support long reads (data from PacBio / Nanopore devices).\n - support reading from STDIN and writing to STDOUT\n - support interleaved input\n - support ultra-fast FASTQ-level deduplication\n", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "`fastp` supports both single-end (SE) and paired-end (PE) input.\n\n- for SE data, you only have to specify read1 input by `-i` or `--in1`.\n- for PE data, you should also specify read2 input by `-I` or `--in2`.\n", + "properties": { + + + "in1": { + "type": + "string", + "description": "Type: `file`, required, example: `in.R1.fq.gz`. Input FastQ file", + "help_text": "Type: `file`, required, example: `in.R1.fq.gz`. Input FastQ file. Must be single-end or paired-end R1. Can be gzipped." + + } + + + , + "in2": { + "type": + "string", + "description": "Type: `file`, example: `in.R2.fq.gz`. Input FastQ file", + "help_text": "Type: `file`, example: `in.R2.fq.gz`. Input FastQ file. Must be paired-end R2. Can be gzipped." + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "\n- for SE data, you only have to specify read1 output by `-o` or `--out1`.\n- for PE data, you should also specify read2 output by `-O` or `--out2`.\n- if you don\u0027t specify the output file names, no output files will be written, but the QC will still be done for both data before and after filtering.\n- the output will be gzip-compressed if its file name ends with `.gz`\n", + "properties": { + + + "out1": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.out1.gz`, example: `out.R1.fq.gz`. The single-end or paired-end R1 reads that pass QC", + "help_text": "Type: `file`, required, default: `$id.$key.out1.gz`, example: `out.R1.fq.gz`. The single-end or paired-end R1 reads that pass QC. Will be gzipped if its file name ends with `.gz`." + , + "default": "$id.$key.out1.gz" + } + + + , + "out2": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.out2.gz`, example: `out.R2.fq.gz`. The paired-end R2 reads that pass QC", + "help_text": "Type: `file`, default: `$id.$key.out2.gz`, example: `out.R2.fq.gz`. The paired-end R2 reads that pass QC. Will be gzipped if its file name ends with `.gz`." + , + "default": "$id.$key.out2.gz" + } + + + , + "unpaired1": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.unpaired1.gz`, example: `unpaired.R1.fq.gz`. Store the reads that `read1` passes filters but its paired `read2` doesn\u0027t", + "help_text": "Type: `file`, default: `$id.$key.unpaired1.gz`, example: `unpaired.R1.fq.gz`. Store the reads that `read1` passes filters but its paired `read2` doesn\u0027t." + , + "default": "$id.$key.unpaired1.gz" + } + + + , + "unpaired2": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.unpaired2.gz`, example: `unpaired.R2.fq.gz`. Store the reads that `read2` passes filters but its paired `read1` doesn\u0027t", + "help_text": "Type: `file`, default: `$id.$key.unpaired2.gz`, example: `unpaired.R2.fq.gz`. Store the reads that `read2` passes filters but its paired `read1` doesn\u0027t." + , + "default": "$id.$key.unpaired2.gz" + } + + + , + "failed_out": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.failed_out.gz`, example: `failed.fq.gz`. Store the reads that fail filters", + "help_text": "Type: `file`, default: `$id.$key.failed_out.gz`, example: `failed.fq.gz`. Store the reads that fail filters.\n\nIf one read failed and is written to --failed_out, its failure reason will be appended to its read name. For example, failed_quality_filter, failed_too_short etc.\nFor PE data, if unpaired reads are not stored (by giving --unpaired1 or --unpaired2), the failed pair of reads will be put together. If one read passes the filters but its pair doesn\u0027t, the failure reason will be paired_read_is_failing.\n" + , + "default": "$id.$key.failed_out.gz" + } + + + , + "overlapped_out": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.overlapped_out.overlapped_out`. For each read pair, output the overlapped region if it has no any mismatched base", + "help_text": "Type: `file`, default: `$id.$key.overlapped_out.overlapped_out`. For each read pair, output the overlapped region if it has no any mismatched base.\n" + , + "default": "$id.$key.overlapped_out.overlapped_out" + } + + +} +}, + + + "report output arguments" : { + "title": "Report output arguments", + "type": "object", + "description": "No description", + "properties": { + + + "json": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.json.json`, example: `out.json`. The json format report file name\n", + "help_text": "Type: `file`, default: `$id.$key.json.json`, example: `out.json`. The json format report file name\n" + , + "default": "$id.$key.json.json" + } + + + , + "html": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.html.html`, example: `out.html`. The html format report file name\n", + "help_text": "Type: `file`, default: `$id.$key.html.html`, example: `out.html`. The html format report file name\n" + , + "default": "$id.$key.html.html" + } + + + , + "report_title": { + "type": + "string", + "description": "Type: `string`, example: `fastp report`. The title of the html report, default is \"fastp report\"", + "help_text": "Type: `string`, example: `fastp report`. The title of the html report, default is \"fastp report\".\n" + + } + + +} +}, + + + "adapter trimming" : { + "title": "Adapter trimming", + "type": "object", + "description": "Adapter trimming is enabled by default, but you can disable it by `-A` or `--disable_adapter_trimming`. Adapter sequences can be automatically detected for both PE/SE data.\n\n- For SE data, the adapters are evaluated by analyzing the tails of first ~1M reads. This evaluation may be inacurrate, and you can specify the adapter sequence by `-a` or `--adapter_sequence` option. If adapter sequence is specified, the auto detection for SE data will be disabled.\n- For PE data, the adapters can be detected by per-read overlap analysis, which seeks for the overlap of each pair of reads. This method is robust and fast, so normally you don\u0027t have to input the adapter sequence even you know it. But you can still specify the adapter sequences for read1 by `--adapter_sequence`, and for read2 by `--adapter_sequence_r2`. If `fastp` fails to find an overlap (i.e. due to low quality bases), it will use these sequences to trim adapters for read1 and read2 respectively.\n- For PE data, the adapter sequence auto-detection is disabled by default since the adapters can be trimmed by overlap analysis. However, you can specify `--detect_adapter_for_pe` to enable it.\n- For PE data, `fastp` will run a little slower if you specify the sequence adapters or enable adapter auto-detection, but usually result in a slightly cleaner output, since the overlap analysis may fail due to sequencing errors or adapter dimers.\n- The most widely used adapter is the Illumina TruSeq adapters. If your data is from the TruSeq library, you can add `--adapter_sequence=AGATCGGAAGAGCACACGTCTGAACTCCAGTCA --adapter_sequence_r2=AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT` to your command lines, or enable auto detection for PE data by specifing `detect_adapter_for_pe`.\n- `fastp` contains some built-in known adapter sequences for better auto-detection. If you want to make some adapters to be a part of the built-in adapters, please file an issue.\n\nYou can also specify --adapter_fasta to give a FASTA file to tell fastp to trim multiple adapters in this FASTA file. Here is a sample of such adapter FASTA file:\n\n```\n\u003eIllumina TruSeq Adapter Read 1\nAGATCGGAAGAGCACACGTCTGAACTCCAGTCA\n\u003eIllumina TruSeq Adapter Read 2\nAGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT\n\u003epolyA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n```\n\nThe adapter sequence in this file should be at least 6bp long, otherwise it will be skipped. And you can give whatever you want to trim, rather than regular sequencing adapters (i.e. polyA).\n\n`fastp` first trims the auto-detected adapter or the adapter sequences given by `--adapter_sequence | --adapter_sequence_r2`, then trims the adapters given by `--adapter_fasta` one by one.\n\nThe sequence distribution of trimmed adapters can be found at the HTML/JSON reports.\n", + "properties": { + + + "disable_adapter_trimming": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Disable adapter trimming", + "help_text": "Type: `boolean_true`, default: `false`. Disable adapter trimming.\n" + , + "default": "False" + } + + + , + "detect_adapter_for_pe": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. By default, the auto-detection for adapter is for SE data input only, turn on this option to enable it for PE data", + "help_text": "Type: `boolean_true`, default: `false`. By default, the auto-detection for adapter is for SE data input only, turn on this option to enable it for PE data.\n" + , + "default": "False" + } + + + , + "adapter_sequence": { + "type": + "string", + "description": "Type: `string`. The adapter sequences to be trimmed", + "help_text": "Type: `string`. The adapter sequences to be trimmed. For SE data, if not specified, the adapters will be auto-detected. For PE data, this is used if R1/R2 are found not overlapped\n" + + } + + + , + "adapter_sequence_r2": { + "type": + "string", + "description": "Type: `string`. The adapter sequences to be trimmed for R2", + "help_text": "Type: `string`. The adapter sequences to be trimmed for R2. This is used for PE data if R1/R2 are found overlapped.\n" + + } + + + , + "adapter_fasta": { + "type": + "string", + "description": "Type: `file`. A FASTA file containing all the adapter sequences to be trimmed", + "help_text": "Type: `file`. A FASTA file containing all the adapter sequences to be trimmed. For SE data, if not specified, the adapters will be auto-detected. For PE data, this is used if R1/R2 are found not overlapped.\n" + + } + + +} +}, + + + "base trimming" : { + "title": "Base trimming", + "type": "object", + "description": "No description", + "properties": { + + + "trim_front1": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Trimming how many bases in front for read1, default is 0", + "help_text": "Type: `integer`, example: `0`. Trimming how many bases in front for read1, default is 0.\n" + + } + + + , + "trim_tail1": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Trimming how many bases in tail for read1, default is 0", + "help_text": "Type: `integer`, example: `0`. Trimming how many bases in tail for read1, default is 0.\n" + + } + + + , + "max_len1": { + "type": + "integer", + "description": "Type: `integer`. If read1 is longer than max_len1, then trim read1 at its tail to make it as long as max_len1", + "help_text": "Type: `integer`. If read1 is longer than max_len1, then trim read1 at its tail to make it as long as max_len1. Default 0 means no limitation.\n" + + } + + + , + "trim_front2": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Trimming how many bases in front for read2, default is 0", + "help_text": "Type: `integer`, example: `0`. Trimming how many bases in front for read2, default is 0.\n" + + } + + + , + "trim_tail2": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Trimming how many bases in tail for read2, default is 0", + "help_text": "Type: `integer`, example: `0`. Trimming how many bases in tail for read2, default is 0.\n" + + } + + + , + "max_len2": { + "type": + "integer", + "description": "Type: `integer`. If read2 is longer than max_len2, then trim read2 at its tail to make it as long as max_len2", + "help_text": "Type: `integer`. If read2 is longer than max_len2, then trim read2 at its tail to make it as long as max_len2. Default 0 means no limitation.\n" + + } + + +} +}, + + + "merging mode" : { + "title": "Merging mode", + "type": "object", + "description": "Allows merging paired-end reads into a single longer read if they are overlapping.", + "properties": { + + + "merge": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For paired-end input, merge each pair of reads into a single read if they are overlapped", + "help_text": "Type: `boolean_true`, default: `false`. For paired-end input, merge each pair of reads into a single read if they are overlapped. The merged reads will be written to the file given by --merged_out, the unmerged reads will be written to the files specified by --out1 and --out2. The merging mode is disabled by default.\n" + , + "default": "False" + } + + + , + "merged_out": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.merged_out.gz`, example: `merged.fq.gz`. In the merging mode, specify the file name to store merged output, or specify --stdout to stream the merged output", + "help_text": "Type: `file`, default: `$id.$key.merged_out.gz`, example: `merged.fq.gz`. In the merging mode, specify the file name to store merged output, or specify --stdout to stream the merged output.\n" + , + "default": "$id.$key.merged_out.gz" + } + + + , + "include_unmerged": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. In the merging mode, write the unmerged or unpaired reads to the file specified by --merge", + "help_text": "Type: `boolean_true`, default: `false`. In the merging mode, write the unmerged or unpaired reads to the file specified by --merge. Disabled by default.\n" + , + "default": "False" + } + + +} +}, + + + "additional input arguments" : { + "title": "Additional input arguments", + "type": "object", + "description": "Affects how the input is read.", + "properties": { + + + "interleaved_in": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Indicate that \u003cin1\u003e is an interleaved FASTQ which contains both read1 and read2", + "help_text": "Type: `boolean_true`, default: `false`. Indicate that \u003cin1\u003e is an interleaved FASTQ which contains both read1 and read2. Disabled by default.\n" + , + "default": "False" + } + + + , + "fix_mgi_id": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. The MGI FASTQ ID format is not compatible with many BAM operation tools, enable this option to fix it", + "help_text": "Type: `boolean_true`, default: `false`. The MGI FASTQ ID format is not compatible with many BAM operation tools, enable this option to fix it.\n" + , + "default": "False" + } + + + , + "phred64": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Indicate the input is using phred64 scoring (it\u0027ll be converted to phred33, so the output will still be phred33)\n", + "help_text": "Type: `boolean_true`, default: `false`. Indicate the input is using phred64 scoring (it\u0027ll be converted to phred33, so the output will still be phred33)\n" + , + "default": "False" + } + + +} +}, + + + "additional output arguments" : { + "title": "Additional output arguments", + "type": "object", + "description": "Affects how the output is written.", + "properties": { + + + "compression": { + "type": + "integer", + "description": "Type: `integer`, example: `4`. Compression level for gzip output (1 ~ 9)", + "help_text": "Type: `integer`, example: `4`. Compression level for gzip output (1 ~ 9). 1 is fastest, 9 is smallest, default is 4.\n" + + } + + + , + "dont_overwrite": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Don\u0027t overwrite existing files", + "help_text": "Type: `boolean_true`, default: `false`. Don\u0027t overwrite existing files. Overwritting is allowed by default.\n" + , + "default": "False" + } + + +} +}, + + + "logging arguments" : { + "title": "Logging arguments", + "type": "object", + "description": "No description", + "properties": { + + + "verbose": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output verbose log information (i", + "help_text": "Type: `boolean_true`, default: `false`. Output verbose log information (i.e. when every 1M reads are processed)." + , + "default": "False" + } + + +} +}, + + + "processing arguments" : { + "title": "Processing arguments", + "type": "object", + "description": "No description", + "properties": { + + + "reads_to_process": { + "type": + "string", + "description": "Type: `long`, example: `1000000`. Specify how many reads/pairs to be processed", + "help_text": "Type: `long`, example: `1000000`. Specify how many reads/pairs to be processed. Default 0 means process all reads.\n" + + } + + +} +}, + + + "deduplication arguments" : { + "title": "Deduplication arguments", + "type": "object", + "description": "No description", + "properties": { + + + "dedup": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Enable deduplication to drop the duplicated reads/pairs\n", + "help_text": "Type: `boolean_true`, default: `false`. Enable deduplication to drop the duplicated reads/pairs\n" + , + "default": "False" + } + + + , + "dup_calc_accuracy": { + "type": + "integer", + "description": "Type: `integer`, example: `3`. Accuracy level to calculate duplication (1~6)", + "help_text": "Type: `integer`, example: `3`. Accuracy level to calculate duplication (1~6). Higher level uses more memory (1G, 2G, 4G, 8G, 16G, 24G). Default 1 for no-dedup mode, and 3 for dedup mode.\n" + + } + + + , + "dont_eval_duplication": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Don\u0027t evaluate duplication rate to save time and use less memory", + "help_text": "Type: `boolean_true`, default: `false`. Don\u0027t evaluate duplication rate to save time and use less memory.\n" + , + "default": "False" + } + + +} +}, + + + "polyg tail trimming arguments" : { + "title": "PolyG tail trimming arguments", + "type": "object", + "description": "No description", + "properties": { + + + "trim_poly_g": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Force polyG tail trimming, by default trimming is automatically enabled for Illumina NextSeq/NovaSeq data\n", + "help_text": "Type: `boolean_true`, default: `false`. Force polyG tail trimming, by default trimming is automatically enabled for Illumina NextSeq/NovaSeq data\n" + , + "default": "False" + } + + + , + "poly_g_min_len": { + "type": + "integer", + "description": "Type: `integer`, example: `10`. The minimum length to detect polyG in the read tail", + "help_text": "Type: `integer`, example: `10`. The minimum length to detect polyG in the read tail. 10 by default.\n" + + } + + + , + "disable_trim_poly_g": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Disable polyG tail trimming, by default trimming is automatically enabled for Illumina NextSeq/NovaSeq data\n", + "help_text": "Type: `boolean_true`, default: `false`. Disable polyG tail trimming, by default trimming is automatically enabled for Illumina NextSeq/NovaSeq data\n" + , + "default": "False" + } + + +} +}, + + + "polyx tail trimming arguments" : { + "title": "PolyX tail trimming arguments", + "type": "object", + "description": "No description", + "properties": { + + + "trim_poly_x": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Enable polyX trimming in 3\u0027 ends", + "help_text": "Type: `boolean_true`, default: `false`. Enable polyX trimming in 3\u0027 ends.\n" + , + "default": "False" + } + + + , + "poly_x_min_len": { + "type": + "integer", + "description": "Type: `integer`, example: `10`. The minimum length to detect polyX in the read tail", + "help_text": "Type: `integer`, example: `10`. The minimum length to detect polyX in the read tail. 10 by default.\n" + + } + + +} +}, + + + "cut arguments" : { + "title": "Cut arguments", + "type": "object", + "description": "No description", + "properties": { + + + "cut_front": { + "type": + "integer", + "description": "Type: `integer`. Move a sliding window from front (5\u0027) to tail, drop the bases in the window if its mean quality \u003c threshold, stop otherwise", + "help_text": "Type: `integer`. Move a sliding window from front (5\u0027) to tail, drop the bases in the window if its mean quality \u003c threshold, stop otherwise.\n" + + } + + + , + "cut_tail": { + "type": + "integer", + "description": "Type: `integer`. Move a sliding window from tail (3\u0027) to front, drop the bases in the window if its mean quality \u003c threshold, stop otherwise", + "help_text": "Type: `integer`. Move a sliding window from tail (3\u0027) to front, drop the bases in the window if its mean quality \u003c threshold, stop otherwise.\n" + + } + + + , + "cut_right": { + "type": + "integer", + "description": "Type: `integer`. Move a sliding window from front to tail, if meet one window with mean quality \u003c threshold, drop the bases in the window and the right part, and then stop", + "help_text": "Type: `integer`. Move a sliding window from front to tail, if meet one window with mean quality \u003c threshold, drop the bases in the window and the right part, and then stop.\n" + + } + + + , + "cut_window_size": { + "type": + "integer", + "description": "Type: `integer`, example: `4`. The window size option shared by cut_front, cut_tail or cut_sliding", + "help_text": "Type: `integer`, example: `4`. The window size option shared by cut_front, cut_tail or cut_sliding. Range: 1~1000, default: 4.\n" + + } + + + , + "cut_mean_quality": { + "type": + "integer", + "description": "Type: `integer`, example: `20`. The mean quality requirement option shared by cut_front, cut_tail or cut_sliding", + "help_text": "Type: `integer`, example: `20`. The mean quality requirement option shared by cut_front, cut_tail or cut_sliding. Range: 1~36 default: 20 (Q20)\n" + + } + + + , + "cut_front_window_size": { + "type": + "integer", + "description": "Type: `integer`, example: `4`. The window size option of cut_front, default to cut_window_size if not specified", + "help_text": "Type: `integer`, example: `4`. The window size option of cut_front, default to cut_window_size if not specified.\n" + + } + + + , + "cut_front_mean_quality": { + "type": + "integer", + "description": "Type: `integer`, example: `20`. The mean quality requirement option of cut_front, default to cut_mean_quality if not specified", + "help_text": "Type: `integer`, example: `20`. The mean quality requirement option of cut_front, default to cut_mean_quality if not specified.\n" + + } + + + , + "cut_tail_window_size": { + "type": + "integer", + "description": "Type: `integer`, example: `4`. The window size option of cut_tail, default to cut_window_size if not specified", + "help_text": "Type: `integer`, example: `4`. The window size option of cut_tail, default to cut_window_size if not specified.\n" + + } + + + , + "cut_tail_mean_quality": { + "type": + "integer", + "description": "Type: `integer`, example: `20`. The mean quality requirement option of cut_tail, default to cut_mean_quality if not specified", + "help_text": "Type: `integer`, example: `20`. The mean quality requirement option of cut_tail, default to cut_mean_quality if not specified.\n" + + } + + + , + "cut_right_window_size": { + "type": + "integer", + "description": "Type: `integer`, example: `4`. The window size option of cut_right, default to cut_window_size if not specified", + "help_text": "Type: `integer`, example: `4`. The window size option of cut_right, default to cut_window_size if not specified.\n" + + } + + + , + "cut_right_mean_quality": { + "type": + "integer", + "description": "Type: `integer`, example: `20`. The mean quality requirement option of cut_right, default to cut_mean_quality if not specified", + "help_text": "Type: `integer`, example: `20`. The mean quality requirement option of cut_right, default to cut_mean_quality if not specified.\n" + + } + + +} +}, + + + "quality filtering arguments" : { + "title": "Quality filtering arguments", + "type": "object", + "description": "No description", + "properties": { + + + "disable_quality_filtering": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Quality filtering is enabled by default", + "help_text": "Type: `boolean_true`, default: `false`. Quality filtering is enabled by default. If this option is specified, quality filtering is disabled.\n" + , + "default": "False" + } + + + , + "qualified_quality_phred": { + "type": + "integer", + "description": "Type: `integer`, example: `15`. The quality value that a base is qualified", + "help_text": "Type: `integer`, example: `15`. The quality value that a base is qualified. Default 15 means phred quality \u003e=Q15 is qualified.\n" + + } + + + , + "unqualified_percent_limit": { + "type": + "integer", + "description": "Type: `integer`, example: `40`. How many percents of bases are allowed to be unqualified (0~100)", + "help_text": "Type: `integer`, example: `40`. How many percents of bases are allowed to be unqualified (0~100). Default 40 means 40%.\n" + + } + + + , + "n_base_limit": { + "type": + "integer", + "description": "Type: `integer`, example: `5`. If one read\u0027s number of N base is \u003en_base_limit, then this read/pair is discarded", + "help_text": "Type: `integer`, example: `5`. If one read\u0027s number of N base is \u003en_base_limit, then this read/pair is discarded. Default is 5.\n" + + } + + + , + "average_qual": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. If one read\u0027s average quality score \u003cavg_qual, then this read/pair is discarded", + "help_text": "Type: `integer`, example: `0`. If one read\u0027s average quality score \u003cavg_qual, then this read/pair is discarded. Default 0 means no requirement.\n" + + } + + +} +}, + + + "length filtering arguments" : { + "title": "Length filtering arguments", + "type": "object", + "description": "No description", + "properties": { + + + "disable_length_filtering": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Length filtering is enabled by default", + "help_text": "Type: `boolean_true`, default: `false`. Length filtering is enabled by default. If this option is specified, length filtering is disabled.\n" + , + "default": "False" + } + + + , + "length_required": { + "type": + "integer", + "description": "Type: `integer`, example: `15`. Reads shorter than length_required will be discarded, default is 15", + "help_text": "Type: `integer`, example: `15`. Reads shorter than length_required will be discarded, default is 15.\n" + + } + + + , + "length_limit": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Reads longer than length_limit will be discarded, default 0 means no limitation", + "help_text": "Type: `integer`, example: `0`. Reads longer than length_limit will be discarded, default 0 means no limitation.\n" + + } + + +} +}, + + + "low complexity filtering arguments" : { + "title": "Low complexity filtering arguments", + "type": "object", + "description": "No description", + "properties": { + + + "low_complexity_filter": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Enable low complexity filter", + "help_text": "Type: `boolean_true`, default: `false`. Enable low complexity filter. The complexity is defined as the percentage of base that is different from its next base (base[i] != base[i+1]).\n" + , + "default": "False" + } + + + , + "complexity_threshold": { + "type": + "integer", + "description": "Type: `integer`, example: `30`. The threshold for low complexity filter (0~100)", + "help_text": "Type: `integer`, example: `30`. The threshold for low complexity filter (0~100). Default is 30, which means 30% complexity is required.\n" + + } + + +} +}, + + + "index filtering arguments" : { + "title": "Index filtering arguments", + "type": "object", + "description": "No description", + "properties": { + + + "filter_by_index1": { + "type": + "string", + "description": "Type: `file`. Specify a file contains a list of barcodes of index1 to be filtered out, one barcode per line", + "help_text": "Type: `file`. Specify a file contains a list of barcodes of index1 to be filtered out, one barcode per line.\n" + + } + + + , + "filter_by_index2": { + "type": + "string", + "description": "Type: `file`. Specify a file contains a list of barcodes of index2 to be filtered out, one barcode per line", + "help_text": "Type: `file`. Specify a file contains a list of barcodes of index2 to be filtered out, one barcode per line.\n" + + } + + + , + "filter_by_index_threshold": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. The allowed difference of index barcode for index filtering, default 0 means completely identical", + "help_text": "Type: `integer`, example: `0`. The allowed difference of index barcode for index filtering, default 0 means completely identical.\n" + + } + + +} +}, + + + "overlapped region correction" : { + "title": "Overlapped region correction", + "type": "object", + "description": "No description", + "properties": { + + + "correction": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Enable base correction in overlapped regions (only for PE data), default is disabled", + "help_text": "Type: `boolean_true`, default: `false`. Enable base correction in overlapped regions (only for PE data), default is disabled.\n" + , + "default": "False" + } + + + , + "overlap_len_require": { + "type": + "integer", + "description": "Type: `integer`, example: `30`. The minimum length to detect overlapped region of PE reads", + "help_text": "Type: `integer`, example: `30`. The minimum length to detect overlapped region of PE reads. This will affect overlap analysis based PE merge, adapter trimming and correction. 30 by default.\n" + + } + + + , + "overlap_diff_limit": { + "type": + "integer", + "description": "Type: `integer`, example: `5`. The maximum number of mismatched bases to detect overlapped region of PE reads", + "help_text": "Type: `integer`, example: `5`. The maximum number of mismatched bases to detect overlapped region of PE reads. This will affect overlap analysis based PE merge, adapter trimming and correction. 5 by default.\n" + + } + + + , + "overlap_diff_percent_limit": { + "type": + "integer", + "description": "Type: `integer`, example: `20`. The maximum percentage of mismatched bases to detect overlapped region of PE reads", + "help_text": "Type: `integer`, example: `20`. The maximum percentage of mismatched bases to detect overlapped region of PE reads. This will affect overlap analysis based PE merge, adapter trimming and correction. Default 20 means 20%.\n" + + } + + +} +}, + + + "umi arguments" : { + "title": "UMI arguments", + "type": "object", + "description": "No description", + "properties": { + + + "umi": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Enable unique molecular identifier (UMI) preprocessing", + "help_text": "Type: `boolean_true`, default: `false`. Enable unique molecular identifier (UMI) preprocessing.\n" + , + "default": "False" + } + + + , + "umi_loc": { + "type": + "string", + "description": "Type: `string`, choices: ``index1`, `index2`, `read1`, `read2`, `per_index`, `per_read``. Specify the location of UMI, can be (index1/index2/read1/read2/per_index/per_read, default is none", + "help_text": "Type: `string`, choices: ``index1`, `index2`, `read1`, `read2`, `per_index`, `per_read``. Specify the location of UMI, can be (index1/index2/read1/read2/per_index/per_read, default is none.\n", + "enum": ["index1", "index2", "read1", "read2", "per_index", "per_read"] + + + } + + + , + "umi_len": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. If the UMI is in read1/read2, its length should be provided", + "help_text": "Type: `integer`, example: `0`. If the UMI is in read1/read2, its length should be provided.\n" + + } + + + , + "umi_prefix": { + "type": + "string", + "description": "Type: `string`. If specified, an underline will be used to connect prefix and UMI (i", + "help_text": "Type: `string`. If specified, an underline will be used to connect prefix and UMI (i.e. prefix=UMI, UMI=AATTCG, final=UMI_AATTCG). No prefix by default.\n" + + } + + + , + "umi_skip": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. If the UMI is in read1/read2, fastp can skip several bases following UMI, default is 0", + "help_text": "Type: `integer`, example: `0`. If the UMI is in read1/read2, fastp can skip several bases following UMI, default is 0.\n" + + } + + + , + "umi_delim": { + "type": + "string", + "description": "Type: `string`. If the UMI is in index1/index2, fastp can use a delimiter to separate UMI from the read sequence, default is none", + "help_text": "Type: `string`. If the UMI is in index1/index2, fastp can use a delimiter to separate UMI from the read sequence, default is none.\n" + + } + + +} +}, + + + "overrepresentation analysis arguments" : { + "title": "Overrepresentation analysis arguments", + "type": "object", + "description": "No description", + "properties": { + + + "overrepresentation_analysis": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Enable overrepresentation analysis", + "help_text": "Type: `boolean_true`, default: `false`. Enable overrepresentation analysis.\n" + , + "default": "False" + } + + + , + "overrepresentation_sampling": { + "type": + "integer", + "description": "Type: `integer`, example: `20`. One in (--overrepresentation_sampling) reads will be computed for overrepresentation analysis (1~10000), smaller is slower, default is 20", + "help_text": "Type: `integer`, example: `20`. One in (--overrepresentation_sampling) reads will be computed for overrepresentation analysis (1~10000), smaller is slower, default is 20.\n" + + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/report output arguments" + }, + + { + "$ref": "#/definitions/adapter trimming" + }, + + { + "$ref": "#/definitions/base trimming" + }, + + { + "$ref": "#/definitions/merging mode" + }, + + { + "$ref": "#/definitions/additional input arguments" + }, + + { + "$ref": "#/definitions/additional output arguments" + }, + + { + "$ref": "#/definitions/logging arguments" + }, + + { + "$ref": "#/definitions/processing arguments" + }, + + { + "$ref": "#/definitions/deduplication arguments" + }, + + { + "$ref": "#/definitions/polyg tail trimming arguments" + }, + + { + "$ref": "#/definitions/polyx tail trimming arguments" + }, + + { + "$ref": "#/definitions/cut arguments" + }, + + { + "$ref": "#/definitions/quality filtering arguments" + }, + + { + "$ref": "#/definitions/length filtering arguments" + }, + + { + "$ref": "#/definitions/low complexity filtering arguments" + }, + + { + "$ref": "#/definitions/index filtering arguments" + }, + + { + "$ref": "#/definitions/overlapped region correction" + }, + + { + "$ref": "#/definitions/umi arguments" + }, + + { + "$ref": "#/definitions/overrepresentation analysis arguments" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/featurecounts/.config.vsh.yaml b/target/nextflow/featurecounts/.config.vsh.yaml new file mode 100644 index 00000000..b8ac7d6f --- /dev/null +++ b/target/nextflow/featurecounts/.config.vsh.yaml @@ -0,0 +1,657 @@ +name: "featurecounts" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--annotation" + alternatives: + - "-a" + description: "Name of an annotation file. GTF/GFF format by default. See '--format'\ + \ option for more format information.\n" + info: null + example: + - "annotation.gtf" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--input" + alternatives: + - "-i" + description: "A list of SAM or BAM format files separated by semi-colon (;). They\ + \ can be either name or location sorted. Location-sorted paired-end reads are\ + \ automatically sorted by read names.\n" + info: null + example: + - "input_file1.bam" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--counts" + alternatives: + - "-o" + description: "Name of output file including read counts in tab delimited format.\n" + info: null + example: + - "features.tsv" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--summary" + description: "Summary statistics of counting results in tab delimited format.\n" + info: null + example: + - "summary.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--junctions" + description: "Count number of reads supporting each exon-exon junction. Junctions\ + \ were identified from those exon-spanning reads in the input (containing 'N'\ + \ in CIGAR string).\n" + info: null + example: + - "junctions.txt" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Annotation" + arguments: + - type: "string" + name: "--format" + alternatives: + - "-F" + description: "Specify format of the provided annotation file. Acceptable formats\ + \ include 'GTF' (or compatible GFF format) and 'SAF'. 'GTF' by default. \n" + info: null + example: + - "GTF" + required: false + choices: + - "GTF" + - "GFF" + - "SAF" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--feature_type" + alternatives: + - "-t" + description: "Specify feature type(s) in a GTF annotation. If multiple types are\ + \ provided, they should be separated by ';' with no space in between. 'exon'\ + \ by default. Rows in the annotation with a matched feature will be extracted\ + \ and used for read mapping.\n" + info: null + example: + - "exon" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--attribute_type" + alternatives: + - "-g" + description: "Specify attribute type in GTF annotation. 'gene_id' by default.\ + \ Meta-features used for read counting will be extracted from annotation using\ + \ the provided value.\n" + info: null + example: + - "gene_id" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--extra_attributes" + description: "Extract extra attribute types from the provided GTF annotation and\ + \ include them in the counting output. These attribute types will not be used\ + \ to group features. If more than one attribute type is provided they should\ + \ be separated by semicolon (;).\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--chrom_alias" + alternatives: + - "-A" + description: "Provide a chromosome name alias file to match chr names in annotation\ + \ with those in the reads. This should be a two-column comma-delimited text\ + \ file. Its first column should include chr names in the annotation and its\ + \ second column should include chr names in the reads. Chr names are case sensitive.\ + \ No column header should be included in the file.\n" + info: null + example: + - "chrom_alias.csv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Level of summarization" + arguments: + - type: "boolean_true" + name: "--feature_level" + alternatives: + - "-f" + description: "Perform read counting at feature level (eg. counting reads for exons\ + \ rather than genes).\n" + info: null + direction: "input" +- name: "Overlap between reads and features" + arguments: + - type: "boolean_true" + name: "--overlapping" + alternatives: + - "-O" + description: "Assign reads to all their overlapping meta-features (or features\ + \ if '--feature_level' is specified).\n" + info: null + direction: "input" + - type: "integer" + name: "--min_overlap" + description: "Minimum number of overlapping bases in a read that is required for\ + \ read assignment. 1 by default. Number of overlapping bases is counted from\ + \ both reads if paired end. If a negative value is provided, then a gap of up\ + \ to specified size will be allowed between read and the feature that the read\ + \ is assigned to.\n" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--frac_overlap" + description: "Minimum fraction of overlapping bases in a read that is required\ + \ for read assignment. Value should be within range [0,1]. 0 by default. Number\ + \ of overlapping bases is counted from both reads if paired end. Both this option\ + \ and '--min_overlap' option need to be satisfied for read assignment.\n" + info: null + example: + - 0.0 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--frac_overlap_feature" + description: "Minimum fraction of overlapping bases in a feature that is required\ + \ for read assignment. Value should be within range [0,1]. 0 by default.\n" + info: null + example: + - 0.0 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--largest_overlap" + description: "Assign reads to a meta-feature/feature that has the largest number\ + \ of overlapping bases.\n" + info: null + direction: "input" + - type: "integer" + name: "--non_overlap" + description: "Maximum number of non-overlapping bases in a read (or a read pair)\ + \ that is allowed when being assigned to a feature. No limit is set by default.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--non_overlap_feature" + description: "Maximum number of non-overlapping bases in a feature that is allowed\ + \ in read assignment. No limit is set by default.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--read_extension5" + description: "Reads are extended upstream by bases from their 5' end.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--read_extension3" + description: "Reads are extended upstream by bases from their 3' end.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--read2pos" + description: "Reduce reads to their 5' most base or 3' most base. Read counting\ + \ is then performed based on the single base the read is reduced to.\n" + info: null + required: false + choices: + - 3 + - 5 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Multi-mapping reads" + arguments: + - type: "boolean_true" + name: "--multi_mapping" + alternatives: + - "-M" + description: "Multi-mapping reads will also be counted. For a multi-mapping read,\ + \ all its reported alignments will be counted. The 'NH' tag in BAM/SAM input\ + \ is used to detect multi-mapping reads.\n" + info: null + direction: "input" +- name: "Fractional counting" + arguments: + - type: "boolean_true" + name: "--fraction" + description: "Assign fractional counts to features. This option must be used together\ + \ with '--multi_mapping' or '--overlapping' or both. When '--multi_mapping'\ + \ is specified, each reported alignment from a multi-mapping read (identified\ + \ via 'NH' tag) will carry a fractional count of 1/x, instead of 1 (one), where\ + \ x is the total number of alignments reported for the same read. When '--overlapping'\ + \ is specified, each overlapping feature will receive a fractional count of\ + \ 1/y, where y is the total number of features overlapping with the read. When\ + \ both '--multi_mapping' and '--overlapping' are specified, each alignment will\ + \ carry a fractional count of 1/(x*y).\n" + info: null + direction: "input" +- name: "Read filtering" + arguments: + - type: "integer" + name: "--min_map_quality" + alternatives: + - "-Q" + description: "The minimum mapping quality score a read must satisfy in order to\ + \ be counted. For paired-end reads, at least one end should satisfy this criteria.\ + \ 0 by default.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--split_only" + description: "Count split alignments only (ie. alignments with CIGAR string containing\ + \ 'N'). An example of split alignments is exon-spanning reads in RNA-seq data.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--non_split_only" + description: "If specified, only non-split alignments (CIGAR strings do not contain\ + \ letter 'N') will be counted. All the other alignments will be ignored.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--primary" + description: "Count primary alignments only. Primary alignments are identified\ + \ using bit 0x100 in SAM/BAM FLAG field.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--ignore_dup" + description: "Ignore duplicate reads in read counting. Duplicate reads are identified\ + \ using bit Ox400 in BAM/SAM FLAG field. The whole read pair is ignored if one\ + \ of the reads is a duplicate read for paired end data.\n" + info: null + direction: "input" +- name: "Strandedness" + arguments: + - type: "integer" + name: "--strand" + alternatives: + - "-s" + description: "Perform strand-specific read counting. A single integer value (applied\ + \ to all input files) should be provided. Possible values include: 0 (unstranded),\ + \ 1 (stranded) and 2 (reversely stranded). Default value is 0 (ie. unstranded\ + \ read counting carried out for all input files).\n" + info: null + example: + - 0 + required: false + choices: + - 0 + - 1 + - 2 + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Exon-exon junctions" + arguments: + - type: "file" + name: "--ref_fasta" + alternatives: + - "-G" + description: "Provide the name of a FASTA-format file that contains the reference\ + \ sequences used in read mapping that produced the provided SAM/BAM files.\n" + info: null + example: + - "reference.fasta" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Parameters specific to paired end reads" + arguments: + - type: "boolean_true" + name: "--paired" + alternatives: + - "-p" + description: "Specify that input data contain paired-end reads. To perform fragment\ + \ counting (ie. counting read pairs), the '--countReadPairs' parameter should\ + \ also be specified in addition to this parameter.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--count_read_pairs" + description: "Count read pairs (fragments) instead of reads. This option is only\ + \ applicable for paired-end reads.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--both_aligned" + alternatives: + - "-B" + description: "Count read pairs (fragments) instead of reads. This option is only\ + \ applicable for paired-end reads.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--check_pe_dist" + alternatives: + - "-P" + description: "Check validity of paired-end distance when counting read pairs.\ + \ Use '--min_length' and '--max_length' to set thresholds.\n" + info: null + direction: "input" + - type: "integer" + name: "--min_length" + alternatives: + - "-d" + description: "Minimum fragment/template length, 50 by default.\n" + info: null + example: + - 50 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_length" + alternatives: + - "-D" + description: "Maximum fragment/template length, 600 by default.\n" + info: null + example: + - 600 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--same_strand" + alternatives: + - "-C" + description: "Do not count read pairs that have their two ends mapping to different\ + \ chromosomes or mapping to same chromosome but on different strands.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--donotsort" + description: "Do not sort reads in BAM/SAM input. Note that reads from the same\ + \ pair are required to be located next to each other in the input.\n" + info: null + direction: "input" +- name: "Read groups" + arguments: + - type: "boolean_true" + name: "--by_read_group" + description: "Assign reads by read group. \"RG\" tag is required to be present\ + \ in the input BAM/SAM files.\n" + info: null + direction: "input" +- name: "Long reads" + arguments: + - type: "boolean_true" + name: "--long_reads" + description: "Count long reads such as Nanopore and PacBio reads. Long read counting\ + \ can only run in one thread and only reads (not read-pairs) can be counted.\ + \ There is no limitation on the number of 'M' operations allowed in a CIGAR\ + \ string in long read counting.\n" + info: null + direction: "input" +- name: "Assignment results for each read" + arguments: + - type: "file" + name: "--detailed_results" + description: "Directory to save the detailed assignment results. Use `--detailed_results_format`\ + \ to determine the format of the detailed results.\n" + info: null + example: + - "detailed_results" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--detailed_results_format" + alternatives: + - "-R" + description: "Output detailed assignment results for each read or read-pair. Results\ + \ are saved to a file that is in one of the following formats: CORE, SAM and\ + \ BAM. See documentaiton for more info about these formats.\n" + info: null + required: false + choices: + - "CORE" + - "SAM" + - "BAM" + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Miscellaneous" + arguments: + - type: "integer" + name: "--max_M_op" + description: "Maximum number of 'M' operations allowed in a CIGAR string. 10 by\ + \ default. Both 'X' and '=' are treated as 'M' and adjacent 'M' operations are\ + \ merged in the CIGAR string.\n" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--verbose" + description: "Output verbose information for debugging, such as un-matched chromosome/contig\ + \ names.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "featureCounts is a read summarization program for counting reads generated\ + \ from either RNA or genomic DNA sequencing experiments by implementing highly efficient\ + \ chromosome hashing and feature blocking techniques. It works with either single\ + \ or paired-end reads and provides a wide range of options appropriate for different\ + \ sequencing applications.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "Read counting" +- "Genomic features" +license: "GPL-3.0" +references: + doi: + - "10.1093/bioinformatics/btt656" +links: + repository: "https://github.com/ShiLab-Bioinformatics/subread" + homepage: "https://subread.sourceforge.net/" + documentation: "https://subread.sourceforge.net/SubreadUsersGuide.pdf" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/subread:2.0.6--he4a0461_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "featureCounts -v 2>&1 | sed 's/featureCounts v\\([0-9.]*\\)/featureCounts:\ + \ \\1/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/featurecounts/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/featurecounts" + executable: "target/nextflow/featurecounts/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/featurecounts/main.nf b/target/nextflow/featurecounts/main.nf new file mode 100644 index 00000000..01a22bfc --- /dev/null +++ b/target/nextflow/featurecounts/main.nf @@ -0,0 +1,4173 @@ +// featurecounts main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "featurecounts", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--annotation", + "alternatives" : [ + "-a" + ], + "description" : "Name of an annotation file. GTF/GFF format by default. See '--format' option for more format information.\n", + "example" : [ + "annotation.gtf" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--input", + "alternatives" : [ + "-i" + ], + "description" : "A list of SAM or BAM format files separated by semi-colon (;). They can be either name or location sorted. Location-sorted paired-end reads are automatically sorted by read names.\n", + "example" : [ + "input_file1.bam" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--counts", + "alternatives" : [ + "-o" + ], + "description" : "Name of output file including read counts in tab delimited format.\n", + "example" : [ + "features.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--summary", + "description" : "Summary statistics of counting results in tab delimited format.\n", + "example" : [ + "summary.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--junctions", + "description" : "Count number of reads supporting each exon-exon junction. Junctions were identified from those exon-spanning reads in the input (containing 'N' in CIGAR string).\n", + "example" : [ + "junctions.txt" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Annotation", + "arguments" : [ + { + "type" : "string", + "name" : "--format", + "alternatives" : [ + "-F" + ], + "description" : "Specify format of the provided annotation file. Acceptable formats include 'GTF' (or compatible GFF format) and 'SAF'. 'GTF' by default. \n", + "example" : [ + "GTF" + ], + "required" : false, + "choices" : [ + "GTF", + "GFF", + "SAF" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--feature_type", + "alternatives" : [ + "-t" + ], + "description" : "Specify feature type(s) in a GTF annotation. If multiple types are provided, they should be separated by ';' with no space in between. 'exon' by default. Rows in the annotation with a matched feature will be extracted and used for read mapping.\n", + "example" : [ + "exon" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--attribute_type", + "alternatives" : [ + "-g" + ], + "description" : "Specify attribute type in GTF annotation. 'gene_id' by default. Meta-features used for read counting will be extracted from annotation using the provided value.\n", + "example" : [ + "gene_id" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--extra_attributes", + "description" : "Extract extra attribute types from the provided GTF annotation and include them in the counting output. These attribute types will not be used to group features. If more than one attribute type is provided they should be separated by semicolon (;).\n", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--chrom_alias", + "alternatives" : [ + "-A" + ], + "description" : "Provide a chromosome name alias file to match chr names in annotation with those in the reads. This should be a two-column comma-delimited text file. Its first column should include chr names in the annotation and its second column should include chr names in the reads. Chr names are case sensitive. No column header should be included in the file.\n", + "example" : [ + "chrom_alias.csv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Level of summarization", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--feature_level", + "alternatives" : [ + "-f" + ], + "description" : "Perform read counting at feature level (eg. counting reads for exons rather than genes).\n", + "direction" : "input" + } + ] + }, + { + "name" : "Overlap between reads and features", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--overlapping", + "alternatives" : [ + "-O" + ], + "description" : "Assign reads to all their overlapping meta-features (or features if '--feature_level' is specified).\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--min_overlap", + "description" : "Minimum number of overlapping bases in a read that is required for read assignment. 1 by default. Number of overlapping bases is counted from both reads if paired end. If a negative value is provided, then a gap of up to specified size will be allowed between read and the feature that the read is assigned to.\n", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--frac_overlap", + "description" : "Minimum fraction of overlapping bases in a read that is required for read assignment. Value should be within range [0,1]. 0 by default. Number of overlapping bases is counted from both reads if paired end. Both this option and '--min_overlap' option need to be satisfied for read assignment.\n", + "example" : [ + 0.0 + ], + "required" : false, + "min" : 0.0, + "max" : 1.0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--frac_overlap_feature", + "description" : "Minimum fraction of overlapping bases in a feature that is required for read assignment. Value should be within range [0,1]. 0 by default.\n", + "example" : [ + 0.0 + ], + "required" : false, + "min" : 0.0, + "max" : 1.0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--largest_overlap", + "description" : "Assign reads to a meta-feature/feature that has the largest number of overlapping bases.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--non_overlap", + "description" : "Maximum number of non-overlapping bases in a read (or a read pair) that is allowed when being assigned to a feature. No limit is set by default.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--non_overlap_feature", + "description" : "Maximum number of non-overlapping bases in a feature that is allowed in read assignment. No limit is set by default.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--read_extension5", + "description" : "Reads are extended upstream by bases from their 5' end.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--read_extension3", + "description" : "Reads are extended upstream by bases from their 3' end.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--read2pos", + "description" : "Reduce reads to their 5' most base or 3' most base. Read counting is then performed based on the single base the read is reduced to.\n", + "required" : false, + "choices" : [ + 3, + 5 + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Multi-mapping reads", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--multi_mapping", + "alternatives" : [ + "-M" + ], + "description" : "Multi-mapping reads will also be counted. For a multi-mapping read, all its reported alignments will be counted. The 'NH' tag in BAM/SAM input is used to detect multi-mapping reads.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Fractional counting", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--fraction", + "description" : "Assign fractional counts to features. This option must be used together with '--multi_mapping' or '--overlapping' or both. When '--multi_mapping' is specified, each reported alignment from a multi-mapping read (identified via 'NH' tag) will carry a fractional count of 1/x, instead of 1 (one), where x is the total number of alignments reported for the same read. When '--overlapping' is specified, each overlapping feature will receive a fractional count of 1/y, where y is the total number of features overlapping with the read. When both '--multi_mapping' and '--overlapping' are specified, each alignment will carry a fractional count of 1/(x*y).\n", + "direction" : "input" + } + ] + }, + { + "name" : "Read filtering", + "arguments" : [ + { + "type" : "integer", + "name" : "--min_map_quality", + "alternatives" : [ + "-Q" + ], + "description" : "The minimum mapping quality score a read must satisfy in order to be counted. For paired-end reads, at least one end should satisfy this criteria. 0 by default.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--split_only", + "description" : "Count split alignments only (ie. alignments with CIGAR string containing 'N'). An example of split alignments is exon-spanning reads in RNA-seq data.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--non_split_only", + "description" : "If specified, only non-split alignments (CIGAR strings do not contain letter 'N') will be counted. All the other alignments will be ignored.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--primary", + "description" : "Count primary alignments only. Primary alignments are identified using bit 0x100 in SAM/BAM FLAG field.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--ignore_dup", + "description" : "Ignore duplicate reads in read counting. Duplicate reads are identified using bit Ox400 in BAM/SAM FLAG field. The whole read pair is ignored if one of the reads is a duplicate read for paired end data.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Strandedness", + "arguments" : [ + { + "type" : "integer", + "name" : "--strand", + "alternatives" : [ + "-s" + ], + "description" : "Perform strand-specific read counting. A single integer value (applied to all input files) should be provided. Possible values include: 0 (unstranded), 1 (stranded) and 2 (reversely stranded). Default value is 0 (ie. unstranded read counting carried out for all input files).\n", + "example" : [ + 0 + ], + "required" : false, + "choices" : [ + 0, + 1, + 2 + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Exon-exon junctions", + "arguments" : [ + { + "type" : "file", + "name" : "--ref_fasta", + "alternatives" : [ + "-G" + ], + "description" : "Provide the name of a FASTA-format file that contains the reference sequences used in read mapping that produced the provided SAM/BAM files.\n", + "example" : [ + "reference.fasta" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Parameters specific to paired end reads", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--paired", + "alternatives" : [ + "-p" + ], + "description" : "Specify that input data contain paired-end reads. To perform fragment counting (ie. counting read pairs), the '--countReadPairs' parameter should also be specified in addition to this parameter.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--count_read_pairs", + "description" : "Count read pairs (fragments) instead of reads. This option is only applicable for paired-end reads.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--both_aligned", + "alternatives" : [ + "-B" + ], + "description" : "Count read pairs (fragments) instead of reads. This option is only applicable for paired-end reads.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--check_pe_dist", + "alternatives" : [ + "-P" + ], + "description" : "Check validity of paired-end distance when counting read pairs. Use '--min_length' and '--max_length' to set thresholds.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--min_length", + "alternatives" : [ + "-d" + ], + "description" : "Minimum fragment/template length, 50 by default.\n", + "example" : [ + 50 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--max_length", + "alternatives" : [ + "-D" + ], + "description" : "Maximum fragment/template length, 600 by default.\n", + "example" : [ + 600 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--same_strand", + "alternatives" : [ + "-C" + ], + "description" : "Do not count read pairs that have their two ends mapping to different chromosomes or mapping to same chromosome but on different strands.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--donotsort", + "description" : "Do not sort reads in BAM/SAM input. Note that reads from the same pair are required to be located next to each other in the input.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Read groups", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--by_read_group", + "description" : "Assign reads by read group. \\"RG\\" tag is required to be present in the input BAM/SAM files.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Long reads", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--long_reads", + "description" : "Count long reads such as Nanopore and PacBio reads. Long read counting can only run in one thread and only reads (not read-pairs) can be counted. There is no limitation on the number of 'M' operations allowed in a CIGAR string in long read counting.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Assignment results for each read", + "arguments" : [ + { + "type" : "file", + "name" : "--detailed_results", + "description" : "Directory to save the detailed assignment results. Use `--detailed_results_format` to determine the format of the detailed results.\n", + "example" : [ + "detailed_results" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--detailed_results_format", + "alternatives" : [ + "-R" + ], + "description" : "Output detailed assignment results for each read or read-pair. Results are saved to a file that is in one of the following formats: CORE, SAM and BAM. See documentaiton for more info about these formats.\n", + "required" : false, + "choices" : [ + "CORE", + "SAM", + "BAM" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Miscellaneous", + "arguments" : [ + { + "type" : "integer", + "name" : "--max_M_op", + "description" : "Maximum number of 'M' operations allowed in a CIGAR string. 10 by default. Both 'X' and '=' are treated as 'M' and adjacent 'M' operations are merged in the CIGAR string.\n", + "example" : [ + 10 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--verbose", + "description" : "Output verbose information for debugging, such as un-matched chromosome/contig names.\n", + "direction" : "input" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "featureCounts is a read summarization program for counting reads generated from either RNA or genomic DNA sequencing experiments by implementing highly efficient chromosome hashing and feature blocking techniques. It works with either single or paired-end reads and provides a wide range of options appropriate for different sequencing applications.\n", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "Read counting", + "Genomic features" + ], + "license" : "GPL-3.0", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btt656" + ] + }, + "links" : { + "repository" : "https://github.com/ShiLab-Bioinformatics/subread", + "homepage" : "https://subread.sourceforge.net/", + "documentation" : "https://subread.sourceforge.net/SubreadUsersGuide.pdf" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/subread:2.0.6--he4a0461_0", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "featureCounts -v 2>&1 | sed 's/featureCounts v\\\\([0-9.]*\\\\)/featureCounts: \\\\1/' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/featurecounts/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/featurecounts", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +set -e + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_ANNOTATION+x} ]; then echo "${VIASH_PAR_ANNOTATION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_annotation='&'#" ; else echo "# par_annotation="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_COUNTS+x} ]; then echo "${VIASH_PAR_COUNTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_counts='&'#" ; else echo "# par_counts="; fi ) +$( if [ ! -z ${VIASH_PAR_SUMMARY+x} ]; then echo "${VIASH_PAR_SUMMARY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_summary='&'#" ; else echo "# par_summary="; fi ) +$( if [ ! -z ${VIASH_PAR_JUNCTIONS+x} ]; then echo "${VIASH_PAR_JUNCTIONS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_junctions='&'#" ; else echo "# par_junctions="; fi ) +$( if [ ! -z ${VIASH_PAR_FORMAT+x} ]; then echo "${VIASH_PAR_FORMAT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_format='&'#" ; else echo "# par_format="; fi ) +$( if [ ! -z ${VIASH_PAR_FEATURE_TYPE+x} ]; then echo "${VIASH_PAR_FEATURE_TYPE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_feature_type='&'#" ; else echo "# par_feature_type="; fi ) +$( if [ ! -z ${VIASH_PAR_ATTRIBUTE_TYPE+x} ]; then echo "${VIASH_PAR_ATTRIBUTE_TYPE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_attribute_type='&'#" ; else echo "# par_attribute_type="; fi ) +$( if [ ! -z ${VIASH_PAR_EXTRA_ATTRIBUTES+x} ]; then echo "${VIASH_PAR_EXTRA_ATTRIBUTES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_extra_attributes='&'#" ; else echo "# par_extra_attributes="; fi ) +$( if [ ! -z ${VIASH_PAR_CHROM_ALIAS+x} ]; then echo "${VIASH_PAR_CHROM_ALIAS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_chrom_alias='&'#" ; else echo "# par_chrom_alias="; fi ) +$( if [ ! -z ${VIASH_PAR_FEATURE_LEVEL+x} ]; then echo "${VIASH_PAR_FEATURE_LEVEL}" | sed "s#'#'\\"'\\"'#g;s#.*#par_feature_level='&'#" ; else echo "# par_feature_level="; fi ) +$( if [ ! -z ${VIASH_PAR_OVERLAPPING+x} ]; then echo "${VIASH_PAR_OVERLAPPING}" | sed "s#'#'\\"'\\"'#g;s#.*#par_overlapping='&'#" ; else echo "# par_overlapping="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_OVERLAP+x} ]; then echo "${VIASH_PAR_MIN_OVERLAP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_overlap='&'#" ; else echo "# par_min_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_FRAC_OVERLAP+x} ]; then echo "${VIASH_PAR_FRAC_OVERLAP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_frac_overlap='&'#" ; else echo "# par_frac_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_FRAC_OVERLAP_FEATURE+x} ]; then echo "${VIASH_PAR_FRAC_OVERLAP_FEATURE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_frac_overlap_feature='&'#" ; else echo "# par_frac_overlap_feature="; fi ) +$( if [ ! -z ${VIASH_PAR_LARGEST_OVERLAP+x} ]; then echo "${VIASH_PAR_LARGEST_OVERLAP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_largest_overlap='&'#" ; else echo "# par_largest_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_NON_OVERLAP+x} ]; then echo "${VIASH_PAR_NON_OVERLAP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_non_overlap='&'#" ; else echo "# par_non_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_NON_OVERLAP_FEATURE+x} ]; then echo "${VIASH_PAR_NON_OVERLAP_FEATURE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_non_overlap_feature='&'#" ; else echo "# par_non_overlap_feature="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_EXTENSION5+x} ]; then echo "${VIASH_PAR_READ_EXTENSION5}" | sed "s#'#'\\"'\\"'#g;s#.*#par_read_extension5='&'#" ; else echo "# par_read_extension5="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_EXTENSION3+x} ]; then echo "${VIASH_PAR_READ_EXTENSION3}" | sed "s#'#'\\"'\\"'#g;s#.*#par_read_extension3='&'#" ; else echo "# par_read_extension3="; fi ) +$( if [ ! -z ${VIASH_PAR_READ2POS+x} ]; then echo "${VIASH_PAR_READ2POS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_read2pos='&'#" ; else echo "# par_read2pos="; fi ) +$( if [ ! -z ${VIASH_PAR_MULTI_MAPPING+x} ]; then echo "${VIASH_PAR_MULTI_MAPPING}" | sed "s#'#'\\"'\\"'#g;s#.*#par_multi_mapping='&'#" ; else echo "# par_multi_mapping="; fi ) +$( if [ ! -z ${VIASH_PAR_FRACTION+x} ]; then echo "${VIASH_PAR_FRACTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fraction='&'#" ; else echo "# par_fraction="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_MAP_QUALITY+x} ]; then echo "${VIASH_PAR_MIN_MAP_QUALITY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_map_quality='&'#" ; else echo "# par_min_map_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLIT_ONLY+x} ]; then echo "${VIASH_PAR_SPLIT_ONLY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_split_only='&'#" ; else echo "# par_split_only="; fi ) +$( if [ ! -z ${VIASH_PAR_NON_SPLIT_ONLY+x} ]; then echo "${VIASH_PAR_NON_SPLIT_ONLY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_non_split_only='&'#" ; else echo "# par_non_split_only="; fi ) +$( if [ ! -z ${VIASH_PAR_PRIMARY+x} ]; then echo "${VIASH_PAR_PRIMARY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_primary='&'#" ; else echo "# par_primary="; fi ) +$( if [ ! -z ${VIASH_PAR_IGNORE_DUP+x} ]; then echo "${VIASH_PAR_IGNORE_DUP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ignore_dup='&'#" ; else echo "# par_ignore_dup="; fi ) +$( if [ ! -z ${VIASH_PAR_STRAND+x} ]; then echo "${VIASH_PAR_STRAND}" | sed "s#'#'\\"'\\"'#g;s#.*#par_strand='&'#" ; else echo "# par_strand="; fi ) +$( if [ ! -z ${VIASH_PAR_REF_FASTA+x} ]; then echo "${VIASH_PAR_REF_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ref_fasta='&'#" ; else echo "# par_ref_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_PAIRED+x} ]; then echo "${VIASH_PAR_PAIRED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_paired='&'#" ; else echo "# par_paired="; fi ) +$( if [ ! -z ${VIASH_PAR_COUNT_READ_PAIRS+x} ]; then echo "${VIASH_PAR_COUNT_READ_PAIRS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_count_read_pairs='&'#" ; else echo "# par_count_read_pairs="; fi ) +$( if [ ! -z ${VIASH_PAR_BOTH_ALIGNED+x} ]; then echo "${VIASH_PAR_BOTH_ALIGNED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_both_aligned='&'#" ; else echo "# par_both_aligned="; fi ) +$( if [ ! -z ${VIASH_PAR_CHECK_PE_DIST+x} ]; then echo "${VIASH_PAR_CHECK_PE_DIST}" | sed "s#'#'\\"'\\"'#g;s#.*#par_check_pe_dist='&'#" ; else echo "# par_check_pe_dist="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_LENGTH+x} ]; then echo "${VIASH_PAR_MIN_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_length='&'#" ; else echo "# par_min_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_LENGTH+x} ]; then echo "${VIASH_PAR_MAX_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_length='&'#" ; else echo "# par_max_length="; fi ) +$( if [ ! -z ${VIASH_PAR_SAME_STRAND+x} ]; then echo "${VIASH_PAR_SAME_STRAND}" | sed "s#'#'\\"'\\"'#g;s#.*#par_same_strand='&'#" ; else echo "# par_same_strand="; fi ) +$( if [ ! -z ${VIASH_PAR_DONOTSORT+x} ]; then echo "${VIASH_PAR_DONOTSORT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_donotsort='&'#" ; else echo "# par_donotsort="; fi ) +$( if [ ! -z ${VIASH_PAR_BY_READ_GROUP+x} ]; then echo "${VIASH_PAR_BY_READ_GROUP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_by_read_group='&'#" ; else echo "# par_by_read_group="; fi ) +$( if [ ! -z ${VIASH_PAR_LONG_READS+x} ]; then echo "${VIASH_PAR_LONG_READS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_long_reads='&'#" ; else echo "# par_long_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_DETAILED_RESULTS+x} ]; then echo "${VIASH_PAR_DETAILED_RESULTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_detailed_results='&'#" ; else echo "# par_detailed_results="; fi ) +$( if [ ! -z ${VIASH_PAR_DETAILED_RESULTS_FORMAT+x} ]; then echo "${VIASH_PAR_DETAILED_RESULTS_FORMAT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_detailed_results_format='&'#" ; else echo "# par_detailed_results_format="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_M_OP+x} ]; then echo "${VIASH_PAR_MAX_M_OP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_M_op='&'#" ; else echo "# par_max_M_op="; fi ) +$( if [ ! -z ${VIASH_PAR_VERBOSE+x} ]; then echo "${VIASH_PAR_VERBOSE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_verbose='&'#" ; else echo "# par_verbose="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# create temporary directory +tmp_dir=\\$(mktemp -d -p "\\$meta_temp_dir" "\\${meta_functionality_name}_XXXXXX") +mkdir -p "\\$tmp_dir/temp" + +# create detailed_results directory if variable is set and directory does not exist +if [[ ! -z "\\$par_detailed_results" ]] && [[ ! -d "\\$par_detailed_results" ]]; then + mkdir -p "\\$par_detailed_results" +fi + +# replace comma with semicolon +par_feature_type=\\$(echo \\$par_feature_type | tr ',' ';') +par_extra_attributes=\\$(echo \\$par_extra_attributes | tr ',' ';') + +# unset flag variables +[[ "\\$par_feature_level" == "false" ]] && unset par_feature_level +[[ "\\$par_overlapping" == "false" ]] && unset par_overlapping +[[ "\\$par_largest_overlap" == "false" ]] && unset par_largest_overlap +[[ "\\$par_multi_mapping" == "false" ]] && unset par_multi_mapping +[[ "\\$par_fraction" == "false" ]] && unset par_fraction +[[ "\\$par_split_only" == "false" ]] && unset par_split_only +[[ "\\$par_non_split_only" == "false" ]] && unset par_non_split_only +[[ "\\$par_primary" == "false" ]] && unset par_primary +[[ "\\$par_ignore_dup" == "false" ]] && unset par_ignore_dup +[[ "\\$par_paired" == "false" ]] && unset par_paired +[[ "\\$par_count_read_pairs" == "false" ]] && unset par_count_read_pairs +[[ "\\$par_both_aligned" == "false" ]] && unset par_both_aligned +[[ "\\$par_check_pe_dist" == "false" ]] && unset par_check_pe_dist +[[ "\\$par_same_strand" == "false" ]] && unset par_same_strand +[[ "\\$par_donotsort" == "false" ]] && unset par_donotsort +[[ "\\$par_by_read_group" == "false" ]] && unset par_by_read_group +[[ "\\$par_long_reads" == "false" ]] && unset par_long_reads +[[ "\\$par_verbose" == "false" ]] && unset par_verbose + +IFS=";" read -ra input <<< \\$par_input + +featureCounts \\\\ + \\${par_format:+-F "\\${par_format}"} \\\\ + \\${par_feature_type:+-t "\\${par_feature_type}"} \\\\ + \\${par_attribute_type:+-g "\\${par_attribute_type}"} \\\\ + \\${par_extra_attributes:+--extraAttributes "\\${extra_attributes}"} \\\\ + \\${par_chrom_alias:+-A "\\${par_chrom_alias}"} \\\\ + \\${par_feature_level:+-f} \\\\ + \\${par_overlapping:+-O} \\\\ + \\${par_min_overlap:+--minOverlap "\\${par_min_overlap}"} \\\\ + \\${par_frac_overlap:+--fracOverlap "\\${par_frac_overlap}"} \\\\ + \\${par_frac_overlap_feature:+--fracOverlapFeature "\\${par_frac_overlap_feature}"} \\\\ + \\${par_largest_overlap:+--largestOverlap} \\\\ + \\${par_non_overlap:+--nonOverlap "\\${par_non_overlap}"} \\\\ + \\${par_non_overlap_feature:+--nonOverlapFeature "\\${par_non_overlap_feature}"} \\\\ + \\${par_read_extension5:+--readExtension5 "\\${par_read_extension5}"} \\\\ + \\${par_read_extension3:+--readExtension3 "\\${par_read_extension3}"} \\\\ + \\${par_read2pos:+--read2pos "\\${par_read2pos}"} \\\\ + \\${par_multi_mapping:+-M} \\\\ + \\${par_fraction:+--fraction} \\\\ + \\${par_min_map_quality:+-Q "\\${par_min_map_quality}"} \\\\ + \\${par_split_only:+--splitOnly} \\\\ + \\${par_non_split_only:+--nonSplitOnly} \\\\ + \\${par_primary:+--primary} \\\\ + \\${par_ignore_dup:+--ignoreDup} \\\\ + \\${par_strand:+-s "\\${par_strand}"} \\\\ + \\${par_junctions:+-J} \\\\ + \\${par_ref_fasta:+-G "\\${par_ref_fasta}"} \\\\ + \\${par_paired:+-p} \\\\ + \\${par_count_read_pairs:+--countReadPairs} \\\\ + \\${par_both_aligned:+-B} \\\\ + \\${par_check_pe_dist:+-P} \\\\ + \\${par_min_length:+-d "\\${par_min_length}"} \\\\ + \\${par_max_length:+-D "\\${par_max_length}"} \\\\ + \\${par_same_strand:+-C} \\\\ + \\${par_donotsort:+--donotsort} \\\\ + \\${par_by_read_group:+--byReadGroup} \\\\ + \\${par_long_reads:+-L} \\\\ + \\${par_detailed_results:+--Rpath "\\${par_detailed_results}"} \\\\ + \\${par_detailed_results_format:+-R "\\${par_detailed_results_format}"} \\\\ + \\${par_max_M_op:+--maxMOp "\\${par_max_M_op}"} \\\\ + \\${par_verbose:+--verbose} \\\\ + \\${meta_cpus:+-T "\\${meta_cpus}"} \\\\ + --tmpDir "\\$tmp_dir/temp" \\\\ + -a "\\$par_annotation" \\\\ + -o "\\$tmp_dir/output.txt" \\\\ + "\\${input[*]}" + +[[ ! -z "\\$par_counts" ]] && mv "\\$tmp_dir/output.txt" "\\$par_counts" +[[ ! -z "\\$par_summary" ]] && mv "\\$tmp_dir/output.txt.summary" "\\$par_summary" +if [[ ! -z "\\$par_junctions" ]] && [[ -e "\\$tmp_dir/output.txt.jcounts" ]]; then + mv "\\$tmp_dir/output.txt.jcounts" "\\$par_junctions" +fi +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/featurecounts", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/featurecounts/nextflow.config b/target/nextflow/featurecounts/nextflow.config new file mode 100644 index 00000000..bcd28a27 --- /dev/null +++ b/target/nextflow/featurecounts/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'featurecounts' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'featureCounts is a read summarization program for counting reads generated from either RNA or genomic DNA sequencing experiments by implementing highly efficient chromosome hashing and feature blocking techniques. It works with either single or paired-end reads and provides a wide range of options appropriate for different sequencing applications.\n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/featurecounts/nextflow_schema.json b/target/nextflow/featurecounts/nextflow_schema.json new file mode 100644 index 00000000..564393b1 --- /dev/null +++ b/target/nextflow/featurecounts/nextflow_schema.json @@ -0,0 +1,726 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "featurecounts", +"description": "featureCounts is a read summarization program for counting reads generated from either RNA or genomic DNA sequencing experiments by implementing highly efficient chromosome hashing and feature blocking techniques. It works with either single or paired-end reads and provides a wide range of options appropriate for different sequencing applications.\n", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "annotation": { + "type": + "string", + "description": "Type: `file`, required, example: `annotation.gtf`. Name of an annotation file", + "help_text": "Type: `file`, required, example: `annotation.gtf`. Name of an annotation file. GTF/GFF format by default. See \u0027--format\u0027 option for more format information.\n" + + } + + + , + "input": { + "type": + "string", + "description": "Type: List of `file`, required, example: `input_file1.bam`, multiple_sep: `\":\"`. A list of SAM or BAM format files separated by semi-colon (;)", + "help_text": "Type: List of `file`, required, example: `input_file1.bam`, multiple_sep: `\":\"`. A list of SAM or BAM format files separated by semi-colon (;). They can be either name or location sorted. Location-sorted paired-end reads are automatically sorted by read names.\n" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "counts": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.counts.tsv`, example: `features.tsv`. Name of output file including read counts in tab delimited format", + "help_text": "Type: `file`, required, default: `$id.$key.counts.tsv`, example: `features.tsv`. Name of output file including read counts in tab delimited format.\n" + , + "default": "$id.$key.counts.tsv" + } + + + , + "summary": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.summary.tsv`, example: `summary.tsv`. Summary statistics of counting results in tab delimited format", + "help_text": "Type: `file`, default: `$id.$key.summary.tsv`, example: `summary.tsv`. Summary statistics of counting results in tab delimited format.\n" + , + "default": "$id.$key.summary.tsv" + } + + + , + "junctions": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.junctions.txt`, example: `junctions.txt`. Count number of reads supporting each exon-exon junction", + "help_text": "Type: `file`, default: `$id.$key.junctions.txt`, example: `junctions.txt`. Count number of reads supporting each exon-exon junction. Junctions were identified from those exon-spanning reads in the input (containing \u0027N\u0027 in CIGAR string).\n" + , + "default": "$id.$key.junctions.txt" + } + + +} +}, + + + "annotation" : { + "title": "Annotation", + "type": "object", + "description": "No description", + "properties": { + + + "format": { + "type": + "string", + "description": "Type: `string`, example: `GTF`, choices: ``GTF`, `GFF`, `SAF``. Specify format of the provided annotation file", + "help_text": "Type: `string`, example: `GTF`, choices: ``GTF`, `GFF`, `SAF``. Specify format of the provided annotation file. Acceptable formats include \u0027GTF\u0027 (or compatible GFF format) and \u0027SAF\u0027. \u0027GTF\u0027 by default. \n", + "enum": ["GTF", "GFF", "SAF"] + + + } + + + , + "feature_type": { + "type": + "string", + "description": "Type: List of `string`, example: `exon`, multiple_sep: `\":\"`. Specify feature type(s) in a GTF annotation", + "help_text": "Type: List of `string`, example: `exon`, multiple_sep: `\":\"`. Specify feature type(s) in a GTF annotation. If multiple types are provided, they should be separated by \u0027;\u0027 with no space in between. \u0027exon\u0027 by default. Rows in the annotation with a matched feature will be extracted and used for read mapping.\n" + + } + + + , + "attribute_type": { + "type": + "string", + "description": "Type: `string`, example: `gene_id`. Specify attribute type in GTF annotation", + "help_text": "Type: `string`, example: `gene_id`. Specify attribute type in GTF annotation. \u0027gene_id\u0027 by default. Meta-features used for read counting will be extracted from annotation using the provided value.\n" + + } + + + , + "extra_attributes": { + "type": + "string", + "description": "Type: List of `string`, multiple_sep: `\":\"`. Extract extra attribute types from the provided GTF annotation and include them in the counting output", + "help_text": "Type: List of `string`, multiple_sep: `\":\"`. Extract extra attribute types from the provided GTF annotation and include them in the counting output. These attribute types will not be used to group features. If more than one attribute type is provided they should be separated by semicolon (;).\n" + + } + + + , + "chrom_alias": { + "type": + "string", + "description": "Type: `file`, example: `chrom_alias.csv`. Provide a chromosome name alias file to match chr names in annotation with those in the reads", + "help_text": "Type: `file`, example: `chrom_alias.csv`. Provide a chromosome name alias file to match chr names in annotation with those in the reads. This should be a two-column comma-delimited text file. Its first column should include chr names in the annotation and its second column should include chr names in the reads. Chr names are case sensitive. No column header should be included in the file.\n" + + } + + +} +}, + + + "level of summarization" : { + "title": "Level of summarization", + "type": "object", + "description": "No description", + "properties": { + + + "feature_level": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Perform read counting at feature level (eg", + "help_text": "Type: `boolean_true`, default: `false`. Perform read counting at feature level (eg. counting reads for exons rather than genes).\n" + , + "default": "False" + } + + +} +}, + + + "overlap between reads and features" : { + "title": "Overlap between reads and features", + "type": "object", + "description": "No description", + "properties": { + + + "overlapping": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Assign reads to all their overlapping meta-features (or features if \u0027--feature_level\u0027 is specified)", + "help_text": "Type: `boolean_true`, default: `false`. Assign reads to all their overlapping meta-features (or features if \u0027--feature_level\u0027 is specified).\n" + , + "default": "False" + } + + + , + "min_overlap": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. Minimum number of overlapping bases in a read that is required for read assignment", + "help_text": "Type: `integer`, example: `1`. Minimum number of overlapping bases in a read that is required for read assignment. 1 by default. Number of overlapping bases is counted from both reads if paired end. If a negative value is provided, then a gap of up to specified size will be allowed between read and the feature that the read is assigned to.\n" + + } + + + , + "frac_overlap": { + "type": + "number", + "description": "Type: `double`, example: `0`. Minimum fraction of overlapping bases in a read that is required for read assignment", + "help_text": "Type: `double`, example: `0`. Minimum fraction of overlapping bases in a read that is required for read assignment. Value should be within range [0,1]. 0 by default. Number of overlapping bases is counted from both reads if paired end. Both this option and \u0027--min_overlap\u0027 option need to be satisfied for read assignment.\n" + + } + + + , + "frac_overlap_feature": { + "type": + "number", + "description": "Type: `double`, example: `0`. Minimum fraction of overlapping bases in a feature that is required for read assignment", + "help_text": "Type: `double`, example: `0`. Minimum fraction of overlapping bases in a feature that is required for read assignment. Value should be within range [0,1]. 0 by default.\n" + + } + + + , + "largest_overlap": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Assign reads to a meta-feature/feature that has the largest number of overlapping bases", + "help_text": "Type: `boolean_true`, default: `false`. Assign reads to a meta-feature/feature that has the largest number of overlapping bases.\n" + , + "default": "False" + } + + + , + "non_overlap": { + "type": + "integer", + "description": "Type: `integer`. Maximum number of non-overlapping bases in a read (or a read pair) that is allowed when being assigned to a feature", + "help_text": "Type: `integer`. Maximum number of non-overlapping bases in a read (or a read pair) that is allowed when being assigned to a feature. No limit is set by default.\n" + + } + + + , + "non_overlap_feature": { + "type": + "integer", + "description": "Type: `integer`. Maximum number of non-overlapping bases in a feature that is allowed in read assignment", + "help_text": "Type: `integer`. Maximum number of non-overlapping bases in a feature that is allowed in read assignment. No limit is set by default.\n" + + } + + + , + "read_extension5": { + "type": + "integer", + "description": "Type: `integer`. Reads are extended upstream by \u003cint\u003e bases from their 5\u0027 end", + "help_text": "Type: `integer`. Reads are extended upstream by \u003cint\u003e bases from their 5\u0027 end.\n" + + } + + + , + "read_extension3": { + "type": + "integer", + "description": "Type: `integer`. Reads are extended upstream by \u003cint\u003e bases from their 3\u0027 end", + "help_text": "Type: `integer`. Reads are extended upstream by \u003cint\u003e bases from their 3\u0027 end.\n" + + } + + + , + "read2pos": { + "type": + "integer", + "description": "Type: `integer`, choices: ``3`, `5``. Reduce reads to their 5\u0027 most base or 3\u0027 most base", + "help_text": "Type: `integer`, choices: ``3`, `5``. Reduce reads to their 5\u0027 most base or 3\u0027 most base. Read counting is then performed based on the single base the read is reduced to.\n", + "enum": [3, 5] + + + } + + +} +}, + + + "multi-mapping reads" : { + "title": "Multi-mapping reads", + "type": "object", + "description": "No description", + "properties": { + + + "multi_mapping": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Multi-mapping reads will also be counted", + "help_text": "Type: `boolean_true`, default: `false`. Multi-mapping reads will also be counted. For a multi-mapping read, all its reported alignments will be counted. The \u0027NH\u0027 tag in BAM/SAM input is used to detect multi-mapping reads.\n" + , + "default": "False" + } + + +} +}, + + + "fractional counting" : { + "title": "Fractional counting", + "type": "object", + "description": "No description", + "properties": { + + + "fraction": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Assign fractional counts to features", + "help_text": "Type: `boolean_true`, default: `false`. Assign fractional counts to features. This option must be used together with \u0027--multi_mapping\u0027 or \u0027--overlapping\u0027 or both. When \u0027--multi_mapping\u0027 is specified, each reported alignment from a multi-mapping read (identified via \u0027NH\u0027 tag) will carry a fractional count of 1/x, instead of 1 (one), where x is the total number of alignments reported for the same read. When \u0027--overlapping\u0027 is specified, each overlapping feature will receive a fractional count of 1/y, where y is the total number of features overlapping with the read. When both \u0027--multi_mapping\u0027 and \u0027--overlapping\u0027 are specified, each alignment will carry a fractional count of 1/(x*y).\n" + , + "default": "False" + } + + +} +}, + + + "read filtering" : { + "title": "Read filtering", + "type": "object", + "description": "No description", + "properties": { + + + "min_map_quality": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. The minimum mapping quality score a read must satisfy in order to be counted", + "help_text": "Type: `integer`, example: `0`. The minimum mapping quality score a read must satisfy in order to be counted. For paired-end reads, at least one end should satisfy this criteria. 0 by default.\n" + + } + + + , + "split_only": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Count split alignments only (ie", + "help_text": "Type: `boolean_true`, default: `false`. Count split alignments only (ie. alignments with CIGAR string containing \u0027N\u0027). An example of split alignments is exon-spanning reads in RNA-seq data.\n" + , + "default": "False" + } + + + , + "non_split_only": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. If specified, only non-split alignments (CIGAR strings do not contain letter \u0027N\u0027) will be counted", + "help_text": "Type: `boolean_true`, default: `false`. If specified, only non-split alignments (CIGAR strings do not contain letter \u0027N\u0027) will be counted. All the other alignments will be ignored.\n" + , + "default": "False" + } + + + , + "primary": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Count primary alignments only", + "help_text": "Type: `boolean_true`, default: `false`. Count primary alignments only. Primary alignments are identified using bit 0x100 in SAM/BAM FLAG field.\n" + , + "default": "False" + } + + + , + "ignore_dup": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Ignore duplicate reads in read counting", + "help_text": "Type: `boolean_true`, default: `false`. Ignore duplicate reads in read counting. Duplicate reads are identified using bit Ox400 in BAM/SAM FLAG field. The whole read pair is ignored if one of the reads is a duplicate read for paired end data.\n" + , + "default": "False" + } + + +} +}, + + + "strandedness" : { + "title": "Strandedness", + "type": "object", + "description": "No description", + "properties": { + + + "strand": { + "type": + "integer", + "description": "Type: `integer`, example: `0`, choices: ``0`, `1`, `2``. Perform strand-specific read counting", + "help_text": "Type: `integer`, example: `0`, choices: ``0`, `1`, `2``. Perform strand-specific read counting. A single integer value (applied to all input files) should be provided. Possible values include: 0 (unstranded), 1 (stranded) and 2 (reversely stranded). Default value is 0 (ie. unstranded read counting carried out for all input files).\n", + "enum": [0, 1, 2] + + + } + + +} +}, + + + "exon-exon junctions" : { + "title": "Exon-exon junctions", + "type": "object", + "description": "No description", + "properties": { + + + "ref_fasta": { + "type": + "string", + "description": "Type: `file`, example: `reference.fasta`. Provide the name of a FASTA-format file that contains the reference sequences used in read mapping that produced the provided SAM/BAM files", + "help_text": "Type: `file`, example: `reference.fasta`. Provide the name of a FASTA-format file that contains the reference sequences used in read mapping that produced the provided SAM/BAM files.\n" + + } + + +} +}, + + + "parameters specific to paired end reads" : { + "title": "Parameters specific to paired end reads", + "type": "object", + "description": "No description", + "properties": { + + + "paired": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Specify that input data contain paired-end reads", + "help_text": "Type: `boolean_true`, default: `false`. Specify that input data contain paired-end reads. To perform fragment counting (ie. counting read pairs), the \u0027--countReadPairs\u0027 parameter should also be specified in addition to this parameter.\n" + , + "default": "False" + } + + + , + "count_read_pairs": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Count read pairs (fragments) instead of reads", + "help_text": "Type: `boolean_true`, default: `false`. Count read pairs (fragments) instead of reads. This option is only applicable for paired-end reads.\n" + , + "default": "False" + } + + + , + "both_aligned": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Count read pairs (fragments) instead of reads", + "help_text": "Type: `boolean_true`, default: `false`. Count read pairs (fragments) instead of reads. This option is only applicable for paired-end reads.\n" + , + "default": "False" + } + + + , + "check_pe_dist": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Check validity of paired-end distance when counting read pairs", + "help_text": "Type: `boolean_true`, default: `false`. Check validity of paired-end distance when counting read pairs. Use \u0027--min_length\u0027 and \u0027--max_length\u0027 to set thresholds.\n" + , + "default": "False" + } + + + , + "min_length": { + "type": + "integer", + "description": "Type: `integer`, example: `50`. Minimum fragment/template length, 50 by default", + "help_text": "Type: `integer`, example: `50`. Minimum fragment/template length, 50 by default.\n" + + } + + + , + "max_length": { + "type": + "integer", + "description": "Type: `integer`, example: `600`. Maximum fragment/template length, 600 by default", + "help_text": "Type: `integer`, example: `600`. Maximum fragment/template length, 600 by default.\n" + + } + + + , + "same_strand": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Do not count read pairs that have their two ends mapping to different chromosomes or mapping to same chromosome but on different strands", + "help_text": "Type: `boolean_true`, default: `false`. Do not count read pairs that have their two ends mapping to different chromosomes or mapping to same chromosome but on different strands.\n" + , + "default": "False" + } + + + , + "donotsort": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Do not sort reads in BAM/SAM input", + "help_text": "Type: `boolean_true`, default: `false`. Do not sort reads in BAM/SAM input. Note that reads from the same pair are required to be located next to each other in the input.\n" + , + "default": "False" + } + + +} +}, + + + "read groups" : { + "title": "Read groups", + "type": "object", + "description": "No description", + "properties": { + + + "by_read_group": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Assign reads by read group", + "help_text": "Type: `boolean_true`, default: `false`. Assign reads by read group. \"RG\" tag is required to be present in the input BAM/SAM files.\n" + , + "default": "False" + } + + +} +}, + + + "long reads" : { + "title": "Long reads", + "type": "object", + "description": "No description", + "properties": { + + + "long_reads": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Count long reads such as Nanopore and PacBio reads", + "help_text": "Type: `boolean_true`, default: `false`. Count long reads such as Nanopore and PacBio reads. Long read counting can only run in one thread and only reads (not read-pairs) can be counted. There is no limitation on the number of \u0027M\u0027 operations allowed in a CIGAR string in long read counting.\n" + , + "default": "False" + } + + +} +}, + + + "assignment results for each read" : { + "title": "Assignment results for each read", + "type": "object", + "description": "No description", + "properties": { + + + "detailed_results": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.detailed_results.detailed_results`, example: `detailed_results/`. Directory to save the detailed assignment results", + "help_text": "Type: `file`, default: `$id.$key.detailed_results.detailed_results`, example: `detailed_results/`. Directory to save the detailed assignment results. Use `--detailed_results_format` to determine the format of the detailed results.\n" + , + "default": "$id.$key.detailed_results.detailed_results" + } + + + , + "detailed_results_format": { + "type": + "string", + "description": "Type: `string`, choices: ``CORE`, `SAM`, `BAM``. Output detailed assignment results for each read or read-pair", + "help_text": "Type: `string`, choices: ``CORE`, `SAM`, `BAM``. Output detailed assignment results for each read or read-pair. Results are saved to a file that is in one of the following formats: CORE, SAM and BAM. See documentaiton for more info about these formats.\n", + "enum": ["CORE", "SAM", "BAM"] + + + } + + +} +}, + + + "miscellaneous" : { + "title": "Miscellaneous", + "type": "object", + "description": "No description", + "properties": { + + + "max_M_op": { + "type": + "integer", + "description": "Type: `integer`, example: `10`. Maximum number of \u0027M\u0027 operations allowed in a CIGAR string", + "help_text": "Type: `integer`, example: `10`. Maximum number of \u0027M\u0027 operations allowed in a CIGAR string. 10 by default. Both \u0027X\u0027 and \u0027=\u0027 are treated as \u0027M\u0027 and adjacent \u0027M\u0027 operations are merged in the CIGAR string.\n" + + } + + + , + "verbose": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output verbose information for debugging, such as un-matched chromosome/contig names", + "help_text": "Type: `boolean_true`, default: `false`. Output verbose information for debugging, such as un-matched chromosome/contig names.\n" + , + "default": "False" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/annotation" + }, + + { + "$ref": "#/definitions/level of summarization" + }, + + { + "$ref": "#/definitions/overlap between reads and features" + }, + + { + "$ref": "#/definitions/multi-mapping reads" + }, + + { + "$ref": "#/definitions/fractional counting" + }, + + { + "$ref": "#/definitions/read filtering" + }, + + { + "$ref": "#/definitions/strandedness" + }, + + { + "$ref": "#/definitions/exon-exon junctions" + }, + + { + "$ref": "#/definitions/parameters specific to paired end reads" + }, + + { + "$ref": "#/definitions/read groups" + }, + + { + "$ref": "#/definitions/long reads" + }, + + { + "$ref": "#/definitions/assignment results for each read" + }, + + { + "$ref": "#/definitions/miscellaneous" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/gffread/.config.vsh.yaml b/target/nextflow/gffread/.config.vsh.yaml new file mode 100644 index 00000000..f7e6cc4f --- /dev/null +++ b/target/nextflow/gffread/.config.vsh.yaml @@ -0,0 +1,697 @@ +name: "gffread" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "A reference file in either the GFF3, GFF2 or GTF format.\n" + info: null + example: + - "annotation.gff" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--chr_mapping" + alternatives: + - "-m" + description: " is a name mapping table for converting reference sequence\ + \ names, \nhaving this 2-column format: .\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--seq_info" + alternatives: + - "-s" + description: " is a tab-delimited file providing this info for\ + \ each of the mapped \nsequences: \ + \ (useful for --description option with \nmRNA/EST/protein mappings).\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--genome" + alternatives: + - "-g" + description: "Full path to a multi-fasta file with the genomic sequences for all\ + \ input mappings, \nOR a directory with single-fasta files (one per genomic\ + \ sequence, with file names \nmatching sequence names).\n" + info: null + example: + - "genome.fa" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--outfile" + alternatives: + - "-o" + description: "Write the output records into .\n" + info: null + default: + - "output.gff" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--force_exons" + description: "Make sure that the lowest level GFF features are considered \"exon\"\ + \ features.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--gene2exon" + description: "For single-line genes not parenting any transcripts, add an exon\ + \ feature spanning \nthe entire gene (treat it as a transcript).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--t_adopt" + description: "Try to find a parent gene overlapping/containing a transcript that\ + \ does not have \nany explicit gene Parent.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--decode" + alternatives: + - "-D" + description: "Decode url encoded characters within attributes.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--merge_exons" + alternatives: + - "-Z" + description: "Merge very close exons into a single exon (when intron size<4).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--junctions" + alternatives: + - "-j" + description: "Output the junctions and the corresponding transcripts.\n" + info: null + direction: "input" + - type: "file" + name: "--spliced_exons" + alternatives: + - "-w" + description: "Write a fasta file with spliced exons for each transcript.\n" + info: null + example: + - "exons.fa" + must_exist: false + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--w_add" + description: "For the --spliced_exons option, extract additional bases both\ + \ upstream and \ndownstream of the transcript boundaries.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--w_nocds" + description: "For --spliced_exons, disable the output of CDS info in the FASTA\ + \ file.\n" + info: null + direction: "input" + - type: "file" + name: "--spliced_cds" + alternatives: + - "-x" + description: "Write a fasta file with spliced CDS for each GFF transcript.\n" + info: null + example: + - "cds.fa" + must_exist: false + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--tr_cds" + alternatives: + - "-y" + description: "Write a protein fasta file with the translation of CDS for each\ + \ record.\n" + info: null + example: + - "tr_cds.fa" + must_exist: false + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--w_coords" + alternatives: + - "-W" + description: "For --spliced_exons, --spliced_cds and -tr_cds options, write in\ + \ the FASTA defline \nall the exon coordinates projected onto the spliced sequence.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--stop_dot" + alternatives: + - "-S" + description: "For --tr_cds option, use '*' instead of '.' as stop codon translation.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--id_version" + alternatives: + - "-L" + description: "Ensembl GTF to GFF3 conversion, adds version to IDs.\n" + info: null + direction: "input" + - type: "string" + name: "--trackname" + alternatives: + - "-t" + description: "Use in the 2nd column of each GFF/GTF output line.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--gtf_output" + alternatives: + - "-T" + description: "Main output will be GTF instead of GFF3.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--bed" + description: "Output records in BED format instead of default GFF3.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--tlf" + description: "Output \"transcript line format\" which is like GFF but with exons\ + \ and CDS related \nfeatures stored as GFF attributes in the transcript feature\ + \ line, like this:\n exoncount=N;exons=;CDSphase=;CDS=\n\ + is a comma-delimited list of exon_start-exon_end coordinates;\n\ + \ is CDS_start:CDS_end coordinates or a list like .\n" + info: null + direction: "input" + - type: "string" + name: "--table" + description: "Output a simple tab delimited format instead of GFF, with columns\ + \ having the values \nof GFF attributes given in ; special pseudo-attributes\ + \ (prefixed by @) are \nrecognized:\n @id, @geneid, @chr, @start, @end, @strand,\ + \ @numexons, @exons, @cds, @covlen, @cdslen\nIf any of --spliced_exons/--tr_cds/--spliced_cds\ + \ FASTA output files are enabled, the \nsame fields (excluding @id) are appended\ + \ to the definition line of corresponding FASTA\nrecords.\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "boolean_true" + name: "--expose_dups" + alternatives: + - "-E" + - "-v" + description: "Expose (warn about) duplicate transcript IDs and other potential\ + \ problems with the \ngiven GFF/GTF records.\n" + info: null + direction: "input" +- name: "Options" + arguments: + - type: "file" + name: "--ids" + description: "Discard records/transcripts if their IDs are not listed in .\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--nids" + description: "Discard records/transcripts if their IDs are listed in .\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--maxintron" + alternatives: + - "-i" + description: "Discard transcripts having an intron larger than .\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--minlen" + alternatives: + - "-l" + description: "Discard transcripts shorter than bases.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--range" + alternatives: + - "-r" + description: "Only show transcripts overlapping coordinate range ..\ + \ (on chromosome/contig \n, strand if provided).\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--strict_range" + alternatives: + - "-R" + description: "For --range option, discard all transcripts that are not fully contained\ + \ within the given \nrange.\n" + info: null + direction: "input" + - type: "string" + name: "--jmatch" + description: "Only output transcripts matching the given junction.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_single_exon" + alternatives: + - "-U" + description: "Discard single-exon transcripts.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--coding" + alternatives: + - "-C" + description: "Coding only: discard mRNAs that have no CDS features.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--nc" + description: "Non-coding only: discard mRNAs that have CDS features.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--ignore_locus" + description: "Discard locus features and attributes found in the input.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--description" + alternatives: + - "-A" + description: "Use the description field from and add it as the\ + \ value for a 'descr' \nattribute to the GFF record.\n" + info: null + direction: "input" +- name: "Sorting" + arguments: + - type: "boolean_true" + name: "--sort_alpha" + description: "Chromosomes (reference sequences) are sorted alphabetically.\n" + info: null + direction: "input" + - type: "file" + name: "--sort_by" + description: "Sort the reference sequences by the order in which their names are\ + \ given in the \n file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Misc options" + arguments: + - type: "boolean_true" + name: "--keep_attrs" + alternatives: + - "-F" + description: "Keep all GFF attributes (for non-exon features).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--keep_exon_attrs" + description: "For -F option, do not attempt to reduce redundant exon/CDS attributes.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_exon_attrs" + alternatives: + - "-G" + description: "Do not keep exon attributes, move them to the transcript feature\ + \ (for GFF3 output).\n" + info: null + direction: "input" + - type: "string" + name: "--attrs" + description: "Only output the GTF/GFF attributes listed in which is\ + \ a comma delimited \nlist of attribute names to.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--keep_genes" + description: "In transcript-only mode (default), also preserve gene records.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--keep_comments" + description: "For GFF3 input/output, try to preserve comments.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--process_other" + alternatives: + - "-O" + description: "process other non-transcript GFF records (by default non-transcript\ + \ records are ignored).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--rm_stop_codons" + alternatives: + - "-V" + description: "Discard any mRNAs with CDS having in-frame stop codons (requires\ + \ --genome).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--adj_cds_start" + alternatives: + - "-H" + description: "For --rm_stop_codons option, check and adjust the starting CDS phase\ + \ if the original phase\nleads to a translation with an in-frame stop codon.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--opposite_strand" + alternatives: + - "-B" + description: "For -V option, single-exon transcripts are also checked on the opposite\ + \ strand (requires \n--genome). \n" + info: null + direction: "input" + - type: "boolean_true" + name: "--coding_status" + alternatives: + - "-P" + description: "Add transcript level GFF attributes about the coding status of each\ + \ transcript, including \npartialness or in-frame stop codons (requires --genome).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--add_hasCDS" + description: "Add a \"hasCDS\" attribute with value \"true\" for transcripts that\ + \ have CDS features. \n" + info: null + direction: "input" + - type: "boolean_true" + name: "--adj_stop" + description: "Stop codon adjustment: enables --coding_status and performs automatic\ + \ adjustment of the CDS stop \ncoordinate if premature or downstream.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--rm_noncanon" + alternatives: + - "-N" + description: "Discard multi-exon mRNAs that have any intron with a non-canonical\ + \ splice site consensus \n(i.e. not GT-AG, GC-AG or AT-AC).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--complete_cds" + alternatives: + - "-J" + description: "Discard any mRNAs that either lack initial START codon or the terminal\ + \ STOP codon, or \nhave an in-frame stop codon (i.e. only print mRNAs with a\ + \ complete CDS).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_pseudo" + description: "Filter out records matching the 'pseudo' keyword.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--in_bed" + description: "Input should be parsed as BED format (automatic if the input filename\ + \ ends with .bed*).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--in_tlf" + description: "Input GFF-like one-line-per-transcript format without exon/CDS features\ + \ (see --tlf option \nbelow); automatic if the input filename ends with .tlf).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--stream" + description: "Fast processing of input GFF/BED transcripts as they are received\ + \ (no sorting, exons must \nbe grouped by transcript in the input data).\n" + info: null + direction: "input" +- name: "Clustering" + arguments: + - type: "boolean_true" + name: "--merge" + alternatives: + - "-M" + description: "Cluster the input transcripts into loci, discarding \"redundant\"\ + \ transcripts (those with \nthe same exact introns and fully contained or equal\ + \ boundaries).\n" + info: null + direction: "input" + - type: "file" + name: "--dupinfo" + alternatives: + - "-d" + description: "For --merge option, write duplication info to file .\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--cluster_only" + description: "Same as --merge but without discarding any of the \"duplicate\"\ + \ transcripts, only create \n\"locus\" features.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--rm_redundant" + alternatives: + - "-K" + description: "For --merge option: also discard as redundant the shorter, fully\ + \ contained transcripts (intron \nchains matching a part of the container).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_boundary" + alternatives: + - "-Q" + description: "For --merge option, no longer require boundary containment when\ + \ assessing redundancy (can be \ncombined with --rm_redundant); only introns\ + \ have to match for multi-exon transcripts, and >=80%\noverlap for single-exon\ + \ transcripts.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_overlap" + alternatives: + - "-Y" + description: "For --merge option, enforce --no_boundary but also discard overlapping\ + \ single-exon transcripts,\neven on the opposite strand (can be combined with\ + \ --rm_redudant).\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Validate, filter, convert and perform various other operations on GFF\ + \ files." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "gff" +- "conversion" +- "validation" +- "filtering" +license: "MIT" +references: + doi: + - "10.12688/f1000research.23297.2" +links: + repository: "https://github.com/gpertea/gffread" + homepage: "https://ccb.jhu.edu/software/stringtie/gff.shtml#gffread" + documentation: "https://ccb.jhu.edu/software/stringtie/gff.shtml#gffread" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/gffread:0.12.7--hdcf5f25_3" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "echo \"gffread: \\\"$(gffread --version 2>&1)\\\"\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/gffread/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/gffread" + executable: "target/nextflow/gffread/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/gffread/main.nf b/target/nextflow/gffread/main.nf new file mode 100644 index 00000000..a2a270ec --- /dev/null +++ b/target/nextflow/gffread/main.nf @@ -0,0 +1,4274 @@ +// gffread main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "gffread", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "A reference file in either the GFF3, GFF2 or GTF format.\n", + "example" : [ + "annotation.gff" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--chr_mapping", + "alternatives" : [ + "-m" + ], + "description" : " is a name mapping table for converting reference sequence names, \nhaving this 2-column format: .\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--seq_info", + "alternatives" : [ + "-s" + ], + "description" : " is a tab-delimited file providing this info for each of the mapped \nsequences: (useful for --description option with \nmRNA/EST/protein mappings).\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--genome", + "alternatives" : [ + "-g" + ], + "description" : "Full path to a multi-fasta file with the genomic sequences for all input mappings, \nOR a directory with single-fasta files (one per genomic sequence, with file names \nmatching sequence names).\n", + "example" : [ + "genome.fa" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--outfile", + "alternatives" : [ + "-o" + ], + "description" : "Write the output records into .\n", + "default" : [ + "output.gff" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--force_exons", + "description" : "Make sure that the lowest level GFF features are considered \\"exon\\" features.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--gene2exon", + "description" : "For single-line genes not parenting any transcripts, add an exon feature spanning \nthe entire gene (treat it as a transcript).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--t_adopt", + "description" : "Try to find a parent gene overlapping/containing a transcript that does not have \nany explicit gene Parent.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--decode", + "alternatives" : [ + "-D" + ], + "description" : "Decode url encoded characters within attributes.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--merge_exons", + "alternatives" : [ + "-Z" + ], + "description" : "Merge very close exons into a single exon (when intron size<4).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--junctions", + "alternatives" : [ + "-j" + ], + "description" : "Output the junctions and the corresponding transcripts.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--spliced_exons", + "alternatives" : [ + "-w" + ], + "description" : "Write a fasta file with spliced exons for each transcript.\n", + "example" : [ + "exons.fa" + ], + "must_exist" : false, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--w_add", + "description" : "For the --spliced_exons option, extract additional bases both upstream and \ndownstream of the transcript boundaries.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--w_nocds", + "description" : "For --spliced_exons, disable the output of CDS info in the FASTA file.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--spliced_cds", + "alternatives" : [ + "-x" + ], + "description" : "Write a fasta file with spliced CDS for each GFF transcript.\n", + "example" : [ + "cds.fa" + ], + "must_exist" : false, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--tr_cds", + "alternatives" : [ + "-y" + ], + "description" : "Write a protein fasta file with the translation of CDS for each record.\n", + "example" : [ + "tr_cds.fa" + ], + "must_exist" : false, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--w_coords", + "alternatives" : [ + "-W" + ], + "description" : "For --spliced_exons, --spliced_cds and -tr_cds options, write in the FASTA defline \nall the exon coordinates projected onto the spliced sequence.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--stop_dot", + "alternatives" : [ + "-S" + ], + "description" : "For --tr_cds option, use '*' instead of '.' as stop codon translation.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--id_version", + "alternatives" : [ + "-L" + ], + "description" : "Ensembl GTF to GFF3 conversion, adds version to IDs.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--trackname", + "alternatives" : [ + "-t" + ], + "description" : "Use in the 2nd column of each GFF/GTF output line.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--gtf_output", + "alternatives" : [ + "-T" + ], + "description" : "Main output will be GTF instead of GFF3.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--bed", + "description" : "Output records in BED format instead of default GFF3.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--tlf", + "description" : "Output \\"transcript line format\\" which is like GFF but with exons and CDS related \nfeatures stored as GFF attributes in the transcript feature line, like this:\n exoncount=N;exons=;CDSphase=;CDS=\n is a comma-delimited list of exon_start-exon_end coordinates;\n is CDS_start:CDS_end coordinates or a list like .\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--table", + "description" : "Output a simple tab delimited format instead of GFF, with columns having the values \nof GFF attributes given in ; special pseudo-attributes (prefixed by @) are \nrecognized:\n @id, @geneid, @chr, @start, @end, @strand, @numexons, @exons, @cds, @covlen, @cdslen\nIf any of --spliced_exons/--tr_cds/--spliced_cds FASTA output files are enabled, the \nsame fields (excluding @id) are appended to the definition line of corresponding FASTA\nrecords.\n", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : "," + }, + { + "type" : "boolean_true", + "name" : "--expose_dups", + "alternatives" : [ + "-E", + "-v" + ], + "description" : "Expose (warn about) duplicate transcript IDs and other potential problems with the \ngiven GFF/GTF records.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Options", + "arguments" : [ + { + "type" : "file", + "name" : "--ids", + "description" : "Discard records/transcripts if their IDs are not listed in .\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--nids", + "description" : "Discard records/transcripts if their IDs are listed in .\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--maxintron", + "alternatives" : [ + "-i" + ], + "description" : "Discard transcripts having an intron larger than .\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--minlen", + "alternatives" : [ + "-l" + ], + "description" : "Discard transcripts shorter than bases.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--range", + "alternatives" : [ + "-r" + ], + "description" : "Only show transcripts overlapping coordinate range .. (on chromosome/contig \n, strand if provided).\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--strict_range", + "alternatives" : [ + "-R" + ], + "description" : "For --range option, discard all transcripts that are not fully contained within the given \nrange.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--jmatch", + "description" : "Only output transcripts matching the given junction.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--no_single_exon", + "alternatives" : [ + "-U" + ], + "description" : "Discard single-exon transcripts.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--coding", + "alternatives" : [ + "-C" + ], + "description" : "Coding only: discard mRNAs that have no CDS features.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--nc", + "description" : "Non-coding only: discard mRNAs that have CDS features.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--ignore_locus", + "description" : "Discard locus features and attributes found in the input.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--description", + "alternatives" : [ + "-A" + ], + "description" : "Use the description field from and add it as the value for a 'descr' \nattribute to the GFF record.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Sorting", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--sort_alpha", + "description" : "Chromosomes (reference sequences) are sorted alphabetically.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--sort_by", + "description" : "Sort the reference sequences by the order in which their names are given in the \n file.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Misc options", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--keep_attrs", + "alternatives" : [ + "-F" + ], + "description" : "Keep all GFF attributes (for non-exon features).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--keep_exon_attrs", + "description" : "For -F option, do not attempt to reduce redundant exon/CDS attributes.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_exon_attrs", + "alternatives" : [ + "-G" + ], + "description" : "Do not keep exon attributes, move them to the transcript feature (for GFF3 output).\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--attrs", + "description" : "Only output the GTF/GFF attributes listed in which is a comma delimited \nlist of attribute names to.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--keep_genes", + "description" : "In transcript-only mode (default), also preserve gene records.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--keep_comments", + "description" : "For GFF3 input/output, try to preserve comments.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--process_other", + "alternatives" : [ + "-O" + ], + "description" : "process other non-transcript GFF records (by default non-transcript records are ignored).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--rm_stop_codons", + "alternatives" : [ + "-V" + ], + "description" : "Discard any mRNAs with CDS having in-frame stop codons (requires --genome).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--adj_cds_start", + "alternatives" : [ + "-H" + ], + "description" : "For --rm_stop_codons option, check and adjust the starting CDS phase if the original phase\nleads to a translation with an in-frame stop codon.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--opposite_strand", + "alternatives" : [ + "-B" + ], + "description" : "For -V option, single-exon transcripts are also checked on the opposite strand (requires \n--genome). \n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--coding_status", + "alternatives" : [ + "-P" + ], + "description" : "Add transcript level GFF attributes about the coding status of each transcript, including \npartialness or in-frame stop codons (requires --genome).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--add_hasCDS", + "description" : "Add a \\"hasCDS\\" attribute with value \\"true\\" for transcripts that have CDS features. \n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--adj_stop", + "description" : "Stop codon adjustment: enables --coding_status and performs automatic adjustment of the CDS stop \ncoordinate if premature or downstream.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--rm_noncanon", + "alternatives" : [ + "-N" + ], + "description" : "Discard multi-exon mRNAs that have any intron with a non-canonical splice site consensus \n(i.e. not GT-AG, GC-AG or AT-AC).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--complete_cds", + "alternatives" : [ + "-J" + ], + "description" : "Discard any mRNAs that either lack initial START codon or the terminal STOP codon, or \nhave an in-frame stop codon (i.e. only print mRNAs with a complete CDS).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_pseudo", + "description" : "Filter out records matching the 'pseudo' keyword.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--in_bed", + "description" : "Input should be parsed as BED format (automatic if the input filename ends with .bed*).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--in_tlf", + "description" : "Input GFF-like one-line-per-transcript format without exon/CDS features (see --tlf option \nbelow); automatic if the input filename ends with .tlf).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--stream", + "description" : "Fast processing of input GFF/BED transcripts as they are received (no sorting, exons must \nbe grouped by transcript in the input data).\n", + "direction" : "input" + } + ] + }, + { + "name" : "Clustering", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--merge", + "alternatives" : [ + "-M" + ], + "description" : "Cluster the input transcripts into loci, discarding \\"redundant\\" transcripts (those with \nthe same exact introns and fully contained or equal boundaries).\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--dupinfo", + "alternatives" : [ + "-d" + ], + "description" : "For --merge option, write duplication info to file .\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--cluster_only", + "description" : "Same as --merge but without discarding any of the \\"duplicate\\" transcripts, only create \n\\"locus\\" features.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--rm_redundant", + "alternatives" : [ + "-K" + ], + "description" : "For --merge option: also discard as redundant the shorter, fully contained transcripts (intron \nchains matching a part of the container).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_boundary", + "alternatives" : [ + "-Q" + ], + "description" : "For --merge option, no longer require boundary containment when assessing redundancy (can be \ncombined with --rm_redundant); only introns have to match for multi-exon transcripts, and >=80%\noverlap for single-exon transcripts.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_overlap", + "alternatives" : [ + "-Y" + ], + "description" : "For --merge option, enforce --no_boundary but also discard overlapping single-exon transcripts,\neven on the opposite strand (can be combined with --rm_redudant).\n", + "direction" : "input" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Validate, filter, convert and perform various other operations on GFF files.", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "gff", + "conversion", + "validation", + "filtering" + ], + "license" : "MIT", + "references" : { + "doi" : [ + "10.12688/f1000research.23297.2" + ] + }, + "links" : { + "repository" : "https://github.com/gpertea/gffread", + "homepage" : "https://ccb.jhu.edu/software/stringtie/gff.shtml#gffread", + "documentation" : "https://ccb.jhu.edu/software/stringtie/gff.shtml#gffread" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/gffread:0.12.7--hdcf5f25_3", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "echo \\"gffread: \\\\\\"$(gffread --version 2>&1)\\\\\\"\\" > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/gffread/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/gffread", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_CHR_MAPPING+x} ]; then echo "${VIASH_PAR_CHR_MAPPING}" | sed "s#'#'\\"'\\"'#g;s#.*#par_chr_mapping='&'#" ; else echo "# par_chr_mapping="; fi ) +$( if [ ! -z ${VIASH_PAR_SEQ_INFO+x} ]; then echo "${VIASH_PAR_SEQ_INFO}" | sed "s#'#'\\"'\\"'#g;s#.*#par_seq_info='&'#" ; else echo "# par_seq_info="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOME+x} ]; then echo "${VIASH_PAR_GENOME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_genome='&'#" ; else echo "# par_genome="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTFILE+x} ]; then echo "${VIASH_PAR_OUTFILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_outfile='&'#" ; else echo "# par_outfile="; fi ) +$( if [ ! -z ${VIASH_PAR_FORCE_EXONS+x} ]; then echo "${VIASH_PAR_FORCE_EXONS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_force_exons='&'#" ; else echo "# par_force_exons="; fi ) +$( if [ ! -z ${VIASH_PAR_GENE2EXON+x} ]; then echo "${VIASH_PAR_GENE2EXON}" | sed "s#'#'\\"'\\"'#g;s#.*#par_gene2exon='&'#" ; else echo "# par_gene2exon="; fi ) +$( if [ ! -z ${VIASH_PAR_T_ADOPT+x} ]; then echo "${VIASH_PAR_T_ADOPT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_t_adopt='&'#" ; else echo "# par_t_adopt="; fi ) +$( if [ ! -z ${VIASH_PAR_DECODE+x} ]; then echo "${VIASH_PAR_DECODE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_decode='&'#" ; else echo "# par_decode="; fi ) +$( if [ ! -z ${VIASH_PAR_MERGE_EXONS+x} ]; then echo "${VIASH_PAR_MERGE_EXONS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_merge_exons='&'#" ; else echo "# par_merge_exons="; fi ) +$( if [ ! -z ${VIASH_PAR_JUNCTIONS+x} ]; then echo "${VIASH_PAR_JUNCTIONS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_junctions='&'#" ; else echo "# par_junctions="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLICED_EXONS+x} ]; then echo "${VIASH_PAR_SPLICED_EXONS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_spliced_exons='&'#" ; else echo "# par_spliced_exons="; fi ) +$( if [ ! -z ${VIASH_PAR_W_ADD+x} ]; then echo "${VIASH_PAR_W_ADD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_w_add='&'#" ; else echo "# par_w_add="; fi ) +$( if [ ! -z ${VIASH_PAR_W_NOCDS+x} ]; then echo "${VIASH_PAR_W_NOCDS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_w_nocds='&'#" ; else echo "# par_w_nocds="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLICED_CDS+x} ]; then echo "${VIASH_PAR_SPLICED_CDS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_spliced_cds='&'#" ; else echo "# par_spliced_cds="; fi ) +$( if [ ! -z ${VIASH_PAR_TR_CDS+x} ]; then echo "${VIASH_PAR_TR_CDS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_tr_cds='&'#" ; else echo "# par_tr_cds="; fi ) +$( if [ ! -z ${VIASH_PAR_W_COORDS+x} ]; then echo "${VIASH_PAR_W_COORDS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_w_coords='&'#" ; else echo "# par_w_coords="; fi ) +$( if [ ! -z ${VIASH_PAR_STOP_DOT+x} ]; then echo "${VIASH_PAR_STOP_DOT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_stop_dot='&'#" ; else echo "# par_stop_dot="; fi ) +$( if [ ! -z ${VIASH_PAR_ID_VERSION+x} ]; then echo "${VIASH_PAR_ID_VERSION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_id_version='&'#" ; else echo "# par_id_version="; fi ) +$( if [ ! -z ${VIASH_PAR_TRACKNAME+x} ]; then echo "${VIASH_PAR_TRACKNAME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_trackname='&'#" ; else echo "# par_trackname="; fi ) +$( if [ ! -z ${VIASH_PAR_GTF_OUTPUT+x} ]; then echo "${VIASH_PAR_GTF_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_gtf_output='&'#" ; else echo "# par_gtf_output="; fi ) +$( if [ ! -z ${VIASH_PAR_BED+x} ]; then echo "${VIASH_PAR_BED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bed='&'#" ; else echo "# par_bed="; fi ) +$( if [ ! -z ${VIASH_PAR_TLF+x} ]; then echo "${VIASH_PAR_TLF}" | sed "s#'#'\\"'\\"'#g;s#.*#par_tlf='&'#" ; else echo "# par_tlf="; fi ) +$( if [ ! -z ${VIASH_PAR_TABLE+x} ]; then echo "${VIASH_PAR_TABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_table='&'#" ; else echo "# par_table="; fi ) +$( if [ ! -z ${VIASH_PAR_EXPOSE_DUPS+x} ]; then echo "${VIASH_PAR_EXPOSE_DUPS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_expose_dups='&'#" ; else echo "# par_expose_dups="; fi ) +$( if [ ! -z ${VIASH_PAR_IDS+x} ]; then echo "${VIASH_PAR_IDS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ids='&'#" ; else echo "# par_ids="; fi ) +$( if [ ! -z ${VIASH_PAR_NIDS+x} ]; then echo "${VIASH_PAR_NIDS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_nids='&'#" ; else echo "# par_nids="; fi ) +$( if [ ! -z ${VIASH_PAR_MAXINTRON+x} ]; then echo "${VIASH_PAR_MAXINTRON}" | sed "s#'#'\\"'\\"'#g;s#.*#par_maxintron='&'#" ; else echo "# par_maxintron="; fi ) +$( if [ ! -z ${VIASH_PAR_MINLEN+x} ]; then echo "${VIASH_PAR_MINLEN}" | sed "s#'#'\\"'\\"'#g;s#.*#par_minlen='&'#" ; else echo "# par_minlen="; fi ) +$( if [ ! -z ${VIASH_PAR_RANGE+x} ]; then echo "${VIASH_PAR_RANGE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_range='&'#" ; else echo "# par_range="; fi ) +$( if [ ! -z ${VIASH_PAR_STRICT_RANGE+x} ]; then echo "${VIASH_PAR_STRICT_RANGE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_strict_range='&'#" ; else echo "# par_strict_range="; fi ) +$( if [ ! -z ${VIASH_PAR_JMATCH+x} ]; then echo "${VIASH_PAR_JMATCH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_jmatch='&'#" ; else echo "# par_jmatch="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_SINGLE_EXON+x} ]; then echo "${VIASH_PAR_NO_SINGLE_EXON}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_single_exon='&'#" ; else echo "# par_no_single_exon="; fi ) +$( if [ ! -z ${VIASH_PAR_CODING+x} ]; then echo "${VIASH_PAR_CODING}" | sed "s#'#'\\"'\\"'#g;s#.*#par_coding='&'#" ; else echo "# par_coding="; fi ) +$( if [ ! -z ${VIASH_PAR_NC+x} ]; then echo "${VIASH_PAR_NC}" | sed "s#'#'\\"'\\"'#g;s#.*#par_nc='&'#" ; else echo "# par_nc="; fi ) +$( if [ ! -z ${VIASH_PAR_IGNORE_LOCUS+x} ]; then echo "${VIASH_PAR_IGNORE_LOCUS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ignore_locus='&'#" ; else echo "# par_ignore_locus="; fi ) +$( if [ ! -z ${VIASH_PAR_DESCRIPTION+x} ]; then echo "${VIASH_PAR_DESCRIPTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_description='&'#" ; else echo "# par_description="; fi ) +$( if [ ! -z ${VIASH_PAR_SORT_ALPHA+x} ]; then echo "${VIASH_PAR_SORT_ALPHA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sort_alpha='&'#" ; else echo "# par_sort_alpha="; fi ) +$( if [ ! -z ${VIASH_PAR_SORT_BY+x} ]; then echo "${VIASH_PAR_SORT_BY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sort_by='&'#" ; else echo "# par_sort_by="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_ATTRS+x} ]; then echo "${VIASH_PAR_KEEP_ATTRS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_keep_attrs='&'#" ; else echo "# par_keep_attrs="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_EXON_ATTRS+x} ]; then echo "${VIASH_PAR_KEEP_EXON_ATTRS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_keep_exon_attrs='&'#" ; else echo "# par_keep_exon_attrs="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_EXON_ATTRS+x} ]; then echo "${VIASH_PAR_NO_EXON_ATTRS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_exon_attrs='&'#" ; else echo "# par_no_exon_attrs="; fi ) +$( if [ ! -z ${VIASH_PAR_ATTRS+x} ]; then echo "${VIASH_PAR_ATTRS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_attrs='&'#" ; else echo "# par_attrs="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_GENES+x} ]; then echo "${VIASH_PAR_KEEP_GENES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_keep_genes='&'#" ; else echo "# par_keep_genes="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_COMMENTS+x} ]; then echo "${VIASH_PAR_KEEP_COMMENTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_keep_comments='&'#" ; else echo "# par_keep_comments="; fi ) +$( if [ ! -z ${VIASH_PAR_PROCESS_OTHER+x} ]; then echo "${VIASH_PAR_PROCESS_OTHER}" | sed "s#'#'\\"'\\"'#g;s#.*#par_process_other='&'#" ; else echo "# par_process_other="; fi ) +$( if [ ! -z ${VIASH_PAR_RM_STOP_CODONS+x} ]; then echo "${VIASH_PAR_RM_STOP_CODONS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_rm_stop_codons='&'#" ; else echo "# par_rm_stop_codons="; fi ) +$( if [ ! -z ${VIASH_PAR_ADJ_CDS_START+x} ]; then echo "${VIASH_PAR_ADJ_CDS_START}" | sed "s#'#'\\"'\\"'#g;s#.*#par_adj_cds_start='&'#" ; else echo "# par_adj_cds_start="; fi ) +$( if [ ! -z ${VIASH_PAR_OPPOSITE_STRAND+x} ]; then echo "${VIASH_PAR_OPPOSITE_STRAND}" | sed "s#'#'\\"'\\"'#g;s#.*#par_opposite_strand='&'#" ; else echo "# par_opposite_strand="; fi ) +$( if [ ! -z ${VIASH_PAR_CODING_STATUS+x} ]; then echo "${VIASH_PAR_CODING_STATUS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_coding_status='&'#" ; else echo "# par_coding_status="; fi ) +$( if [ ! -z ${VIASH_PAR_ADD_HASCDS+x} ]; then echo "${VIASH_PAR_ADD_HASCDS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_add_hasCDS='&'#" ; else echo "# par_add_hasCDS="; fi ) +$( if [ ! -z ${VIASH_PAR_ADJ_STOP+x} ]; then echo "${VIASH_PAR_ADJ_STOP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_adj_stop='&'#" ; else echo "# par_adj_stop="; fi ) +$( if [ ! -z ${VIASH_PAR_RM_NONCANON+x} ]; then echo "${VIASH_PAR_RM_NONCANON}" | sed "s#'#'\\"'\\"'#g;s#.*#par_rm_noncanon='&'#" ; else echo "# par_rm_noncanon="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPLETE_CDS+x} ]; then echo "${VIASH_PAR_COMPLETE_CDS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_complete_cds='&'#" ; else echo "# par_complete_cds="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_PSEUDO+x} ]; then echo "${VIASH_PAR_NO_PSEUDO}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_pseudo='&'#" ; else echo "# par_no_pseudo="; fi ) +$( if [ ! -z ${VIASH_PAR_IN_BED+x} ]; then echo "${VIASH_PAR_IN_BED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_in_bed='&'#" ; else echo "# par_in_bed="; fi ) +$( if [ ! -z ${VIASH_PAR_IN_TLF+x} ]; then echo "${VIASH_PAR_IN_TLF}" | sed "s#'#'\\"'\\"'#g;s#.*#par_in_tlf='&'#" ; else echo "# par_in_tlf="; fi ) +$( if [ ! -z ${VIASH_PAR_STREAM+x} ]; then echo "${VIASH_PAR_STREAM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_stream='&'#" ; else echo "# par_stream="; fi ) +$( if [ ! -z ${VIASH_PAR_MERGE+x} ]; then echo "${VIASH_PAR_MERGE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_merge='&'#" ; else echo "# par_merge="; fi ) +$( if [ ! -z ${VIASH_PAR_DUPINFO+x} ]; then echo "${VIASH_PAR_DUPINFO}" | sed "s#'#'\\"'\\"'#g;s#.*#par_dupinfo='&'#" ; else echo "# par_dupinfo="; fi ) +$( if [ ! -z ${VIASH_PAR_CLUSTER_ONLY+x} ]; then echo "${VIASH_PAR_CLUSTER_ONLY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cluster_only='&'#" ; else echo "# par_cluster_only="; fi ) +$( if [ ! -z ${VIASH_PAR_RM_REDUNDANT+x} ]; then echo "${VIASH_PAR_RM_REDUNDANT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_rm_redundant='&'#" ; else echo "# par_rm_redundant="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_BOUNDARY+x} ]; then echo "${VIASH_PAR_NO_BOUNDARY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_boundary='&'#" ; else echo "# par_no_boundary="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_OVERLAP+x} ]; then echo "${VIASH_PAR_NO_OVERLAP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_overlap='&'#" ; else echo "# par_no_overlap="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# unset flags +[[ "\\$par_coding" == "false" ]] && unset par_coding +[[ "\\$par_strict_range" == "false" ]] && unset par_strict_range +[[ "\\$par_no_single_exon" == "false" ]] && unset par_no_single_exon +[[ "\\$par_no_exon_attrs" == "false" ]] && unset par_no_exon_attrs +[[ "\\$par_nc" == "false" ]] && unset par_nc +[[ "\\$par_ignore_locus" == "false" ]] && unset par_ignore_locus +[[ "\\$par_description" == "false" ]] && unset par_description +[[ "\\$par_sort_alpha" == "false" ]] && unset par_sort_alpha +[[ "\\$par_keep_genes" == "false" ]] && unset par_keep_genes +[[ "\\$par_keep_attrs" == "false" ]] && unset par_keep_attrs +[[ "\\$par_keep_exon_attrs" == "false" ]] && unset par_keep_exon_attrs +[[ "\\$par_keep_comments" == "false" ]] && unset par_keep_comments +[[ "\\$par_process_other" == "false" ]] && unset par_process_other +[[ "\\$par_rm_stop_codons" == "false" ]] && unset par_rm_stop_codons +[[ "\\$par_adj_cds_start" == "false" ]] && unset par_adj_cds_start +[[ "\\$par_opposite_strand" == "false" ]] && unset par_opposite_strand +[[ "\\$par_coding_status" == "false" ]] && unset par_coding_status +[[ "\\$par_add_hasCDS" == "false" ]] && unset par_add_hasCDS +[[ "\\$par_adj_stop" == "false" ]] && unset par_adj_stop +[[ "\\$par_rm_noncanon" == "false" ]] && unset par_rm_noncanon +[[ "\\$par_complete_cds" == "false" ]] && unset par_complete_cds +[[ "\\$par_no_pseudo" == "false" ]] && unset par_no_pseudo +[[ "\\$par_in_bed" == "false" ]] && unset par_in_bed +[[ "\\$par_in_tlf" == "false" ]] && unset par_in_tlf +[[ "\\$par_stream" == "false" ]] && unset par_stream +[[ "\\$par_merge" == "false" ]] && unset par_merge +[[ "\\$par_rm_redundant" == "false" ]] && unset par_rm_redundant +[[ "\\$par_no_boundary" == "false" ]] && unset par_no_boundary +[[ "\\$par_no_overlap" == "false" ]] && unset par_no_overlap +[[ "\\$par_force_exons" == "false" ]] && unset par_force_exons +[[ "\\$par_gene2exon" == "false" ]] && unset par_gene2exon +[[ "\\$par_t_adopt" == "false" ]] && unset par_t_adopt +[[ "\\$par_decode" == "false" ]] && unset par_decode +[[ "\\$par_merge_exons" == "false" ]] && unset par_merge_exons +[[ "\\$par_junctions" == "false" ]] && unset par_junctions +[[ "\\$par_w_nocds" == "false" ]] && unset par_w_nocds +[[ "\\$par_tr_cds" == "false" ]] && unset par_tr_cds +[[ "\\$par_w_coords" == "false" ]] && unset par_w_coords +[[ "\\$par_stop_dot" == "false" ]] && unset par_stop_dot +[[ "\\$par_id_version" == "false" ]] && unset par_id_version +[[ "\\$par_gtf_output" == "false" ]] && unset par_gtf_output +[[ "\\$par_bed" == "false" ]] && unset par_bed +[[ "\\$par_tlf" == "false" ]] && unset par_tlf +[[ "\\$par_expose_dups" == "false" ]] && unset par_expose_dups +[[ "\\$par_cluster_only" == "false" ]] && unset par_cluster_only + + +\\$(which gffread) \\\\ + "\\$par_input" \\\\ + \\${par_chr_mapping:+-m "\\$par_chr_mapping"} \\\\ + \\${par_seq_info:+-s "\\$par_seq_info"} \\\\ + -o "\\$par_outfile" \\\\ + \\${par_force_exons:+--force-exons} \\\\ + \\${par_gene2exon:+--gene2exon} \\\\ + \\${par_t_adopt:+--t-adopt} \\\\ + \\${par_decode:+-D} \\\\ + \\${par_merge_exons:+-Z} \\\\ + \\${par_genome:+-g "\\$par_genome"} \\\\ + \\${par_junctions:+-j} \\\\ + \\${par_spliced_exons:+-w "\\$par_spliced_exons"} \\\\ + \\${par_w_add:+--w-add "\\$par_w_add"} \\\\ + \\${par_w_nocds:+--w-nocds} \\\\ + \\${par_spliced_cds:+-x "\\$par_spliced_cds"} \\\\ + \\${par_tr_cds:+-y "\\$par_tr_cds"} \\\\ + \\${par_w_coords:+-W} \\\\ + \\${par_stop_dot:+-S} \\\\ + \\${par_id_version:+-L} \\\\ + \\${par_trackname:+-t "\\$par_trackname"} \\\\ + \\${par_gtf_output:+-T} \\\\ + \\${par_bed:+--bed} \\\\ + \\${par_tlf:+--tlf} \\\\ + \\${par_table:+--table "\\$par_table"} \\\\ + \\${par_expose_dups:+-E} \\\\ + \\${par_ids:+--ids "\\$par_ids"} \\\\ + \\${par_nids:+--nids "\\$par_nids"} \\\\ + \\${par_maxintron:+-i "\\$par_maxintron"} \\\\ + \\${par_minlen:+-l "\\$par_minlen"} \\\\ + \\${par_range:+-r "\\$par_range"} \\\\ + \\${par_strict_range:+-R} \\\\ + \\${par_jmatch:+--jmatch "\\$par_jmatch"} \\\\ + \\${par_no_single_exon:+-U} \\\\ + \\${par_coding:+-C} \\\\ + \\${par_nc:+--nc} \\\\ + \\${par_ignore_locus:+--ignore-locus} \\\\ + \\${par_description:+-A} \\\\ + \\${par_sort_alpha:+--sort-alpha} \\\\ + \\${par_sort_by:+--sort-by "\\$par_sort_by"} \\\\ + \\${par_keep_attrs:+-F} \\\\ + \\${par_keep_exon_attrs:+--keep-exon-attrs} \\\\ + \\${par_no_exon_attrs:+-G} \\\\ + \\${par_attrs:+--attrs "\\$par_attrs"} \\\\ + \\${par_keep_genes:+--keep-genes} \\\\ + \\${par_keep_comments:+--keep-comments} \\\\ + \\${par_process_other:+-O} \\\\ + \\${par_rm_stop_codons:+-V} \\\\ + \\${par_adj_cds_start:+-H} \\\\ + \\${par_opposite_strand:+-B} \\\\ + \\${par_coding_status:+-P} \\\\ + \\${par_add_hasCDS:+--add-hasCDS} \\\\ + \\${par_adj_stop:+--adj-stop} \\\\ + \\${par_rm_noncanon:+-N} \\\\ + \\${par_complete_cds:+-J} \\\\ + \\${par_no_pseudo:+--no-pseudo} \\\\ + \\${par_in_bed:+--in-bed} \\\\ + \\${par_in_tlf:+--in-tlf} \\\\ + \\${par_stream:+--stream} \\\\ + \\${par_merge:+-M} \\\\ + \\${par_dupinfo:+-d "\\$par_dupinfo"} \\\\ + \\${par_cluster_only:+--cluster-only} \\\\ + \\${par_rm_redundant:+-K} \\\\ + \\${par_no_boundary:+-Q} \\\\ + \\${par_no_overlap:+-Y} +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/gffread", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/gffread/nextflow.config b/target/nextflow/gffread/nextflow.config new file mode 100644 index 00000000..b6a3ab7e --- /dev/null +++ b/target/nextflow/gffread/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'gffread' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Validate, filter, convert and perform various other operations on GFF files.' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/gffread/nextflow_schema.json b/target/nextflow/gffread/nextflow_schema.json new file mode 100644 index 00000000..160fe236 --- /dev/null +++ b/target/nextflow/gffread/nextflow_schema.json @@ -0,0 +1,816 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "gffread", +"description": "Validate, filter, convert and perform various other operations on GFF files.", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`, required, example: `annotation.gff`. A reference file in either the GFF3, GFF2 or GTF format", + "help_text": "Type: `file`, required, example: `annotation.gff`. A reference file in either the GFF3, GFF2 or GTF format.\n" + + } + + + , + "chr_mapping": { + "type": + "string", + "description": "Type: `file`. \u003cchr_replace\u003e is a name mapping table for converting reference sequence names, \nhaving this 2-column format: \u003coriginal_ref_ID\u003e \u003cnew_ref_ID\u003e", + "help_text": "Type: `file`. \u003cchr_replace\u003e is a name mapping table for converting reference sequence names, \nhaving this 2-column format: \u003coriginal_ref_ID\u003e \u003cnew_ref_ID\u003e.\n" + + } + + + , + "seq_info": { + "type": + "string", + "description": "Type: `file`. \u003cseq_info", + "help_text": "Type: `file`. \u003cseq_info.fsize\u003e is a tab-delimited file providing this info for each of the mapped \nsequences: \u003cseq-name\u003e \u003cseq-length\u003e \u003cseq-description\u003e (useful for --description option with \nmRNA/EST/protein mappings).\n" + + } + + + , + "genome": { + "type": + "string", + "description": "Type: `file`, example: `genome.fa`. Full path to a multi-fasta file with the genomic sequences for all input mappings, \nOR a directory with single-fasta files (one per genomic sequence, with file names \nmatching sequence names)", + "help_text": "Type: `file`, example: `genome.fa`. Full path to a multi-fasta file with the genomic sequences for all input mappings, \nOR a directory with single-fasta files (one per genomic sequence, with file names \nmatching sequence names).\n" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "outfile": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.outfile.gff`. Write the output records into \u003coutfile\u003e", + "help_text": "Type: `file`, required, default: `$id.$key.outfile.gff`. Write the output records into \u003coutfile\u003e.\n" + , + "default": "$id.$key.outfile.gff" + } + + + , + "force_exons": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Make sure that the lowest level GFF features are considered \"exon\" features", + "help_text": "Type: `boolean_true`, default: `false`. Make sure that the lowest level GFF features are considered \"exon\" features.\n" + , + "default": "False" + } + + + , + "gene2exon": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For single-line genes not parenting any transcripts, add an exon feature spanning \nthe entire gene (treat it as a transcript)", + "help_text": "Type: `boolean_true`, default: `false`. For single-line genes not parenting any transcripts, add an exon feature spanning \nthe entire gene (treat it as a transcript).\n" + , + "default": "False" + } + + + , + "t_adopt": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Try to find a parent gene overlapping/containing a transcript that does not have \nany explicit gene Parent", + "help_text": "Type: `boolean_true`, default: `false`. Try to find a parent gene overlapping/containing a transcript that does not have \nany explicit gene Parent.\n" + , + "default": "False" + } + + + , + "decode": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Decode url encoded characters within attributes", + "help_text": "Type: `boolean_true`, default: `false`. Decode url encoded characters within attributes.\n" + , + "default": "False" + } + + + , + "merge_exons": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Merge very close exons into a single exon (when intron size\u003c4)", + "help_text": "Type: `boolean_true`, default: `false`. Merge very close exons into a single exon (when intron size\u003c4).\n" + , + "default": "False" + } + + + , + "junctions": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output the junctions and the corresponding transcripts", + "help_text": "Type: `boolean_true`, default: `false`. Output the junctions and the corresponding transcripts.\n" + , + "default": "False" + } + + + , + "spliced_exons": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.spliced_exons.fa`, example: `exons.fa`. Write a fasta file with spliced exons for each transcript", + "help_text": "Type: `file`, default: `$id.$key.spliced_exons.fa`, example: `exons.fa`. Write a fasta file with spliced exons for each transcript.\n" + , + "default": "$id.$key.spliced_exons.fa" + } + + + , + "w_add": { + "type": + "integer", + "description": "Type: `integer`. For the --spliced_exons option, extract additional \u003cN\u003e bases both upstream and \ndownstream of the transcript boundaries", + "help_text": "Type: `integer`. For the --spliced_exons option, extract additional \u003cN\u003e bases both upstream and \ndownstream of the transcript boundaries.\n" + + } + + + , + "w_nocds": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For --spliced_exons, disable the output of CDS info in the FASTA file", + "help_text": "Type: `boolean_true`, default: `false`. For --spliced_exons, disable the output of CDS info in the FASTA file.\n" + , + "default": "False" + } + + + , + "spliced_cds": { + "type": + "string", + "description": "Type: `file`, example: `cds.fa`. Write a fasta file with spliced CDS for each GFF transcript", + "help_text": "Type: `file`, example: `cds.fa`. Write a fasta file with spliced CDS for each GFF transcript.\n" + + } + + + , + "tr_cds": { + "type": + "string", + "description": "Type: `file`, example: `tr_cds.fa`. Write a protein fasta file with the translation of CDS for each record", + "help_text": "Type: `file`, example: `tr_cds.fa`. Write a protein fasta file with the translation of CDS for each record.\n" + + } + + + , + "w_coords": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For --spliced_exons, --spliced_cds and -tr_cds options, write in the FASTA defline \nall the exon coordinates projected onto the spliced sequence", + "help_text": "Type: `boolean_true`, default: `false`. For --spliced_exons, --spliced_cds and -tr_cds options, write in the FASTA defline \nall the exon coordinates projected onto the spliced sequence.\n" + , + "default": "False" + } + + + , + "stop_dot": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For --tr_cds option, use \u0027*\u0027 instead of \u0027", + "help_text": "Type: `boolean_true`, default: `false`. For --tr_cds option, use \u0027*\u0027 instead of \u0027.\u0027 as stop codon translation.\n" + , + "default": "False" + } + + + , + "id_version": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Ensembl GTF to GFF3 conversion, adds version to IDs", + "help_text": "Type: `boolean_true`, default: `false`. Ensembl GTF to GFF3 conversion, adds version to IDs.\n" + , + "default": "False" + } + + + , + "trackname": { + "type": + "string", + "description": "Type: `string`. Use \u003ctrackname\u003e in the 2nd column of each GFF/GTF output line", + "help_text": "Type: `string`. Use \u003ctrackname\u003e in the 2nd column of each GFF/GTF output line.\n" + + } + + + , + "gtf_output": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Main output will be GTF instead of GFF3", + "help_text": "Type: `boolean_true`, default: `false`. Main output will be GTF instead of GFF3.\n" + , + "default": "False" + } + + + , + "bed": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output records in BED format instead of default GFF3", + "help_text": "Type: `boolean_true`, default: `false`. Output records in BED format instead of default GFF3.\n" + , + "default": "False" + } + + + , + "tlf": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output \"transcript line format\" which is like GFF but with exons and CDS related \nfeatures stored as GFF attributes in the transcript feature line, like this:\n exoncount=N;exons=\u003cexons\u003e;CDSphase=\u003cN\u003e;CDS=\u003cCDScoords\u003e\n\u003cexons\u003e is a comma-delimited list of exon_start-exon_end coordinates;\n\u003cCDScoords\u003e is CDS_start:CDS_end coordinates or a list like \u003cexons\u003e", + "help_text": "Type: `boolean_true`, default: `false`. Output \"transcript line format\" which is like GFF but with exons and CDS related \nfeatures stored as GFF attributes in the transcript feature line, like this:\n exoncount=N;exons=\u003cexons\u003e;CDSphase=\u003cN\u003e;CDS=\u003cCDScoords\u003e\n\u003cexons\u003e is a comma-delimited list of exon_start-exon_end coordinates;\n\u003cCDScoords\u003e is CDS_start:CDS_end coordinates or a list like \u003cexons\u003e.\n" + , + "default": "False" + } + + + , + "table": { + "type": + "string", + "description": "Type: List of `string`, multiple_sep: `\",\"`. Output a simple tab delimited format instead of GFF, with columns having the values \nof GFF attributes given in \u003cattrlist\u003e; special pseudo-attributes (prefixed by @) are \nrecognized:\n @id, @geneid, @chr, @start, @end, @strand, @numexons, @exons, @cds, @covlen, @cdslen\nIf any of --spliced_exons/--tr_cds/--spliced_cds FASTA output files are enabled, the \nsame fields (excluding @id) are appended to the definition line of corresponding FASTA\nrecords", + "help_text": "Type: List of `string`, multiple_sep: `\",\"`. Output a simple tab delimited format instead of GFF, with columns having the values \nof GFF attributes given in \u003cattrlist\u003e; special pseudo-attributes (prefixed by @) are \nrecognized:\n @id, @geneid, @chr, @start, @end, @strand, @numexons, @exons, @cds, @covlen, @cdslen\nIf any of --spliced_exons/--tr_cds/--spliced_cds FASTA output files are enabled, the \nsame fields (excluding @id) are appended to the definition line of corresponding FASTA\nrecords.\n" + + } + + + , + "expose_dups": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Expose (warn about) duplicate transcript IDs and other potential problems with the \ngiven GFF/GTF records", + "help_text": "Type: `boolean_true`, default: `false`. Expose (warn about) duplicate transcript IDs and other potential problems with the \ngiven GFF/GTF records.\n" + , + "default": "False" + } + + +} +}, + + + "options" : { + "title": "Options", + "type": "object", + "description": "No description", + "properties": { + + + "ids": { + "type": + "string", + "description": "Type: `file`. Discard records/transcripts if their IDs are not listed in \u003cIDs", + "help_text": "Type: `file`. Discard records/transcripts if their IDs are not listed in \u003cIDs.lst\u003e.\n" + + } + + + , + "nids": { + "type": + "string", + "description": "Type: `file`. Discard records/transcripts if their IDs are listed in \u003cIDs", + "help_text": "Type: `file`. Discard records/transcripts if their IDs are listed in \u003cIDs.lst\u003e.\n" + + } + + + , + "maxintron": { + "type": + "integer", + "description": "Type: `integer`. Discard transcripts having an intron larger than \u003cmaxintron\u003e", + "help_text": "Type: `integer`. Discard transcripts having an intron larger than \u003cmaxintron\u003e.\n" + + } + + + , + "minlen": { + "type": + "integer", + "description": "Type: `integer`. Discard transcripts shorter than \u003cminlen\u003e bases", + "help_text": "Type: `integer`. Discard transcripts shorter than \u003cminlen\u003e bases.\n" + + } + + + , + "range": { + "type": + "string", + "description": "Type: `string`. Only show transcripts overlapping coordinate range \u003cstart\u003e", + "help_text": "Type: `string`. Only show transcripts overlapping coordinate range \u003cstart\u003e..\u003cend\u003e (on chromosome/contig \n\u003cchr\u003e, strand \u003cstrand\u003e if provided).\n" + + } + + + , + "strict_range": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For --range option, discard all transcripts that are not fully contained within the given \nrange", + "help_text": "Type: `boolean_true`, default: `false`. For --range option, discard all transcripts that are not fully contained within the given \nrange.\n" + , + "default": "False" + } + + + , + "jmatch": { + "type": + "string", + "description": "Type: `string`. Only output transcripts matching the given junction", + "help_text": "Type: `string`. Only output transcripts matching the given junction.\n" + + } + + + , + "no_single_exon": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Discard single-exon transcripts", + "help_text": "Type: `boolean_true`, default: `false`. Discard single-exon transcripts.\n" + , + "default": "False" + } + + + , + "coding": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Coding only: discard mRNAs that have no CDS features", + "help_text": "Type: `boolean_true`, default: `false`. Coding only: discard mRNAs that have no CDS features.\n" + , + "default": "False" + } + + + , + "nc": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Non-coding only: discard mRNAs that have CDS features", + "help_text": "Type: `boolean_true`, default: `false`. Non-coding only: discard mRNAs that have CDS features.\n" + , + "default": "False" + } + + + , + "ignore_locus": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Discard locus features and attributes found in the input", + "help_text": "Type: `boolean_true`, default: `false`. Discard locus features and attributes found in the input.\n" + , + "default": "False" + } + + + , + "description": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use the description field from \u003cseq_info", + "help_text": "Type: `boolean_true`, default: `false`. Use the description field from \u003cseq_info.fsize\u003e and add it as the value for a \u0027descr\u0027 \nattribute to the GFF record.\n" + , + "default": "False" + } + + +} +}, + + + "sorting" : { + "title": "Sorting", + "type": "object", + "description": "No description", + "properties": { + + + "sort_alpha": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Chromosomes (reference sequences) are sorted alphabetically", + "help_text": "Type: `boolean_true`, default: `false`. Chromosomes (reference sequences) are sorted alphabetically.\n" + , + "default": "False" + } + + + , + "sort_by": { + "type": + "string", + "description": "Type: `file`. Sort the reference sequences by the order in which their names are given in the \n\u003crefseq", + "help_text": "Type: `file`. Sort the reference sequences by the order in which their names are given in the \n\u003crefseq.lst\u003e file.\n" + + } + + +} +}, + + + "misc options" : { + "title": "Misc options", + "type": "object", + "description": "No description", + "properties": { + + + "keep_attrs": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Keep all GFF attributes (for non-exon features)", + "help_text": "Type: `boolean_true`, default: `false`. Keep all GFF attributes (for non-exon features).\n" + , + "default": "False" + } + + + , + "keep_exon_attrs": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For -F option, do not attempt to reduce redundant exon/CDS attributes", + "help_text": "Type: `boolean_true`, default: `false`. For -F option, do not attempt to reduce redundant exon/CDS attributes.\n" + , + "default": "False" + } + + + , + "no_exon_attrs": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Do not keep exon attributes, move them to the transcript feature (for GFF3 output)", + "help_text": "Type: `boolean_true`, default: `false`. Do not keep exon attributes, move them to the transcript feature (for GFF3 output).\n" + , + "default": "False" + } + + + , + "attrs": { + "type": + "string", + "description": "Type: `string`. Only output the GTF/GFF attributes listed in \u003cattr-list\u003e which is a comma delimited \nlist of attribute names to", + "help_text": "Type: `string`. Only output the GTF/GFF attributes listed in \u003cattr-list\u003e which is a comma delimited \nlist of attribute names to.\n" + + } + + + , + "keep_genes": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. In transcript-only mode (default), also preserve gene records", + "help_text": "Type: `boolean_true`, default: `false`. In transcript-only mode (default), also preserve gene records.\n" + , + "default": "False" + } + + + , + "keep_comments": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For GFF3 input/output, try to preserve comments", + "help_text": "Type: `boolean_true`, default: `false`. For GFF3 input/output, try to preserve comments.\n" + , + "default": "False" + } + + + , + "process_other": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. process other non-transcript GFF records (by default non-transcript records are ignored)", + "help_text": "Type: `boolean_true`, default: `false`. process other non-transcript GFF records (by default non-transcript records are ignored).\n" + , + "default": "False" + } + + + , + "rm_stop_codons": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Discard any mRNAs with CDS having in-frame stop codons (requires --genome)", + "help_text": "Type: `boolean_true`, default: `false`. Discard any mRNAs with CDS having in-frame stop codons (requires --genome).\n" + , + "default": "False" + } + + + , + "adj_cds_start": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For --rm_stop_codons option, check and adjust the starting CDS phase if the original phase\nleads to a translation with an in-frame stop codon", + "help_text": "Type: `boolean_true`, default: `false`. For --rm_stop_codons option, check and adjust the starting CDS phase if the original phase\nleads to a translation with an in-frame stop codon.\n" + , + "default": "False" + } + + + , + "opposite_strand": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For -V option, single-exon transcripts are also checked on the opposite strand (requires \n--genome)", + "help_text": "Type: `boolean_true`, default: `false`. For -V option, single-exon transcripts are also checked on the opposite strand (requires \n--genome). \n" + , + "default": "False" + } + + + , + "coding_status": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Add transcript level GFF attributes about the coding status of each transcript, including \npartialness or in-frame stop codons (requires --genome)", + "help_text": "Type: `boolean_true`, default: `false`. Add transcript level GFF attributes about the coding status of each transcript, including \npartialness or in-frame stop codons (requires --genome).\n" + , + "default": "False" + } + + + , + "add_hasCDS": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Add a \"hasCDS\" attribute with value \"true\" for transcripts that have CDS features", + "help_text": "Type: `boolean_true`, default: `false`. Add a \"hasCDS\" attribute with value \"true\" for transcripts that have CDS features. \n" + , + "default": "False" + } + + + , + "adj_stop": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Stop codon adjustment: enables --coding_status and performs automatic adjustment of the CDS stop \ncoordinate if premature or downstream", + "help_text": "Type: `boolean_true`, default: `false`. Stop codon adjustment: enables --coding_status and performs automatic adjustment of the CDS stop \ncoordinate if premature or downstream.\n" + , + "default": "False" + } + + + , + "rm_noncanon": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Discard multi-exon mRNAs that have any intron with a non-canonical splice site consensus \n(i", + "help_text": "Type: `boolean_true`, default: `false`. Discard multi-exon mRNAs that have any intron with a non-canonical splice site consensus \n(i.e. not GT-AG, GC-AG or AT-AC).\n" + , + "default": "False" + } + + + , + "complete_cds": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Discard any mRNAs that either lack initial START codon or the terminal STOP codon, or \nhave an in-frame stop codon (i", + "help_text": "Type: `boolean_true`, default: `false`. Discard any mRNAs that either lack initial START codon or the terminal STOP codon, or \nhave an in-frame stop codon (i.e. only print mRNAs with a complete CDS).\n" + , + "default": "False" + } + + + , + "no_pseudo": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Filter out records matching the \u0027pseudo\u0027 keyword", + "help_text": "Type: `boolean_true`, default: `false`. Filter out records matching the \u0027pseudo\u0027 keyword.\n" + , + "default": "False" + } + + + , + "in_bed": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Input should be parsed as BED format (automatic if the input filename ends with ", + "help_text": "Type: `boolean_true`, default: `false`. Input should be parsed as BED format (automatic if the input filename ends with .bed*).\n" + , + "default": "False" + } + + + , + "in_tlf": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Input GFF-like one-line-per-transcript format without exon/CDS features (see --tlf option \nbelow); automatic if the input filename ends with ", + "help_text": "Type: `boolean_true`, default: `false`. Input GFF-like one-line-per-transcript format without exon/CDS features (see --tlf option \nbelow); automatic if the input filename ends with .tlf).\n" + , + "default": "False" + } + + + , + "stream": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Fast processing of input GFF/BED transcripts as they are received (no sorting, exons must \nbe grouped by transcript in the input data)", + "help_text": "Type: `boolean_true`, default: `false`. Fast processing of input GFF/BED transcripts as they are received (no sorting, exons must \nbe grouped by transcript in the input data).\n" + , + "default": "False" + } + + +} +}, + + + "clustering" : { + "title": "Clustering", + "type": "object", + "description": "No description", + "properties": { + + + "merge": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Cluster the input transcripts into loci, discarding \"redundant\" transcripts (those with \nthe same exact introns and fully contained or equal boundaries)", + "help_text": "Type: `boolean_true`, default: `false`. Cluster the input transcripts into loci, discarding \"redundant\" transcripts (those with \nthe same exact introns and fully contained or equal boundaries).\n" + , + "default": "False" + } + + + , + "dupinfo": { + "type": + "string", + "description": "Type: `file`. For --merge option, write duplication info to file \u003cdupinfo\u003e", + "help_text": "Type: `file`. For --merge option, write duplication info to file \u003cdupinfo\u003e.\n" + + } + + + , + "cluster_only": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Same as --merge but without discarding any of the \"duplicate\" transcripts, only create \n\"locus\" features", + "help_text": "Type: `boolean_true`, default: `false`. Same as --merge but without discarding any of the \"duplicate\" transcripts, only create \n\"locus\" features.\n" + , + "default": "False" + } + + + , + "rm_redundant": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For --merge option: also discard as redundant the shorter, fully contained transcripts (intron \nchains matching a part of the container)", + "help_text": "Type: `boolean_true`, default: `false`. For --merge option: also discard as redundant the shorter, fully contained transcripts (intron \nchains matching a part of the container).\n" + , + "default": "False" + } + + + , + "no_boundary": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For --merge option, no longer require boundary containment when assessing redundancy (can be \ncombined with --rm_redundant); only introns have to match for multi-exon transcripts, and \u003e=80%\noverlap for single-exon transcripts", + "help_text": "Type: `boolean_true`, default: `false`. For --merge option, no longer require boundary containment when assessing redundancy (can be \ncombined with --rm_redundant); only introns have to match for multi-exon transcripts, and \u003e=80%\noverlap for single-exon transcripts.\n" + , + "default": "False" + } + + + , + "no_overlap": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. For --merge option, enforce --no_boundary but also discard overlapping single-exon transcripts,\neven on the opposite strand (can be combined with --rm_redudant)", + "help_text": "Type: `boolean_true`, default: `false`. For --merge option, enforce --no_boundary but also discard overlapping single-exon transcripts,\neven on the opposite strand (can be combined with --rm_redudant).\n" + , + "default": "False" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/options" + }, + + { + "$ref": "#/definitions/sorting" + }, + + { + "$ref": "#/definitions/misc options" + }, + + { + "$ref": "#/definitions/clustering" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/lofreq/lofreq_call/.config.vsh.yaml b/target/nextflow/lofreq/lofreq_call/.config.vsh.yaml new file mode 100644 index 00000000..c39253ee --- /dev/null +++ b/target/nextflow/lofreq/lofreq_call/.config.vsh.yaml @@ -0,0 +1,515 @@ +name: "lofreq_call" +namespace: "lofreq" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "Input BAM file.\n" + info: null + example: + - "normal.bam" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--input_bai" + description: "Index file for the input BAM file.\n" + info: null + example: + - "normal.bai" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--ref" + alternatives: + - "-f" + description: "Indexed reference fasta file (gzip supported). Default: none.\n" + info: null + example: + - "reference.fasta" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--out" + alternatives: + - "-o" + description: "Vcf output file. Default: stdout.\n" + info: null + example: + - "output.vcf" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Arguments" + arguments: + - type: "string" + name: "--region" + alternatives: + - "-r" + description: "Limit calls to this region (chrom:start-end). Default: none.\n" + info: null + example: + - "chr1:1000-2000" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--bed" + alternatives: + - "-l" + description: "List of positions (chr pos) or regions (BED). Default: none.\n" + info: null + example: + - "regions.bed" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_bq" + alternatives: + - "-q" + description: "Skip any base with baseQ smaller than INT. Default: 6.\n" + info: null + example: + - 6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_alt_bq" + alternatives: + - "-Q" + description: "Skip alternate bases with baseQ smaller than INT. Default: 6.\n" + info: null + example: + - 6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--def_alt_bq" + alternatives: + - "-R" + description: "Overwrite baseQs of alternate bases (that passed bq filter) with\ + \ this value (-1: use median ref-bq; 0: keep). Default: 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_jq" + alternatives: + - "-j" + description: "Skip any base with joinedQ smaller than INT. Default: 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_alt_jq" + alternatives: + - "-J" + description: "Skip alternate bases with joinedQ smaller than INT. Default: 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--def_alt_jq" + alternatives: + - "-K" + description: "Overwrite joinedQs of alternate bases (that passed jq filter) with\ + \ this value (-1: use median ref-bq; 0: keep). Default: 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_baq" + alternatives: + - "-B" + description: "Disable use of base-alignment quality (BAQ).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_idaq" + alternatives: + - "-A" + description: "Don't use IDAQ values (NOT recommended under ANY circumstances other\ + \ than debugging).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--del_baq" + alternatives: + - "-D" + description: "Delete pre-existing BAQ values, i.e. compute even if already present\ + \ in BAM.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_ext_baq" + alternatives: + - "-e" + description: "Use 'normal' BAQ (samtools default) instead of extended BAQ (both\ + \ computed on the fly if not already present in lb tag).\n" + info: null + direction: "input" + - type: "integer" + name: "--min_mq" + alternatives: + - "-m" + description: "Skip reads with mapping quality smaller than INT. Default: 0.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_mq" + alternatives: + - "-M" + description: "Cap mapping quality at INT. Default: 255.\n" + info: null + example: + - 255 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_mq" + alternatives: + - "-N" + description: "Don't merge mapping quality in LoFreq's model.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--call_indels" + description: "Enable indel calls (note: preprocess your file to include indel\ + \ alignment qualities!).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--only_indels" + description: "Only call indels; no SNVs.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--src_qual" + alternatives: + - "-s" + description: "Enable computation of source quality.\n" + info: null + direction: "input" + - type: "file" + name: "--ign_vcf" + alternatives: + - "-S" + description: "Ignore variants in this vcf file for source quality computation.\ + \ Multiple files can be given separated by commas.\n" + info: null + example: + - "variants.vcf" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--def_nm_q" + alternatives: + - "-T" + description: "If >= 0, then replace non-match base qualities with this default\ + \ value. Default: -1.\n" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--sig" + alternatives: + - "-a" + description: "P-Value cutoff / significance level. Default: 0.010000.\n" + info: null + example: + - 0.01 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--bonf" + alternatives: + - "-b" + description: "Bonferroni factor. 'dynamic' (increase per actually performed test)\ + \ or INT. Default: Dynamic.\n" + info: null + example: + - "dynamic" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_cov" + alternatives: + - "-C" + description: "Test only positions having at least this coverage. Default: 1.\n\ + (note: without --no-default-filter default filters (incl. coverage) kick in\ + \ after predictions are done).\n" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_depth" + alternatives: + - "-d" + description: "Cap coverage at this depth. Default: 1000000.\n" + info: null + example: + - 1000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--illumina_13" + description: "Assume the quality is Illumina-1.3-1.7/ASCII+64 encoded.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--use_orphan" + description: "Count anomalous read pairs (i.e. where mate is not aligned properly).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--plp_summary_only" + description: "No variant calling. Just output pileup summary per column.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_default_filter" + description: "Don't run default 'lofreq filter' automatically after calling variants.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--force_overwrite" + description: "Overwrite any existing output.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--verbose" + description: "Be verbose.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--debug" + description: "Enable debugging.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Call variants from a BAM file.\n\nLoFreq* (i.e. LoFreq version 2) is\ + \ a fast and sensitive variant-caller for inferring SNVs and indels from next-generation\ + \ sequencing data. It makes full use of base-call qualities and other sources of\ + \ errors inherent in sequencing (e.g. mapping or base/indel alignment uncertainty),\ + \ which are usually ignored by other methods or only used for filtering.\n\nLoFreq*\ + \ can run on almost any type of aligned sequencing data (e.g. Illumina, IonTorrent\ + \ or Pacbio) since no machine- or sequencing-technology dependent thresholds are\ + \ used. It automatically adapts to changes in coverage and sequencing quality and\ + \ can therefore be applied to a variety of data-sets e.g. viral/quasispecies, bacterial,\ + \ metagenomics or somatic data.\n\nLoFreq* is very sensitive; most notably, it is\ + \ able to predict variants below the average base-call quality (i.e. sequencing\ + \ error rate). Each variant call is assigned a p-value which allows for rigorous\ + \ false positive control. Even though it uses no approximations or heuristics, it\ + \ is very efficient due to several runtime optimizations and also provides a (pseudo-)parallel\ + \ implementation. LoFreq* is generic and fast enough to be applied to high-coverage\ + \ data and large genomes. On a single processor it takes a minute to analyze Dengue\ + \ genome sequencing data with nearly 4000X coverage, roughly one hour to call SNVs\ + \ on a 600X coverage E.coli genome and also roughly an hour to run on a 100X coverage\ + \ human exome dataset.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "variant calling" +- "low frequancy variant calling" +- "lofreq" +- "lofreq/call" +license: "MIT" +references: + doi: + - "10.1093/nar/gks918" +links: + repository: "https://github.com/viash-hub/biobox" + homepage: "https://csb5.github.io/lofreq/" + documentation: "https://csb5.github.io/lofreq/commands/" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/lofreq:2.1.5--py38h794fc9e_10" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "version=$(lofreq version | grep 'version' | sed 's/version: //') && \\\necho\ + \ \"lofreq: $version\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/lofreq/call/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/lofreq/lofreq_call" + executable: "target/nextflow/lofreq/lofreq_call/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/lofreq/lofreq_call/main.nf b/target/nextflow/lofreq/lofreq_call/main.nf new file mode 100644 index 00000000..aad0ddd7 --- /dev/null +++ b/target/nextflow/lofreq/lofreq_call/main.nf @@ -0,0 +1,3986 @@ +// lofreq_call main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "lofreq_call", + "namespace" : "lofreq", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "Input BAM file.\n", + "example" : [ + "normal.bam" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--input_bai", + "description" : "Index file for the input BAM file.\n", + "example" : [ + "normal.bai" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--ref", + "alternatives" : [ + "-f" + ], + "description" : "Indexed reference fasta file (gzip supported). Default: none.\n", + "example" : [ + "reference.fasta" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--out", + "alternatives" : [ + "-o" + ], + "description" : "Vcf output file. Default: stdout.\n", + "example" : [ + "output.vcf" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Arguments", + "arguments" : [ + { + "type" : "string", + "name" : "--region", + "alternatives" : [ + "-r" + ], + "description" : "Limit calls to this region (chrom:start-end). Default: none.\n", + "example" : [ + "chr1:1000-2000" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--bed", + "alternatives" : [ + "-l" + ], + "description" : "List of positions (chr pos) or regions (BED). Default: none.\n", + "example" : [ + "regions.bed" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_bq", + "alternatives" : [ + "-q" + ], + "description" : "Skip any base with baseQ smaller than INT. Default: 6.\n", + "example" : [ + 6 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_alt_bq", + "alternatives" : [ + "-Q" + ], + "description" : "Skip alternate bases with baseQ smaller than INT. Default: 6.\n", + "example" : [ + 6 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--def_alt_bq", + "alternatives" : [ + "-R" + ], + "description" : "Overwrite baseQs of alternate bases (that passed bq filter) with this value (-1: use median ref-bq; 0: keep). Default: 0.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_jq", + "alternatives" : [ + "-j" + ], + "description" : "Skip any base with joinedQ smaller than INT. Default: 0.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_alt_jq", + "alternatives" : [ + "-J" + ], + "description" : "Skip alternate bases with joinedQ smaller than INT. Default: 0.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--def_alt_jq", + "alternatives" : [ + "-K" + ], + "description" : "Overwrite joinedQs of alternate bases (that passed jq filter) with this value (-1: use median ref-bq; 0: keep). Default: 0.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--no_baq", + "alternatives" : [ + "-B" + ], + "description" : "Disable use of base-alignment quality (BAQ).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_idaq", + "alternatives" : [ + "-A" + ], + "description" : "Don't use IDAQ values (NOT recommended under ANY circumstances other than debugging).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--del_baq", + "alternatives" : [ + "-D" + ], + "description" : "Delete pre-existing BAQ values, i.e. compute even if already present in BAM.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_ext_baq", + "alternatives" : [ + "-e" + ], + "description" : "Use 'normal' BAQ (samtools default) instead of extended BAQ (both computed on the fly if not already present in lb tag).\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--min_mq", + "alternatives" : [ + "-m" + ], + "description" : "Skip reads with mapping quality smaller than INT. Default: 0.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--max_mq", + "alternatives" : [ + "-M" + ], + "description" : "Cap mapping quality at INT. Default: 255.\n", + "example" : [ + 255 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--no_mq", + "alternatives" : [ + "-N" + ], + "description" : "Don't merge mapping quality in LoFreq's model.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--call_indels", + "description" : "Enable indel calls (note: preprocess your file to include indel alignment qualities!).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--only_indels", + "description" : "Only call indels; no SNVs.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--src_qual", + "alternatives" : [ + "-s" + ], + "description" : "Enable computation of source quality.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--ign_vcf", + "alternatives" : [ + "-S" + ], + "description" : "Ignore variants in this vcf file for source quality computation. Multiple files can be given separated by commas.\n", + "example" : [ + "variants.vcf" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--def_nm_q", + "alternatives" : [ + "-T" + ], + "description" : "If >= 0, then replace non-match base qualities with this default value. Default: -1.\n", + "example" : [ + -1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--sig", + "alternatives" : [ + "-a" + ], + "description" : "P-Value cutoff / significance level. Default: 0.010000.\n", + "example" : [ + 0.01 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--bonf", + "alternatives" : [ + "-b" + ], + "description" : "Bonferroni factor. 'dynamic' (increase per actually performed test) or INT. Default: Dynamic.\n", + "example" : [ + "dynamic" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_cov", + "alternatives" : [ + "-C" + ], + "description" : "Test only positions having at least this coverage. Default: 1.\n(note: without --no-default-filter default filters (incl. coverage) kick in after predictions are done).\n", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--max_depth", + "alternatives" : [ + "-d" + ], + "description" : "Cap coverage at this depth. Default: 1000000.\n", + "example" : [ + 1000000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--illumina_13", + "description" : "Assume the quality is Illumina-1.3-1.7/ASCII+64 encoded.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--use_orphan", + "description" : "Count anomalous read pairs (i.e. where mate is not aligned properly).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--plp_summary_only", + "description" : "No variant calling. Just output pileup summary per column.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_default_filter", + "description" : "Don't run default 'lofreq filter' automatically after calling variants.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--force_overwrite", + "description" : "Overwrite any existing output.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--verbose", + "description" : "Be verbose.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--debug", + "description" : "Enable debugging.\n", + "direction" : "input" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Call variants from a BAM file.\n\nLoFreq* (i.e. LoFreq version 2) is a fast and sensitive variant-caller for inferring SNVs and indels from next-generation sequencing data. It makes full use of base-call qualities and other sources of errors inherent in sequencing (e.g. mapping or base/indel alignment uncertainty), which are usually ignored by other methods or only used for filtering.\n\nLoFreq* can run on almost any type of aligned sequencing data (e.g. Illumina, IonTorrent or Pacbio) since no machine- or sequencing-technology dependent thresholds are used. It automatically adapts to changes in coverage and sequencing quality and can therefore be applied to a variety of data-sets e.g. viral/quasispecies, bacterial, metagenomics or somatic data.\n\nLoFreq* is very sensitive; most notably, it is able to predict variants below the average base-call quality (i.e. sequencing error rate). Each variant call is assigned a p-value which allows for rigorous false positive control. Even though it uses no approximations or heuristics, it is very efficient due to several runtime optimizations and also provides a (pseudo-)parallel implementation. LoFreq* is generic and fast enough to be applied to high-coverage data and large genomes. On a single processor it takes a minute to analyze Dengue genome sequencing data with nearly 4000X coverage, roughly one hour to call SNVs on a 600X coverage E.coli genome and also roughly an hour to run on a 100X coverage human exome dataset.\n", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "variant calling", + "low frequancy variant calling", + "lofreq", + "lofreq/call" + ], + "license" : "MIT", + "references" : { + "doi" : [ + "10.1093/nar/gks918" + ] + }, + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "homepage" : "https://csb5.github.io/lofreq/", + "documentation" : "https://csb5.github.io/lofreq/commands/" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/lofreq:2.1.5--py38h794fc9e_10", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "version=$(lofreq version | grep 'version' | sed 's/version: //') && \\\\\necho \\"lofreq: $version\\" > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/lofreq/call/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/lofreq/lofreq_call", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_BAI+x} ]; then echo "${VIASH_PAR_INPUT_BAI}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input_bai='&'#" ; else echo "# par_input_bai="; fi ) +$( if [ ! -z ${VIASH_PAR_REF+x} ]; then echo "${VIASH_PAR_REF}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ref='&'#" ; else echo "# par_ref="; fi ) +$( if [ ! -z ${VIASH_PAR_OUT+x} ]; then echo "${VIASH_PAR_OUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_out='&'#" ; else echo "# par_out="; fi ) +$( if [ ! -z ${VIASH_PAR_REGION+x} ]; then echo "${VIASH_PAR_REGION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_region='&'#" ; else echo "# par_region="; fi ) +$( if [ ! -z ${VIASH_PAR_BED+x} ]; then echo "${VIASH_PAR_BED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bed='&'#" ; else echo "# par_bed="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_BQ+x} ]; then echo "${VIASH_PAR_MIN_BQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_bq='&'#" ; else echo "# par_min_bq="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ALT_BQ+x} ]; then echo "${VIASH_PAR_MIN_ALT_BQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_alt_bq='&'#" ; else echo "# par_min_alt_bq="; fi ) +$( if [ ! -z ${VIASH_PAR_DEF_ALT_BQ+x} ]; then echo "${VIASH_PAR_DEF_ALT_BQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_def_alt_bq='&'#" ; else echo "# par_def_alt_bq="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_JQ+x} ]; then echo "${VIASH_PAR_MIN_JQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_jq='&'#" ; else echo "# par_min_jq="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ALT_JQ+x} ]; then echo "${VIASH_PAR_MIN_ALT_JQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_alt_jq='&'#" ; else echo "# par_min_alt_jq="; fi ) +$( if [ ! -z ${VIASH_PAR_DEF_ALT_JQ+x} ]; then echo "${VIASH_PAR_DEF_ALT_JQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_def_alt_jq='&'#" ; else echo "# par_def_alt_jq="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_BAQ+x} ]; then echo "${VIASH_PAR_NO_BAQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_baq='&'#" ; else echo "# par_no_baq="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_IDAQ+x} ]; then echo "${VIASH_PAR_NO_IDAQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_idaq='&'#" ; else echo "# par_no_idaq="; fi ) +$( if [ ! -z ${VIASH_PAR_DEL_BAQ+x} ]; then echo "${VIASH_PAR_DEL_BAQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_del_baq='&'#" ; else echo "# par_del_baq="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_EXT_BAQ+x} ]; then echo "${VIASH_PAR_NO_EXT_BAQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_ext_baq='&'#" ; else echo "# par_no_ext_baq="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_MQ+x} ]; then echo "${VIASH_PAR_MIN_MQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_mq='&'#" ; else echo "# par_min_mq="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_MQ+x} ]; then echo "${VIASH_PAR_MAX_MQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_mq='&'#" ; else echo "# par_max_mq="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_MQ+x} ]; then echo "${VIASH_PAR_NO_MQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_mq='&'#" ; else echo "# par_no_mq="; fi ) +$( if [ ! -z ${VIASH_PAR_CALL_INDELS+x} ]; then echo "${VIASH_PAR_CALL_INDELS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_call_indels='&'#" ; else echo "# par_call_indels="; fi ) +$( if [ ! -z ${VIASH_PAR_ONLY_INDELS+x} ]; then echo "${VIASH_PAR_ONLY_INDELS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_only_indels='&'#" ; else echo "# par_only_indels="; fi ) +$( if [ ! -z ${VIASH_PAR_SRC_QUAL+x} ]; then echo "${VIASH_PAR_SRC_QUAL}" | sed "s#'#'\\"'\\"'#g;s#.*#par_src_qual='&'#" ; else echo "# par_src_qual="; fi ) +$( if [ ! -z ${VIASH_PAR_IGN_VCF+x} ]; then echo "${VIASH_PAR_IGN_VCF}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ign_vcf='&'#" ; else echo "# par_ign_vcf="; fi ) +$( if [ ! -z ${VIASH_PAR_DEF_NM_Q+x} ]; then echo "${VIASH_PAR_DEF_NM_Q}" | sed "s#'#'\\"'\\"'#g;s#.*#par_def_nm_q='&'#" ; else echo "# par_def_nm_q="; fi ) +$( if [ ! -z ${VIASH_PAR_SIG+x} ]; then echo "${VIASH_PAR_SIG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sig='&'#" ; else echo "# par_sig="; fi ) +$( if [ ! -z ${VIASH_PAR_BONF+x} ]; then echo "${VIASH_PAR_BONF}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bonf='&'#" ; else echo "# par_bonf="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_COV+x} ]; then echo "${VIASH_PAR_MIN_COV}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_cov='&'#" ; else echo "# par_min_cov="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_DEPTH+x} ]; then echo "${VIASH_PAR_MAX_DEPTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_depth='&'#" ; else echo "# par_max_depth="; fi ) +$( if [ ! -z ${VIASH_PAR_ILLUMINA_13+x} ]; then echo "${VIASH_PAR_ILLUMINA_13}" | sed "s#'#'\\"'\\"'#g;s#.*#par_illumina_13='&'#" ; else echo "# par_illumina_13="; fi ) +$( if [ ! -z ${VIASH_PAR_USE_ORPHAN+x} ]; then echo "${VIASH_PAR_USE_ORPHAN}" | sed "s#'#'\\"'\\"'#g;s#.*#par_use_orphan='&'#" ; else echo "# par_use_orphan="; fi ) +$( if [ ! -z ${VIASH_PAR_PLP_SUMMARY_ONLY+x} ]; then echo "${VIASH_PAR_PLP_SUMMARY_ONLY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_plp_summary_only='&'#" ; else echo "# par_plp_summary_only="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_DEFAULT_FILTER+x} ]; then echo "${VIASH_PAR_NO_DEFAULT_FILTER}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_default_filter='&'#" ; else echo "# par_no_default_filter="; fi ) +$( if [ ! -z ${VIASH_PAR_FORCE_OVERWRITE+x} ]; then echo "${VIASH_PAR_FORCE_OVERWRITE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_force_overwrite='&'#" ; else echo "# par_force_overwrite="; fi ) +$( if [ ! -z ${VIASH_PAR_VERBOSE+x} ]; then echo "${VIASH_PAR_VERBOSE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_verbose='&'#" ; else echo "# par_verbose="; fi ) +$( if [ ! -z ${VIASH_PAR_DEBUG+x} ]; then echo "${VIASH_PAR_DEBUG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_debug='&'#" ; else echo "# par_debug="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# Unset all parameters that are set to "false" +[[ "\\$par_no_baq" == "false" ]] && unset par_no_baq +[[ "\\$par_no_idaq" == "false" ]] && unset par_no_idaq +[[ "\\$par_del_baq" == "false" ]] && unset par_del_baq +[[ "\\$par_no_ext_baq" == "false" ]] && unset par_no_ext_baq +[[ "\\$par_no_mq" == "false" ]] && unset par_no_mq +[[ "\\$par_call_indels" == "false" ]] && unset par_call_indels +[[ "\\$par_only_indels" == "false" ]] && unset par_only_indels +[[ "\\$par_src_qual" == "false" ]] && unset par_src_qual +[[ "\\$par_illumina_13" == "false" ]] && unset par_illumina_13 +[[ "\\$par_use_orphan" == "false" ]] && unset par_use_orphan +[[ "\\$par_plp_summary_only" == "false" ]] && unset par_plp_summary_only +[[ "\\$par_no_default_filter" == "false" ]] && unset par_no_default_filter +[[ "\\$par_force_overwrite" == "false" ]] && unset par_force_overwrite +[[ "\\$par_verbose" == "false" ]] && unset par_verbose +[[ "\\$par_debug" == "false" ]] && unset par_debug + +# Run lofreq call +lofreq call \\\\ + -f "\\$par_ref" \\\\ + -o "\\$par_out" \\\\ + \\${par_region:+-r "\\${par_region}"} \\\\ + \\${par_bed:+-l "\\${par_bed}"} \\\\ + \\${par_min_bq:+-q "\\${par_min_bq}"} \\\\ + \\${par_min_alt_bq:+-Q "\\${par_min_alt_bq}"} \\\\ + \\${par_def_alt_bq:+-R "\\${par_def_alt_bq}"} \\\\ + \\${par_min_jq:+-j "\\${par_min_jq}"} \\\\ + \\${par_alt_jq:+-K "\\${par_alt_jq}"} \\\\ + \\${par_no_baq:+-B} \\\\ + \\${par_no_idaq:+-A} \\\\ + \\${par_del_baq:+-D} \\\\ + \\${par_no_ext_baq:+-e} \\\\ + \\${par_min_mq:+-m "\\${par_min_mq}"} \\\\ + \\${par_max_mq:+-M "\\${par_max_mq}"} \\\\ + \\${par_no_mq:+-N} \\\\ + \\${par_call_indels:+--call-indels} \\\\ + \\${par_only_indels:+--only-indels} \\\\ + \\${par_src_qual:+-s} \\\\ + \\${par_ign_vcf:+-S "\\${par_ign_vcf}"} \\\\ + \\${par_def_nm_q:+-T "\\${par_def_nm_q}"} \\\\ + \\${par_sig:+-a "\\${par_sig}"} \\\\ + \\${par_bonf:+-b "\\${par_bonf}"} \\\\ + \\${par_min_cov:+-C "\\${par_min_cov}"} \\\\ + \\${par_max_depth:+-d "\\${par_max_depth}"} \\\\ + \\${par_illumina_13:+--illumina-1.3} \\\\ + \\${par_use_orphan:+--use-orphan} \\\\ + \\${par_plp_summary_only:+--plp-summary-only} \\\\ + \\${par_no_default_filter:+--no-default-filter} \\\\ + \\${par_force_overwrite:+--force-overwrite} \\\\ + \\${par_verbose:+--verbose} \\\\ + \\${par_debug:+--debug} \\\\ + "\\$par_input" +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/lofreq/lofreq_call", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/lofreq/lofreq_call/nextflow.config b/target/nextflow/lofreq/lofreq_call/nextflow.config new file mode 100644 index 00000000..6e2bb23f --- /dev/null +++ b/target/nextflow/lofreq/lofreq_call/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'lofreq/lofreq_call' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Call variants from a BAM file.\n\nLoFreq* (i.e. LoFreq version 2) is a fast and sensitive variant-caller for inferring SNVs and indels from next-generation sequencing data. It makes full use of base-call qualities and other sources of errors inherent in sequencing (e.g. mapping or base/indel alignment uncertainty), which are usually ignored by other methods or only used for filtering.\n\nLoFreq* can run on almost any type of aligned sequencing data (e.g. Illumina, IonTorrent or Pacbio) since no machine- or sequencing-technology dependent thresholds are used. It automatically adapts to changes in coverage and sequencing quality and can therefore be applied to a variety of data-sets e.g. viral/quasispecies, bacterial, metagenomics or somatic data.\n\nLoFreq* is very sensitive; most notably, it is able to predict variants below the average base-call quality (i.e. sequencing error rate). Each variant call is assigned a p-value which allows for rigorous false positive control. Even though it uses no approximations or heuristics, it is very efficient due to several runtime optimizations and also provides a (pseudo-)parallel implementation. LoFreq* is generic and fast enough to be applied to high-coverage data and large genomes. On a single processor it takes a minute to analyze Dengue genome sequencing data with nearly 4000X coverage, roughly one hour to call SNVs on a 600X coverage E.coli genome and also roughly an hour to run on a 100X coverage human exome dataset.\n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/lofreq/lofreq_call/nextflow_schema.json b/target/nextflow/lofreq/lofreq_call/nextflow_schema.json new file mode 100644 index 00000000..431e363e --- /dev/null +++ b/target/nextflow/lofreq/lofreq_call/nextflow_schema.json @@ -0,0 +1,454 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "lofreq_call", +"description": "Call variants from a BAM file.\n\nLoFreq* (i.e. LoFreq version 2) is a fast and sensitive variant-caller for inferring SNVs and indels from next-generation sequencing data. It makes full use of base-call qualities and other sources of errors inherent in sequencing (e.g. mapping or base/indel alignment uncertainty), which are usually ignored by other methods or only used for filtering.\n\nLoFreq* can run on almost any type of aligned sequencing data (e.g. Illumina, IonTorrent or Pacbio) since no machine- or sequencing-technology dependent thresholds are used. It automatically adapts to changes in coverage and sequencing quality and can therefore be applied to a variety of data-sets e.g. viral/quasispecies, bacterial, metagenomics or somatic data.\n\nLoFreq* is very sensitive; most notably, it is able to predict variants below the average base-call quality (i.e. sequencing error rate). Each variant call is assigned a p-value which allows for rigorous false positive control. Even though it uses no approximations or heuristics, it is very efficient due to several runtime optimizations and also provides a (pseudo-)parallel implementation. LoFreq* is generic and fast enough to be applied to high-coverage data and large genomes. On a single processor it takes a minute to analyze Dengue genome sequencing data with nearly 4000X coverage, roughly one hour to call SNVs on a 600X coverage E.coli genome and also roughly an hour to run on a 100X coverage human exome dataset.\n", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`, required, example: `normal.bam`. Input BAM file", + "help_text": "Type: `file`, required, example: `normal.bam`. Input BAM file.\n" + + } + + + , + "input_bai": { + "type": + "string", + "description": "Type: `file`, required, example: `normal.bai`. Index file for the input BAM file", + "help_text": "Type: `file`, required, example: `normal.bai`. Index file for the input BAM file.\n" + + } + + + , + "ref": { + "type": + "string", + "description": "Type: `file`, required, example: `reference.fasta`. Indexed reference fasta file (gzip supported)", + "help_text": "Type: `file`, required, example: `reference.fasta`. Indexed reference fasta file (gzip supported). Default: none.\n" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "out": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.out.vcf`, example: `output.vcf`. Vcf output file", + "help_text": "Type: `file`, required, default: `$id.$key.out.vcf`, example: `output.vcf`. Vcf output file. Default: stdout.\n" + , + "default": "$id.$key.out.vcf" + } + + +} +}, + + + "arguments" : { + "title": "Arguments", + "type": "object", + "description": "No description", + "properties": { + + + "region": { + "type": + "string", + "description": "Type: `string`, example: `chr1:1000-2000`. Limit calls to this region (chrom:start-end)", + "help_text": "Type: `string`, example: `chr1:1000-2000`. Limit calls to this region (chrom:start-end). Default: none.\n" + + } + + + , + "bed": { + "type": + "string", + "description": "Type: `file`, example: `regions.bed`. List of positions (chr pos) or regions (BED)", + "help_text": "Type: `file`, example: `regions.bed`. List of positions (chr pos) or regions (BED). Default: none.\n" + + } + + + , + "min_bq": { + "type": + "integer", + "description": "Type: `integer`, example: `6`. Skip any base with baseQ smaller than INT", + "help_text": "Type: `integer`, example: `6`. Skip any base with baseQ smaller than INT. Default: 6.\n" + + } + + + , + "min_alt_bq": { + "type": + "integer", + "description": "Type: `integer`, example: `6`. Skip alternate bases with baseQ smaller than INT", + "help_text": "Type: `integer`, example: `6`. Skip alternate bases with baseQ smaller than INT. Default: 6.\n" + + } + + + , + "def_alt_bq": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Overwrite baseQs of alternate bases (that passed bq filter) with this value (-1: use median ref-bq; 0: keep)", + "help_text": "Type: `integer`, example: `0`. Overwrite baseQs of alternate bases (that passed bq filter) with this value (-1: use median ref-bq; 0: keep). Default: 0.\n" + + } + + + , + "min_jq": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Skip any base with joinedQ smaller than INT", + "help_text": "Type: `integer`, example: `0`. Skip any base with joinedQ smaller than INT. Default: 0.\n" + + } + + + , + "min_alt_jq": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Skip alternate bases with joinedQ smaller than INT", + "help_text": "Type: `integer`, example: `0`. Skip alternate bases with joinedQ smaller than INT. Default: 0.\n" + + } + + + , + "def_alt_jq": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Overwrite joinedQs of alternate bases (that passed jq filter) with this value (-1: use median ref-bq; 0: keep)", + "help_text": "Type: `integer`, example: `0`. Overwrite joinedQs of alternate bases (that passed jq filter) with this value (-1: use median ref-bq; 0: keep). Default: 0.\n" + + } + + + , + "no_baq": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Disable use of base-alignment quality (BAQ)", + "help_text": "Type: `boolean_true`, default: `false`. Disable use of base-alignment quality (BAQ).\n" + , + "default": "False" + } + + + , + "no_idaq": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Don\u0027t use IDAQ values (NOT recommended under ANY circumstances other than debugging)", + "help_text": "Type: `boolean_true`, default: `false`. Don\u0027t use IDAQ values (NOT recommended under ANY circumstances other than debugging).\n" + , + "default": "False" + } + + + , + "del_baq": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Delete pre-existing BAQ values, i", + "help_text": "Type: `boolean_true`, default: `false`. Delete pre-existing BAQ values, i.e. compute even if already present in BAM.\n" + , + "default": "False" + } + + + , + "no_ext_baq": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use \u0027normal\u0027 BAQ (samtools default) instead of extended BAQ (both computed on the fly if not already present in lb tag)", + "help_text": "Type: `boolean_true`, default: `false`. Use \u0027normal\u0027 BAQ (samtools default) instead of extended BAQ (both computed on the fly if not already present in lb tag).\n" + , + "default": "False" + } + + + , + "min_mq": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Skip reads with mapping quality smaller than INT", + "help_text": "Type: `integer`, example: `0`. Skip reads with mapping quality smaller than INT. Default: 0.\n" + + } + + + , + "max_mq": { + "type": + "integer", + "description": "Type: `integer`, example: `255`. Cap mapping quality at INT", + "help_text": "Type: `integer`, example: `255`. Cap mapping quality at INT. Default: 255.\n" + + } + + + , + "no_mq": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Don\u0027t merge mapping quality in LoFreq\u0027s model", + "help_text": "Type: `boolean_true`, default: `false`. Don\u0027t merge mapping quality in LoFreq\u0027s model.\n" + , + "default": "False" + } + + + , + "call_indels": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Enable indel calls (note: preprocess your file to include indel alignment qualities!)", + "help_text": "Type: `boolean_true`, default: `false`. Enable indel calls (note: preprocess your file to include indel alignment qualities!).\n" + , + "default": "False" + } + + + , + "only_indels": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Only call indels; no SNVs", + "help_text": "Type: `boolean_true`, default: `false`. Only call indels; no SNVs.\n" + , + "default": "False" + } + + + , + "src_qual": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Enable computation of source quality", + "help_text": "Type: `boolean_true`, default: `false`. Enable computation of source quality.\n" + , + "default": "False" + } + + + , + "ign_vcf": { + "type": + "string", + "description": "Type: `file`, example: `variants.vcf`. Ignore variants in this vcf file for source quality computation", + "help_text": "Type: `file`, example: `variants.vcf`. Ignore variants in this vcf file for source quality computation. Multiple files can be given separated by commas.\n" + + } + + + , + "def_nm_q": { + "type": + "integer", + "description": "Type: `integer`, example: `-1`. If \u003e= 0, then replace non-match base qualities with this default value", + "help_text": "Type: `integer`, example: `-1`. If \u003e= 0, then replace non-match base qualities with this default value. Default: -1.\n" + + } + + + , + "sig": { + "type": + "number", + "description": "Type: `double`, example: `0.01`. P-Value cutoff / significance level", + "help_text": "Type: `double`, example: `0.01`. P-Value cutoff / significance level. Default: 0.010000.\n" + + } + + + , + "bonf": { + "type": + "string", + "description": "Type: `string`, example: `dynamic`. Bonferroni factor", + "help_text": "Type: `string`, example: `dynamic`. Bonferroni factor. \u0027dynamic\u0027 (increase per actually performed test) or INT. Default: Dynamic.\n" + + } + + + , + "min_cov": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. Test only positions having at least this coverage", + "help_text": "Type: `integer`, example: `1`. Test only positions having at least this coverage. Default: 1.\n(note: without --no-default-filter default filters (incl. coverage) kick in after predictions are done).\n" + + } + + + , + "max_depth": { + "type": + "integer", + "description": "Type: `integer`, example: `1000000`. Cap coverage at this depth", + "help_text": "Type: `integer`, example: `1000000`. Cap coverage at this depth. Default: 1000000.\n" + + } + + + , + "illumina_13": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Assume the quality is Illumina-1", + "help_text": "Type: `boolean_true`, default: `false`. Assume the quality is Illumina-1.3-1.7/ASCII+64 encoded.\n" + , + "default": "False" + } + + + , + "use_orphan": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Count anomalous read pairs (i", + "help_text": "Type: `boolean_true`, default: `false`. Count anomalous read pairs (i.e. where mate is not aligned properly).\n" + , + "default": "False" + } + + + , + "plp_summary_only": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. No variant calling", + "help_text": "Type: `boolean_true`, default: `false`. No variant calling. Just output pileup summary per column.\n" + , + "default": "False" + } + + + , + "no_default_filter": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Don\u0027t run default \u0027lofreq filter\u0027 automatically after calling variants", + "help_text": "Type: `boolean_true`, default: `false`. Don\u0027t run default \u0027lofreq filter\u0027 automatically after calling variants.\n" + , + "default": "False" + } + + + , + "force_overwrite": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Overwrite any existing output", + "help_text": "Type: `boolean_true`, default: `false`. Overwrite any existing output.\n" + , + "default": "False" + } + + + , + "verbose": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Be verbose", + "help_text": "Type: `boolean_true`, default: `false`. Be verbose.\n" + , + "default": "False" + } + + + , + "debug": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Enable debugging", + "help_text": "Type: `boolean_true`, default: `false`. Enable debugging.\n" + , + "default": "False" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/arguments" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/lofreq/lofreq_indelqual/.config.vsh.yaml b/target/nextflow/lofreq/lofreq_indelqual/.config.vsh.yaml new file mode 100644 index 00000000..551cb9c4 --- /dev/null +++ b/target/nextflow/lofreq/lofreq_indelqual/.config.vsh.yaml @@ -0,0 +1,223 @@ +name: "lofreq_indelqual" +namespace: "lofreq" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "Input BAM file.\n" + info: null + example: + - "normal.bam" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--ref" + alternatives: + - "-f" + description: "Reference sequence used for mapping (Only required for --dindel).\n" + info: null + example: + - "reference.fasta" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--out" + alternatives: + - "-o" + description: "Output BAM file.\n" + info: null + example: + - "output.bam" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Arguments" + arguments: + - type: "string" + name: "--uniform" + alternatives: + - "-u" + description: "Add this indel quality uniformly to all bases. Use two comma separated\ + \ values to specify insertion and deletion quality separately. (clashes with\ + \ --dindel).\n" + info: null + example: + - "50,50" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--dindel" + description: "Add Dindel's indel qualities (Illumina specific) (clashes with -u;\ + \ needs --ref).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--verbose" + description: "Be verbose.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Insert indel qualities into BAM file (required for indel predictions).\n\ + \nThe preferred way of inserting indel qualities should be via GATK's BQSR (>=2)\ + \ If that's not possible, use this subcommand.\nThe command has two modes: 'uniform'\ + \ and 'dindel':\n- 'uniform' will assign a given value uniformly, whereas\n- 'dindel'\ + \ will insert indel qualities based on Dindel (PMID 20980555).\nBoth will overwrite\ + \ any existing values.\nDo not realign your BAM file afterwards!\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "bam" +- "indel" +- "qualities" +- "indelqual" +- "lofreq" +- "lofreq/indelqual" +license: "MIT" +references: + doi: + - "10.1093/nar/gks918" +links: + repository: "https://github.com/viash-hub/biobox" + homepage: "https://csb5.github.io/lofreq/" + documentation: "https://csb5.github.io/lofreq/commands/" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/lofreq:2.1.5--py38h794fc9e_10" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "version=$(lofreq version | grep 'version' | sed 's/version: //') && \\\necho\ + \ \"lofreq: $version\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/lofreq/indelqual/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/lofreq/lofreq_indelqual" + executable: "target/nextflow/lofreq/lofreq_indelqual/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/lofreq/lofreq_indelqual/main.nf b/target/nextflow/lofreq/lofreq_indelqual/main.nf new file mode 100644 index 00000000..bc790eca --- /dev/null +++ b/target/nextflow/lofreq/lofreq_indelqual/main.nf @@ -0,0 +1,3580 @@ +// lofreq_indelqual main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "lofreq_indelqual", + "namespace" : "lofreq", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "Input BAM file.\n", + "example" : [ + "normal.bam" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--ref", + "alternatives" : [ + "-f" + ], + "description" : "Reference sequence used for mapping (Only required for --dindel).\n", + "example" : [ + "reference.fasta" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--out", + "alternatives" : [ + "-o" + ], + "description" : "Output BAM file.\n", + "example" : [ + "output.bam" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Arguments", + "arguments" : [ + { + "type" : "string", + "name" : "--uniform", + "alternatives" : [ + "-u" + ], + "description" : "Add this indel quality uniformly to all bases. Use two comma separated values to specify insertion and deletion quality separately. (clashes with --dindel).\n", + "example" : [ + "50,50" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--dindel", + "description" : "Add Dindel's indel qualities (Illumina specific) (clashes with -u; needs --ref).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--verbose", + "description" : "Be verbose.\n", + "direction" : "input" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Insert indel qualities into BAM file (required for indel predictions).\n\nThe preferred way of inserting indel qualities should be via GATK's BQSR (>=2) If that's not possible, use this subcommand.\nThe command has two modes: 'uniform' and 'dindel':\n- 'uniform' will assign a given value uniformly, whereas\n- 'dindel' will insert indel qualities based on Dindel (PMID 20980555).\nBoth will overwrite any existing values.\nDo not realign your BAM file afterwards!\n", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "bam", + "indel", + "qualities", + "indelqual", + "lofreq", + "lofreq/indelqual" + ], + "license" : "MIT", + "references" : { + "doi" : [ + "10.1093/nar/gks918" + ] + }, + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "homepage" : "https://csb5.github.io/lofreq/", + "documentation" : "https://csb5.github.io/lofreq/commands/" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/lofreq:2.1.5--py38h794fc9e_10", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "version=$(lofreq version | grep 'version' | sed 's/version: //') && \\\\\necho \\"lofreq: $version\\" > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/lofreq/indelqual/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/lofreq/lofreq_indelqual", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_REF+x} ]; then echo "${VIASH_PAR_REF}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ref='&'#" ; else echo "# par_ref="; fi ) +$( if [ ! -z ${VIASH_PAR_OUT+x} ]; then echo "${VIASH_PAR_OUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_out='&'#" ; else echo "# par_out="; fi ) +$( if [ ! -z ${VIASH_PAR_UNIFORM+x} ]; then echo "${VIASH_PAR_UNIFORM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_uniform='&'#" ; else echo "# par_uniform="; fi ) +$( if [ ! -z ${VIASH_PAR_DINDEL+x} ]; then echo "${VIASH_PAR_DINDEL}" | sed "s#'#'\\"'\\"'#g;s#.*#par_dindel='&'#" ; else echo "# par_dindel="; fi ) +$( if [ ! -z ${VIASH_PAR_VERBOSE+x} ]; then echo "${VIASH_PAR_VERBOSE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_verbose='&'#" ; else echo "# par_verbose="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +# Unset all parameters that are set to "false" +[[ "\\$par_dindel" == "false" ]] && unset par_dindel +[[ "\\$par_verbose" == "false" ]] && unset par_verbose + +# run lofreq indelqual +lofreq indelqual \\\\ + -o "\\$par_out" \\\\ + \\${par_uniform:+-u "\\${par_uniform}"} \\\\ + \\${par_dindel:+--dindel} \\\\ + \\${par_ref:+-f "\\${par_ref}"} \\\\ + \\${par_verbose:+--verbose} \\\\ + "\\$par_input" +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/lofreq/lofreq_indelqual", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/lofreq/lofreq_indelqual/nextflow.config b/target/nextflow/lofreq/lofreq_indelqual/nextflow.config new file mode 100644 index 00000000..6a92ac91 --- /dev/null +++ b/target/nextflow/lofreq/lofreq_indelqual/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'lofreq/lofreq_indelqual' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Insert indel qualities into BAM file (required for indel predictions).\n\nThe preferred way of inserting indel qualities should be via GATK\'s BQSR (>=2) If that\'s not possible, use this subcommand.\nThe command has two modes: \'uniform\' and \'dindel\':\n- \'uniform\' will assign a given value uniformly, whereas\n- \'dindel\' will insert indel qualities based on Dindel (PMID 20980555).\nBoth will overwrite any existing values.\nDo not realign your BAM file afterwards!\n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/lofreq/lofreq_indelqual/nextflow_schema.json b/target/nextflow/lofreq/lofreq_indelqual/nextflow_schema.json new file mode 100644 index 00000000..fc09ea50 --- /dev/null +++ b/target/nextflow/lofreq/lofreq_indelqual/nextflow_schema.json @@ -0,0 +1,151 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "lofreq_indelqual", +"description": "Insert indel qualities into BAM file (required for indel predictions).\n\nThe preferred way of inserting indel qualities should be via GATK\u0027s BQSR (\u003e=2) If that\u0027s not possible, use this subcommand.\nThe command has two modes: \u0027uniform\u0027 and \u0027dindel\u0027:\n- \u0027uniform\u0027 will assign a given value uniformly, whereas\n- \u0027dindel\u0027 will insert indel qualities based on Dindel (PMID 20980555).\nBoth will overwrite any existing values.\nDo not realign your BAM file afterwards!\n", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`, required, example: `normal.bam`. Input BAM file", + "help_text": "Type: `file`, required, example: `normal.bam`. Input BAM file.\n" + + } + + + , + "ref": { + "type": + "string", + "description": "Type: `file`, example: `reference.fasta`. Reference sequence used for mapping (Only required for --dindel)", + "help_text": "Type: `file`, example: `reference.fasta`. Reference sequence used for mapping (Only required for --dindel).\n" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "out": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.out.bam`, example: `output.bam`. Output BAM file", + "help_text": "Type: `file`, required, default: `$id.$key.out.bam`, example: `output.bam`. Output BAM file.\n" + , + "default": "$id.$key.out.bam" + } + + +} +}, + + + "arguments" : { + "title": "Arguments", + "type": "object", + "description": "No description", + "properties": { + + + "uniform": { + "type": + "string", + "description": "Type: `string`, example: `50,50`. Add this indel quality uniformly to all bases", + "help_text": "Type: `string`, example: `50,50`. Add this indel quality uniformly to all bases. Use two comma separated values to specify insertion and deletion quality separately. (clashes with --dindel).\n" + + } + + + , + "dindel": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Add Dindel\u0027s indel qualities (Illumina specific) (clashes with -u; needs --ref)", + "help_text": "Type: `boolean_true`, default: `false`. Add Dindel\u0027s indel qualities (Illumina specific) (clashes with -u; needs --ref).\n" + , + "default": "False" + } + + + , + "verbose": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Be verbose", + "help_text": "Type: `boolean_true`, default: `false`. Be verbose.\n" + , + "default": "False" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/arguments" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/multiqc/.config.vsh.yaml b/target/nextflow/multiqc/.config.vsh.yaml new file mode 100644 index 00000000..96cb9c7c --- /dev/null +++ b/target/nextflow/multiqc/.config.vsh.yaml @@ -0,0 +1,464 @@ +name: "multiqc" +version: "main" +argument_groups: +- name: "Input" + arguments: + - type: "file" + name: "--input" + description: "File paths to be searched for analysis results to be included in\ + \ the report.\n" + info: null + example: + - "data/results" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Ouput" + arguments: + - type: "file" + name: "--output_report" + description: "Filepath of the generated report.\n" + info: null + example: + - "multiqc_report.html" + must_exist: false + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--output_data" + description: "Output directory for parsed data files. If not provided, parsed\ + \ data will not be published.\n" + info: null + example: + - "multiqc_data" + must_exist: false + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--output_plots" + description: "Output directory for generated plots. If not provided, plots will\ + \ not be published.\n" + info: null + example: + - "multiqc_plots" + must_exist: false + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Modules and analyses to run" + arguments: + - type: "string" + name: "--include_modules" + description: "Use only these module" + info: null + example: + - "fastqc,cutadapt" + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "string" + name: "--exclude_modules" + description: "Do not use only these modules" + info: null + example: + - "fastqc,cutadapt" + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "string" + name: "--ignore_analysis" + info: null + example: + - "run_one/*,run_two/*" + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "string" + name: "--ignore_samples" + info: null + example: + - "sample_1*,sample_3*" + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "boolean_true" + name: "--ignore_symlinks" + description: "Ignore symlinked directories and files" + info: null + direction: "input" +- name: "Sample name handling" + arguments: + - type: "boolean_true" + name: "--dirs" + description: "Prepend directory to sample names to avoid clashing filenames" + info: null + direction: "input" + - type: "integer" + name: "--dirs_depth" + description: "Prepend n directories to sample names. Negative number to take from\ + \ start of path." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--full_names" + description: "Do not clean the sample names (leave as full file name)" + info: null + direction: "input" + - type: "boolean_true" + name: "--fn_as_s_name" + description: "Use the log filename as the sample name" + info: null + direction: "input" + - type: "file" + name: "--replace_names" + description: "TSV file to rename sample names during report generation" + info: null + example: + - "replace_names.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Report Customisation" + arguments: + - type: "string" + name: "--title" + description: "Report title. Printed as page header, used for filename if not otherwise\ + \ specified.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--comment" + description: "Custom comment, will be printed at the top of the report.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--template" + description: "Report template to use.\n" + info: null + required: false + choices: + - "default" + - "gathered" + - "geo" + - "highcharts" + - "sections" + - "simple" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--sample_names" + description: "TSV file containing alternative sample names for renaming buttons\ + \ in the report.\n" + info: null + example: + - "sample_names.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--sample_filters" + description: "TSV file containing show/hide patterns for the report\n" + info: null + example: + - "sample_filters.tsv" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--custom_css_file" + description: "Custom CSS file to add to the final report\n" + info: null + example: + - "custom_style_sheet.css" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--profile_runtime" + description: "Add analysis of how long MultiQC takes to run to the report\n" + info: null + direction: "input" +- name: "MultiQC behaviour" + arguments: + - type: "boolean_true" + name: "--verbose" + description: "Increase output verbosity.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--quiet" + description: "Only show log warnings\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--strict" + description: "Don't catch exceptions, run additional code checks to help development.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--development" + description: "Development mode. Do not compress and minimise JS, export uncompressed\ + \ plot data.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--require_logs" + description: "Require all explicitly requested modules to have log files. If not,\ + \ MultiQC will exit with an error.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_megaqc_upload" + description: "Don't upload generated report to MegaQC, even if MegaQC options\ + \ are found.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_ansi" + description: "Disable coloured log output.\n" + info: null + direction: "input" + - type: "string" + name: "--cl_config" + description: "YAML formatted string that allows to customize MultiQC behaviour\ + \ like input file detection.\n" + info: null + example: + - "qualimap_config: { general_stats_coverage: [20,40,200] }" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output format" + arguments: + - type: "boolean_true" + name: "--flat" + description: "Use only flat plots (static images).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--interactive" + description: "Use only interactive plots (in-browser Javascript).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--data_dir" + description: "Force the parsed data directory to be created.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_data_dir" + description: "Prevent the parsed data directory from being created.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--zip_data_dir" + description: "Compress the data directory.\n" + info: null + direction: "input" + - type: "string" + name: "--data_format" + description: "Output parsed data in a different format than the default 'txt'.\n" + info: null + required: false + choices: + - "tsv" + - "csv" + - "json" + - "yaml" + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--pdf" + description: "Creates PDF report with the 'simple' template. Requires Pandoc to\ + \ be installed.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "MultiQC aggregates results from bioinformatics analyses across many\ + \ samples into a single report.\nIt searches a given directory for analysis logs\ + \ and compiles a HTML report. It's a general use tool, perfect for summarising the\ + \ output from numerous bioinformatics tools.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: + keywords: + - "QC" + - "html report" + - "aggregate analysis" + links: + homepage: "https://multiqc.info/" + documentation: "https://multiqc.info/docs/" + repository: "https://github.com/MultiQC/MultiQC" + references: + doi: "10.1093/bioinformatics/btw354" + licence: "GPL v3 or later" +status: "enabled" +requirements: + commands: + - "ps" +license: "MIT" +links: + repository: "https://github.com/viash-hub/biobox" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/multiqc:1.21--pyhdfd78af_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "multiqc --version | sed 's/multiqc, version\\s\\(.*\\)/multiqc: \"\\1\"/' >\ + \ /var/software_versions.txt\n" + test_setup: + - type: "apt" + packages: + - "jq" + interactive: false + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/multiqc/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/multiqc" + executable: "target/nextflow/multiqc/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/multiqc/main.nf b/target/nextflow/multiqc/main.nf new file mode 100644 index 00000000..a323a79b --- /dev/null +++ b/target/nextflow/multiqc/main.nf @@ -0,0 +1,4016 @@ +// multiqc main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "multiqc", + "version" : "main", + "argument_groups" : [ + { + "name" : "Input", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "File paths to be searched for analysis results to be included in the report.\n", + "example" : [ + "data/results" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Ouput", + "arguments" : [ + { + "type" : "file", + "name" : "--output_report", + "description" : "Filepath of the generated report.\n", + "example" : [ + "multiqc_report.html" + ], + "must_exist" : false, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--output_data", + "description" : "Output directory for parsed data files. If not provided, parsed data will not be published.\n", + "example" : [ + "multiqc_data" + ], + "must_exist" : false, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--output_plots", + "description" : "Output directory for generated plots. If not provided, plots will not be published.\n", + "example" : [ + "multiqc_plots" + ], + "must_exist" : false, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Modules and analyses to run", + "arguments" : [ + { + "type" : "string", + "name" : "--include_modules", + "description" : "Use only these module", + "example" : [ + "fastqc,cutadapt" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : "," + }, + { + "type" : "string", + "name" : "--exclude_modules", + "description" : "Do not use only these modules", + "example" : [ + "fastqc,cutadapt" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : "," + }, + { + "type" : "string", + "name" : "--ignore_analysis", + "example" : [ + "run_one/*,run_two/*" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : "," + }, + { + "type" : "string", + "name" : "--ignore_samples", + "example" : [ + "sample_1*,sample_3*" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : "," + }, + { + "type" : "boolean_true", + "name" : "--ignore_symlinks", + "description" : "Ignore symlinked directories and files", + "direction" : "input" + } + ] + }, + { + "name" : "Sample name handling", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--dirs", + "description" : "Prepend directory to sample names to avoid clashing filenames", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--dirs_depth", + "description" : "Prepend n directories to sample names. Negative number to take from start of path.", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--full_names", + "description" : "Do not clean the sample names (leave as full file name)", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--fn_as_s_name", + "description" : "Use the log filename as the sample name", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--replace_names", + "description" : "TSV file to rename sample names during report generation", + "example" : [ + "replace_names.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Report Customisation", + "arguments" : [ + { + "type" : "string", + "name" : "--title", + "description" : "Report title. Printed as page header, used for filename if not otherwise specified.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--comment", + "description" : "Custom comment, will be printed at the top of the report.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--template", + "description" : "Report template to use.\n", + "required" : false, + "choices" : [ + "default", + "gathered", + "geo", + "highcharts", + "sections", + "simple" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--sample_names", + "description" : "TSV file containing alternative sample names for renaming buttons in the report.\n", + "example" : [ + "sample_names.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--sample_filters", + "description" : "TSV file containing show/hide patterns for the report\n", + "example" : [ + "sample_filters.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--custom_css_file", + "description" : "Custom CSS file to add to the final report\n", + "example" : [ + "custom_style_sheet.css" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--profile_runtime", + "description" : "Add analysis of how long MultiQC takes to run to the report\n", + "direction" : "input" + } + ] + }, + { + "name" : "MultiQC behaviour", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--verbose", + "description" : "Increase output verbosity.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--quiet", + "description" : "Only show log warnings\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--strict", + "description" : "Don't catch exceptions, run additional code checks to help development.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--development", + "description" : "Development mode. Do not compress and minimise JS, export uncompressed plot data.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--require_logs", + "description" : "Require all explicitly requested modules to have log files. If not, MultiQC will exit with an error.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_megaqc_upload", + "description" : "Don't upload generated report to MegaQC, even if MegaQC options are found.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_ansi", + "description" : "Disable coloured log output.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--cl_config", + "description" : "YAML formatted string that allows to customize MultiQC behaviour like input file detection.\n", + "example" : [ + "qualimap_config: { general_stats_coverage: [20,40,200] }" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Output format", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--flat", + "description" : "Use only flat plots (static images).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--interactive", + "description" : "Use only interactive plots (in-browser Javascript).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--data_dir", + "description" : "Force the parsed data directory to be created.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_data_dir", + "description" : "Prevent the parsed data directory from being created.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--zip_data_dir", + "description" : "Compress the data directory.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--data_format", + "description" : "Output parsed data in a different format than the default 'txt'.\n", + "required" : false, + "choices" : [ + "tsv", + "csv", + "json", + "yaml" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--pdf", + "description" : "Creates PDF report with the 'simple' template. Requires Pandoc to be installed.\n", + "direction" : "input" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "MultiQC aggregates results from bioinformatics analyses across many samples into a single report.\nIt searches a given directory for analysis logs and compiles a HTML report. It's a general use tool, perfect for summarising the output from numerous bioinformatics tools.\n", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "info" : { + "keywords" : [ + "QC", + "html report", + "aggregate analysis" + ], + "links" : { + "homepage" : "https://multiqc.info/", + "documentation" : "https://multiqc.info/docs/", + "repository" : "https://github.com/MultiQC/MultiQC" + }, + "references" : { + "doi" : "10.1093/bioinformatics/btw354" + }, + "licence" : "GPL v3 or later" + }, + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "license" : "MIT", + "links" : { + "repository" : "https://github.com/viash-hub/biobox" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/multiqc:1.21--pyhdfd78af_0", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "multiqc --version | sed 's/multiqc, version\\\\s\\\\(.*\\\\)/multiqc: \\"\\\\1\\"/' > /var/software_versions.txt\n" + ] + } + ], + "test_setup" : [ + { + "type" : "apt", + "packages" : [ + "jq" + ], + "interactive" : false + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/multiqc/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/multiqc", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_REPORT+x} ]; then echo "${VIASH_PAR_OUTPUT_REPORT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_report='&'#" ; else echo "# par_output_report="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_DATA+x} ]; then echo "${VIASH_PAR_OUTPUT_DATA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_data='&'#" ; else echo "# par_output_data="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_PLOTS+x} ]; then echo "${VIASH_PAR_OUTPUT_PLOTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_plots='&'#" ; else echo "# par_output_plots="; fi ) +$( if [ ! -z ${VIASH_PAR_INCLUDE_MODULES+x} ]; then echo "${VIASH_PAR_INCLUDE_MODULES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_include_modules='&'#" ; else echo "# par_include_modules="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCLUDE_MODULES+x} ]; then echo "${VIASH_PAR_EXCLUDE_MODULES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_exclude_modules='&'#" ; else echo "# par_exclude_modules="; fi ) +$( if [ ! -z ${VIASH_PAR_IGNORE_ANALYSIS+x} ]; then echo "${VIASH_PAR_IGNORE_ANALYSIS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ignore_analysis='&'#" ; else echo "# par_ignore_analysis="; fi ) +$( if [ ! -z ${VIASH_PAR_IGNORE_SAMPLES+x} ]; then echo "${VIASH_PAR_IGNORE_SAMPLES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ignore_samples='&'#" ; else echo "# par_ignore_samples="; fi ) +$( if [ ! -z ${VIASH_PAR_IGNORE_SYMLINKS+x} ]; then echo "${VIASH_PAR_IGNORE_SYMLINKS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ignore_symlinks='&'#" ; else echo "# par_ignore_symlinks="; fi ) +$( if [ ! -z ${VIASH_PAR_DIRS+x} ]; then echo "${VIASH_PAR_DIRS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_dirs='&'#" ; else echo "# par_dirs="; fi ) +$( if [ ! -z ${VIASH_PAR_DIRS_DEPTH+x} ]; then echo "${VIASH_PAR_DIRS_DEPTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_dirs_depth='&'#" ; else echo "# par_dirs_depth="; fi ) +$( if [ ! -z ${VIASH_PAR_FULL_NAMES+x} ]; then echo "${VIASH_PAR_FULL_NAMES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_full_names='&'#" ; else echo "# par_full_names="; fi ) +$( if [ ! -z ${VIASH_PAR_FN_AS_S_NAME+x} ]; then echo "${VIASH_PAR_FN_AS_S_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fn_as_s_name='&'#" ; else echo "# par_fn_as_s_name="; fi ) +$( if [ ! -z ${VIASH_PAR_REPLACE_NAMES+x} ]; then echo "${VIASH_PAR_REPLACE_NAMES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_replace_names='&'#" ; else echo "# par_replace_names="; fi ) +$( if [ ! -z ${VIASH_PAR_TITLE+x} ]; then echo "${VIASH_PAR_TITLE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_title='&'#" ; else echo "# par_title="; fi ) +$( if [ ! -z ${VIASH_PAR_COMMENT+x} ]; then echo "${VIASH_PAR_COMMENT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_comment='&'#" ; else echo "# par_comment="; fi ) +$( if [ ! -z ${VIASH_PAR_TEMPLATE+x} ]; then echo "${VIASH_PAR_TEMPLATE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_template='&'#" ; else echo "# par_template="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_NAMES+x} ]; then echo "${VIASH_PAR_SAMPLE_NAMES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sample_names='&'#" ; else echo "# par_sample_names="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_FILTERS+x} ]; then echo "${VIASH_PAR_SAMPLE_FILTERS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sample_filters='&'#" ; else echo "# par_sample_filters="; fi ) +$( if [ ! -z ${VIASH_PAR_CUSTOM_CSS_FILE+x} ]; then echo "${VIASH_PAR_CUSTOM_CSS_FILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_custom_css_file='&'#" ; else echo "# par_custom_css_file="; fi ) +$( if [ ! -z ${VIASH_PAR_PROFILE_RUNTIME+x} ]; then echo "${VIASH_PAR_PROFILE_RUNTIME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_profile_runtime='&'#" ; else echo "# par_profile_runtime="; fi ) +$( if [ ! -z ${VIASH_PAR_VERBOSE+x} ]; then echo "${VIASH_PAR_VERBOSE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_verbose='&'#" ; else echo "# par_verbose="; fi ) +$( if [ ! -z ${VIASH_PAR_QUIET+x} ]; then echo "${VIASH_PAR_QUIET}" | sed "s#'#'\\"'\\"'#g;s#.*#par_quiet='&'#" ; else echo "# par_quiet="; fi ) +$( if [ ! -z ${VIASH_PAR_STRICT+x} ]; then echo "${VIASH_PAR_STRICT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_strict='&'#" ; else echo "# par_strict="; fi ) +$( if [ ! -z ${VIASH_PAR_DEVELOPMENT+x} ]; then echo "${VIASH_PAR_DEVELOPMENT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_development='&'#" ; else echo "# par_development="; fi ) +$( if [ ! -z ${VIASH_PAR_REQUIRE_LOGS+x} ]; then echo "${VIASH_PAR_REQUIRE_LOGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_require_logs='&'#" ; else echo "# par_require_logs="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_MEGAQC_UPLOAD+x} ]; then echo "${VIASH_PAR_NO_MEGAQC_UPLOAD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_megaqc_upload='&'#" ; else echo "# par_no_megaqc_upload="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_ANSI+x} ]; then echo "${VIASH_PAR_NO_ANSI}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_ansi='&'#" ; else echo "# par_no_ansi="; fi ) +$( if [ ! -z ${VIASH_PAR_CL_CONFIG+x} ]; then echo "${VIASH_PAR_CL_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cl_config='&'#" ; else echo "# par_cl_config="; fi ) +$( if [ ! -z ${VIASH_PAR_FLAT+x} ]; then echo "${VIASH_PAR_FLAT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_flat='&'#" ; else echo "# par_flat="; fi ) +$( if [ ! -z ${VIASH_PAR_INTERACTIVE+x} ]; then echo "${VIASH_PAR_INTERACTIVE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_interactive='&'#" ; else echo "# par_interactive="; fi ) +$( if [ ! -z ${VIASH_PAR_DATA_DIR+x} ]; then echo "${VIASH_PAR_DATA_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_data_dir='&'#" ; else echo "# par_data_dir="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_DATA_DIR+x} ]; then echo "${VIASH_PAR_NO_DATA_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_data_dir='&'#" ; else echo "# par_no_data_dir="; fi ) +$( if [ ! -z ${VIASH_PAR_ZIP_DATA_DIR+x} ]; then echo "${VIASH_PAR_ZIP_DATA_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_zip_data_dir='&'#" ; else echo "# par_zip_data_dir="; fi ) +$( if [ ! -z ${VIASH_PAR_DATA_FORMAT+x} ]; then echo "${VIASH_PAR_DATA_FORMAT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_data_format='&'#" ; else echo "# par_data_format="; fi ) +$( if [ ! -z ${VIASH_PAR_PDF+x} ]; then echo "${VIASH_PAR_PDF}" | sed "s#'#'\\"'\\"'#g;s#.*#par_pdf='&'#" ; else echo "# par_pdf="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END +#!/bin/bash + +# disable flags +[[ "\\$par_ignore_symlinks" == "false" ]] && unset par_ignore_symlinks +[[ "\\$par_dirs" == "false" ]] && unset par_dirs +[[ "\\$par_full_names" == "false" ]] && unset par_full_names +[[ "\\$par_fn_as_s_name" == "false" ]] && unset par_fn_as_s_name +[[ "\\$par_profile_runtime" == "false" ]] && unset par_profile_runtime +[[ "\\$par_verbose" == "false" ]] && unset par_verbose +[[ "\\$par_quiet" == "false" ]] && unset par_quiet +[[ "\\$par_strict" == "false" ]] && unset par_strict +[[ "\\$par_development" == "false" ]] && unset par_development +[[ "\\$par_require_logs" == "false" ]] && unset par_require_logs +[[ "\\$par_no_megaqc_upload" == "false" ]] && unset par_no_megaqc_upload +[[ "\\$par_no_ansi" == "false" ]] && unset par_no_ansi +[[ "\\$par_flat" == "false" ]] && unset par_flat +[[ "\\$par_interactive" == "false" ]] && unset par_interactive +[[ "\\$par_static_plot_export" == "false" ]] && unset par_static_plot_export +[[ "\\$par_data_dir" == "false" ]] && unset par_data_dir +[[ "\\$par_no_data_dir" == "false" ]] && unset par_no_data_dir +[[ "\\$par_zip_data_dir" == "false" ]] && unset par_zip_data_dir +[[ "\\$par_pdf" == "false" ]] && unset par_pdf + + +# handle inputs +out_dir=\\$(dirname "\\$par_output_report") +output_report_file=\\$(basename "\\$par_output_report") +report_name="\\${output_report_file%.*}" + +# handle outputs +[[ -z "\\$par_output_report" ]] && no_report=true +[[ -z "\\$par_output_data" ]] && no_data_dir=true +[[ ! -z "\\$par_output_data" ]] && data_dir=true +[[ ! -z "\\$par_output_plots" ]] && export=true + +# handle multiples +IFS=";" read -ra inputs <<< \\$par_input + +if [[ -n "\\$par_include_modules" ]]; then + include_modules="" + IFS="," read -ra incl_modules <<< \\$par_include_modules + for i in "\\${incl_modules[@]}"; do + include_modules+="--include \\$i " + done + unset IFS +fi + +if [[ -n "\\$par_exclude_modules" ]]; then + exclude_modules="" + IFS="," read -ra excl_modules <<< \\$par_exclude_modules + for i in "\\${excl_modules[@]}"; do + exclude_modules+="--exclude \\$i" + done + unset IFS +fi + +if [[ -n "\\$par_ignore_analysis" ]]; then + ignore="" + IFS="," read -ra ignore_analysis <<< \\$par_ignore_analysis + for i in "\\${ignore_analysis[@]}"; do + ignore+="--ignore \\$i " + done + unset IFS +fi + +if [[ -n "\\$par_ignore_samples" ]]; then + ignore_samples="" + IFS="," read -ra ign_samples <<< \\$par_ignore_samples + for i in "\\${ign_samples[@]}"; do + ignore_samples+="--ignore-samples \\$i" + done + unset IFS +fi + +# run multiqc +multiqc \\\\ + \\${par_output_report:+--filename "\\$report_name"} \\\\ + \\${out_dir:+--outdir "\\$out_dir"} \\\\ + \\${no_report:+--no-report} \\\\ + \\${no_data_dir:+--no-data-dir} \\\\ + \\${data_dir:+--data-dir} \\\\ + \\${export:+--export} \\\\ + \\${par_title:+--title "\\$par_title"} \\\\ + \\${par_comment:+--comment "\\$par_comment"} \\\\ + \\${par_template:+--template "\\$par_template"} \\\\ + \\${par_sample_names:+--sample-names "\\$par_sample_names"} \\\\ + \\${par_sample_filters:+--sample-filters "\\$par_sample_filters"} \\\\ + \\${par_custom_css_file:+--custom-css-file "\\$par_custom_css_file"} \\\\ + \\${par_profile_runtime:+--profile-runtime} \\\\ + \\${par_dirs:+--dirs} \\\\ + \\${par_dirs_depth:+--dirs-depth "\\$par_dirs_depth"} \\\\ + \\${par_full_names:+--full-names} \\\\ + \\${par_fn_as_s_name:+--fn-as-s-name} \\\\ + \\${par_ignore_names:+--ignore-names "\\$par_ignore_names"} \\\\ + \\${par_ignore_symlinks:+--ignore-symlinks} \\\\ + \\${ignore_samples} \\\\ + \\${ignore} \\\\ + \\${exclude_modules} \\\\ + \\${include_modules} \\\\ + \\${par_include_modules:+--include-modules "\\$par_include_modules"} \\\\ + \\${par_data_format:+--data-format "\\$par_data_format"} \\\\ + \\${par_cl_config:+--cl-config "\\$par_cl_config"} \\\\ + \\${par_zip_data_dir:+--zip-data-dir} \\\\ + \\${par_pdf:+--pdf} \\\\ + \\${par_interactive:+--interactive} \\\\ + \\${par_flat:+--flat} \\\\ + \\${par_verbose:+--verbose} \\\\ + \\${par_quiet:+--quiet} \\\\ + \\${par_strict:+--strict} \\\\ + \\${par_no_megaqc_upload:+--no-megaqc-upload} \\\\ + \\${par_no_ansi:+--no-ansi} \\\\ + \\${par_profile_runtime:+--profile-runtime} \\\\ + \\${par_require_logs:+--require-logs} \\\\ + \\${par_development:+--development} \\\\ + --force \\\\ + "\\${inputs[@]}" + +# Move outputs + +if [[ -n "\\$par_output_data" ]] && [[ -d "\\${out_dir}/\\${report_name}_data" ]]; then + mv "\\${out_dir}/\\${report_name}_data" "\\$par_output_data" +elif [[ -n "\\$par_output_data" ]] && [[ ! -d "\\${out_dir}/\\${report_name}_data" ]]; then + echo "WARNING: Data could not be saved because data folder was not generated by multiqc. This could be due to filtering out of modules or samples." +fi + +if [[ -n "\\$par_output_plots" ]] && [[ -d "\\${out_dir}/\\${report_name}_plots" ]]; then + mv "\\${out_dir}/\\${report_name}_plots" "\\$par_output_plots" +elif [[ -n "\\$par_output_plots" ]] && [[ ! -d "\\${out_dir}/\\${report_name}_plots" ]]; then + echo "WARNING: Plots could not be saved because plots folder was not generated by multiqc. This could be due to filtering out of modules or samples." +fi +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/multiqc", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/multiqc/nextflow.config b/target/nextflow/multiqc/nextflow.config new file mode 100644 index 00000000..2c0c1557 --- /dev/null +++ b/target/nextflow/multiqc/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'multiqc' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'MultiQC aggregates results from bioinformatics analyses across many samples into a single report.\nIt searches a given directory for analysis logs and compiles a HTML report. It\'s a general use tool, perfect for summarising the output from numerous bioinformatics tools.\n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/multiqc/nextflow_schema.json b/target/nextflow/multiqc/nextflow_schema.json new file mode 100644 index 00000000..daab1d2a --- /dev/null +++ b/target/nextflow/multiqc/nextflow_schema.json @@ -0,0 +1,529 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "multiqc", +"description": "MultiQC aggregates results from bioinformatics analyses across many samples into a single report.\nIt searches a given directory for analysis logs and compiles a HTML report. It\u0027s a general use tool, perfect for summarising the output from numerous bioinformatics tools.\n", +"type": "object", +"definitions": { + + + + "input" : { + "title": "Input", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: List of `file`, required, example: `data/results/`, multiple_sep: `\":\"`. File paths to be searched for analysis results to be included in the report", + "help_text": "Type: List of `file`, required, example: `data/results/`, multiple_sep: `\":\"`. File paths to be searched for analysis results to be included in the report.\n" + + } + + +} +}, + + + "ouput" : { + "title": "Ouput", + "type": "object", + "description": "No description", + "properties": { + + + "output_report": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.output_report.html`, example: `multiqc_report.html`. Filepath of the generated report", + "help_text": "Type: `file`, default: `$id.$key.output_report.html`, example: `multiqc_report.html`. Filepath of the generated report.\n" + , + "default": "$id.$key.output_report.html" + } + + + , + "output_data": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.output_data.output_data`, example: `multiqc_data`. Output directory for parsed data files", + "help_text": "Type: `file`, default: `$id.$key.output_data.output_data`, example: `multiqc_data`. Output directory for parsed data files. If not provided, parsed data will not be published.\n" + , + "default": "$id.$key.output_data.output_data" + } + + + , + "output_plots": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.output_plots.output_plots`, example: `multiqc_plots`. Output directory for generated plots", + "help_text": "Type: `file`, default: `$id.$key.output_plots.output_plots`, example: `multiqc_plots`. Output directory for generated plots. If not provided, plots will not be published.\n" + , + "default": "$id.$key.output_plots.output_plots" + } + + +} +}, + + + "modules and analyses to run" : { + "title": "Modules and analyses to run", + "type": "object", + "description": "No description", + "properties": { + + + "include_modules": { + "type": + "string", + "description": "Type: List of `string`, example: `fastqc,cutadapt`, multiple_sep: `\",\"`. Use only these module", + "help_text": "Type: List of `string`, example: `fastqc,cutadapt`, multiple_sep: `\",\"`. Use only these module" + + } + + + , + "exclude_modules": { + "type": + "string", + "description": "Type: List of `string`, example: `fastqc,cutadapt`, multiple_sep: `\",\"`. Do not use only these modules", + "help_text": "Type: List of `string`, example: `fastqc,cutadapt`, multiple_sep: `\",\"`. Do not use only these modules" + + } + + + , + "ignore_analysis": { + "type": + "string", + "description": "Type: List of `string`, example: `run_one/*,run_two/*`, multiple_sep: `\",\"`. ", + "help_text": "Type: List of `string`, example: `run_one/*,run_two/*`, multiple_sep: `\",\"`. " + + } + + + , + "ignore_samples": { + "type": + "string", + "description": "Type: List of `string`, example: `sample_1*,sample_3*`, multiple_sep: `\",\"`. ", + "help_text": "Type: List of `string`, example: `sample_1*,sample_3*`, multiple_sep: `\",\"`. " + + } + + + , + "ignore_symlinks": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Ignore symlinked directories and files", + "help_text": "Type: `boolean_true`, default: `false`. Ignore symlinked directories and files" + , + "default": "False" + } + + +} +}, + + + "sample name handling" : { + "title": "Sample name handling", + "type": "object", + "description": "No description", + "properties": { + + + "dirs": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Prepend directory to sample names to avoid clashing filenames", + "help_text": "Type: `boolean_true`, default: `false`. Prepend directory to sample names to avoid clashing filenames" + , + "default": "False" + } + + + , + "dirs_depth": { + "type": + "integer", + "description": "Type: `integer`. Prepend n directories to sample names", + "help_text": "Type: `integer`. Prepend n directories to sample names. Negative number to take from start of path." + + } + + + , + "full_names": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Do not clean the sample names (leave as full file name)", + "help_text": "Type: `boolean_true`, default: `false`. Do not clean the sample names (leave as full file name)" + , + "default": "False" + } + + + , + "fn_as_s_name": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use the log filename as the sample name", + "help_text": "Type: `boolean_true`, default: `false`. Use the log filename as the sample name" + , + "default": "False" + } + + + , + "replace_names": { + "type": + "string", + "description": "Type: `file`, example: `replace_names.tsv`. TSV file to rename sample names during report generation", + "help_text": "Type: `file`, example: `replace_names.tsv`. TSV file to rename sample names during report generation" + + } + + +} +}, + + + "report customisation" : { + "title": "Report Customisation", + "type": "object", + "description": "No description", + "properties": { + + + "title": { + "type": + "string", + "description": "Type: `string`. Report title", + "help_text": "Type: `string`. Report title. Printed as page header, used for filename if not otherwise specified.\n" + + } + + + , + "comment": { + "type": + "string", + "description": "Type: `string`. Custom comment, will be printed at the top of the report", + "help_text": "Type: `string`. Custom comment, will be printed at the top of the report.\n" + + } + + + , + "template": { + "type": + "string", + "description": "Type: `string`, choices: ``default`, `gathered`, `geo`, `highcharts`, `sections`, `simple``. Report template to use", + "help_text": "Type: `string`, choices: ``default`, `gathered`, `geo`, `highcharts`, `sections`, `simple``. Report template to use.\n", + "enum": ["default", "gathered", "geo", "highcharts", "sections", "simple"] + + + } + + + , + "sample_names": { + "type": + "string", + "description": "Type: `file`, example: `sample_names.tsv`. TSV file containing alternative sample names for renaming buttons in the report", + "help_text": "Type: `file`, example: `sample_names.tsv`. TSV file containing alternative sample names for renaming buttons in the report.\n" + + } + + + , + "sample_filters": { + "type": + "string", + "description": "Type: `file`, example: `sample_filters.tsv`. TSV file containing show/hide patterns for the report\n", + "help_text": "Type: `file`, example: `sample_filters.tsv`. TSV file containing show/hide patterns for the report\n" + + } + + + , + "custom_css_file": { + "type": + "string", + "description": "Type: `file`, example: `custom_style_sheet.css`. Custom CSS file to add to the final report\n", + "help_text": "Type: `file`, example: `custom_style_sheet.css`. Custom CSS file to add to the final report\n" + + } + + + , + "profile_runtime": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Add analysis of how long MultiQC takes to run to the report\n", + "help_text": "Type: `boolean_true`, default: `false`. Add analysis of how long MultiQC takes to run to the report\n" + , + "default": "False" + } + + +} +}, + + + "multiqc behaviour" : { + "title": "MultiQC behaviour", + "type": "object", + "description": "No description", + "properties": { + + + "verbose": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Increase output verbosity", + "help_text": "Type: `boolean_true`, default: `false`. Increase output verbosity.\n" + , + "default": "False" + } + + + , + "quiet": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Only show log warnings\n", + "help_text": "Type: `boolean_true`, default: `false`. Only show log warnings\n" + , + "default": "False" + } + + + , + "strict": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Don\u0027t catch exceptions, run additional code checks to help development", + "help_text": "Type: `boolean_true`, default: `false`. Don\u0027t catch exceptions, run additional code checks to help development.\n" + , + "default": "False" + } + + + , + "development": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Development mode", + "help_text": "Type: `boolean_true`, default: `false`. Development mode. Do not compress and minimise JS, export uncompressed plot data.\n" + , + "default": "False" + } + + + , + "require_logs": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Require all explicitly requested modules to have log files", + "help_text": "Type: `boolean_true`, default: `false`. Require all explicitly requested modules to have log files. If not, MultiQC will exit with an error.\n" + , + "default": "False" + } + + + , + "no_megaqc_upload": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Don\u0027t upload generated report to MegaQC, even if MegaQC options are found", + "help_text": "Type: `boolean_true`, default: `false`. Don\u0027t upload generated report to MegaQC, even if MegaQC options are found.\n" + , + "default": "False" + } + + + , + "no_ansi": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Disable coloured log output", + "help_text": "Type: `boolean_true`, default: `false`. Disable coloured log output.\n" + , + "default": "False" + } + + + , + "cl_config": { + "type": + "string", + "description": "Type: `string`, example: `qualimap_config: { general_stats_coverage: [20,40,200] }`. YAML formatted string that allows to customize MultiQC behaviour like input file detection", + "help_text": "Type: `string`, example: `qualimap_config: { general_stats_coverage: [20,40,200] }`. YAML formatted string that allows to customize MultiQC behaviour like input file detection.\n" + + } + + +} +}, + + + "output format" : { + "title": "Output format", + "type": "object", + "description": "No description", + "properties": { + + + "flat": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use only flat plots (static images)", + "help_text": "Type: `boolean_true`, default: `false`. Use only flat plots (static images).\n" + , + "default": "False" + } + + + , + "interactive": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use only interactive plots (in-browser Javascript)", + "help_text": "Type: `boolean_true`, default: `false`. Use only interactive plots (in-browser Javascript).\n" + , + "default": "False" + } + + + , + "data_dir": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Force the parsed data directory to be created", + "help_text": "Type: `boolean_true`, default: `false`. Force the parsed data directory to be created.\n" + , + "default": "False" + } + + + , + "no_data_dir": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Prevent the parsed data directory from being created", + "help_text": "Type: `boolean_true`, default: `false`. Prevent the parsed data directory from being created.\n" + , + "default": "False" + } + + + , + "zip_data_dir": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Compress the data directory", + "help_text": "Type: `boolean_true`, default: `false`. Compress the data directory.\n" + , + "default": "False" + } + + + , + "data_format": { + "type": + "string", + "description": "Type: `string`, choices: ``tsv`, `csv`, `json`, `yaml``. Output parsed data in a different format than the default \u0027txt\u0027", + "help_text": "Type: `string`, choices: ``tsv`, `csv`, `json`, `yaml``. Output parsed data in a different format than the default \u0027txt\u0027.\n", + "enum": ["tsv", "csv", "json", "yaml"] + + + } + + + , + "pdf": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Creates PDF report with the \u0027simple\u0027 template", + "help_text": "Type: `boolean_true`, default: `false`. Creates PDF report with the \u0027simple\u0027 template. Requires Pandoc to be installed.\n" + , + "default": "False" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/input" + }, + + { + "$ref": "#/definitions/ouput" + }, + + { + "$ref": "#/definitions/modules and analyses to run" + }, + + { + "$ref": "#/definitions/sample name handling" + }, + + { + "$ref": "#/definitions/report customisation" + }, + + { + "$ref": "#/definitions/multiqc behaviour" + }, + + { + "$ref": "#/definitions/output format" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/pear/.config.vsh.yaml b/target/nextflow/pear/.config.vsh.yaml new file mode 100644 index 00000000..57af6ce6 --- /dev/null +++ b/target/nextflow/pear/.config.vsh.yaml @@ -0,0 +1,406 @@ +name: "pear" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--forward_fastq" + alternatives: + - "-f" + description: "Forward paired-end FASTQ file" + info: null + example: + - "forward.fastq" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reverse_fastq" + alternatives: + - "-r" + description: "Reverse paired-end FASTQ file" + info: null + example: + - "reverse.fastq" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--assembled" + description: "The output file containing assembled reads. Can be compressed with\ + \ gzip." + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unassembled_forward" + description: "The output file containing forward reads that could not be assembled.\ + \ Can be compressed with gzip." + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unassembled_reverse" + description: "The output file containing reverse reads that could not be assembled.\ + \ Can be compressed with gzip." + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--discarded" + description: "The output file containing reads that were discarded due to too\ + \ low quality or too many uncalled bases. Can be compressed with gzip." + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Arguments" + arguments: + - type: "double" + name: "--p_value" + alternatives: + - "-p" + description: "Specify a p-value for the statistical test. If the computed p-value\ + \ of a possible assembly exceeds the specified p-value then paired-end read\ + \ will not be assembled. Valid options are: 0.0001, 0.001, 0.01, 0.05 and 1.0.\ + \ Setting 1.0 disables the test.\n" + info: null + example: + - 0.01 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_overlap" + alternatives: + - "-v" + description: "Specify the minimum overlap size. The minimum overlap may be set\ + \ to 1 when the statistical test is used. However, further restricting the minimum\ + \ overlap size to a proper value may reduce false-positive assembles.\n" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_assembly_length" + alternatives: + - "-m" + description: "Specify the maximum possible length of the assembled sequences.\ + \ Setting this value to 0 disables the restriction and assembled sequences may\ + \ be arbitrary long.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_assembly_length" + alternatives: + - "-n" + description: "Specify the minimum possible length of the assembled sequences.\ + \ Setting this value to 0 disables the restriction and assembled sequences may\ + \ be arbitrary short.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_trim_length" + alternatives: + - "-t" + description: "Specify the minimum length of reads after trimming the low quality\ + \ part (see option -q)\n" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--quality_threshold" + alternatives: + - "-q" + description: "Specify the quality threshold for trimming the low quality part\ + \ of a read. If the quality scores of two consecutive bases are strictly less\ + \ than the specified threshold, the rest of the read will be trimmed.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--max_uncalled_base" + alternatives: + - "-u" + description: "Specify the maximal proportion of uncalled bases in a read. Setting\ + \ this value to 0 will cause PEAR to discard all reads containing uncalled bases.\ + \ The other extreme setting is 1 which causes PEAR to process all reads independent\ + \ on the number of uncalled bases.\n" + info: null + example: + - 1.0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--test_method" + alternatives: + - "-g" + description: "Specify the type of statistical test. Two options are available.\ + \ 1: Given the minimum allowed overlap, test using the highest OES. Note that\ + \ due to its discrete nature, this test usually yields a lower p-value for the\ + \ assembled read than the cut- off (specified by -p). For example, setting the\ + \ cut-off to 0.05 using this test, the assembled reads might have an actual\ + \ p-value of 0.02.\n2. Use the acceptance probability (m.a.p). This test methods\ + \ computes the same probability as test method 1. However, it assumes that the\ + \ minimal overlap is the observed overlap with the highest OES, instead of the\ + \ one specified by -v. Therefore, this is not a valid statistical test and the\ + \ 'p-value' is in fact the maximal probability for accepting the assembly. Nevertheless,\ + \ we observed in practice that for the case the actual overlap sizes are relatively\ + \ small, test 2 can correctly assemble more reads with only slightly higher\ + \ false-positive rate.\n" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--emperical_freqs" + alternatives: + - "-e" + description: "Disable empirical base frequencies.\n" + info: null + direction: "input" + - type: "integer" + name: "--score_method" + alternatives: + - "-s" + description: "Specify the scoring method. 1. OES with +1 for match and -1 for\ + \ mismatch. 2: Assembly score (AS). Use +1 for match and -1 for mismatch multiplied\ + \ by base quality scores. 3: Ignore quality scores and use +1 for a match and\ + \ -1 for a mismatch.\n" + info: null + example: + - 2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--phred_base" + alternatives: + - "-b" + description: "Base PHRED quality score.\n" + info: null + example: + - 33 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--cap" + alternatives: + - "-c" + description: "Specify the upper bound for the resulting quality score. If set\ + \ to zero, capping is disabled.\n" + info: null + example: + - 40 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--nbase" + alternatives: + - "-z" + description: "When merging a base-pair that consists of two non-equal bases out\ + \ of which none is degenerate, set the merged base to N and use the highest\ + \ quality score of the two bases\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "PEAR is an ultrafast, memory-efficient and highly accurate pair-end\ + \ read merger. It is fully parallelized and can run with as low as just a few kilobytes\ + \ of memory.\n\nPEAR evaluates all possible paired-end read overlaps and without\ + \ requiring the target fragment size as input. In addition, it implements a statistical\ + \ test for minimizing false-positive results. Together with a highly optimized implementation,\ + \ it can merge millions of paired end reads within a couple of minutes on a standard\ + \ desktop computer.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "pair-end" +- "read" +- "merge" +license: "CC-BY-NC-SA-3.0" +references: + doi: + - "10.1093/bioinformatics/btt593" +links: + repository: "https://github.com/tseemann/PEAR" + homepage: "https://cme.h-its.org/exelixis/web/software/pear" + documentation: "https://cme.h-its.org/exelixis/web/software/pear/doc.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/pear:0.9.6--h9d449c0_10" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "version=$(pear -h | grep 'PEAR v' | sed 's/PEAR v//' | sed 's/ .*//') && \\\ + \necho \"pear: $version\" > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/pear/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/pear" + executable: "target/nextflow/pear/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/pear/main.nf b/target/nextflow/pear/main.nf new file mode 100644 index 00000000..eb614385 --- /dev/null +++ b/target/nextflow/pear/main.nf @@ -0,0 +1,3821 @@ +// pear main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "pear", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--forward_fastq", + "alternatives" : [ + "-f" + ], + "description" : "Forward paired-end FASTQ file", + "example" : [ + "forward.fastq" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--reverse_fastq", + "alternatives" : [ + "-r" + ], + "description" : "Reverse paired-end FASTQ file", + "example" : [ + "reverse.fastq" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--assembled", + "description" : "The output file containing assembled reads. Can be compressed with gzip.", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--unassembled_forward", + "description" : "The output file containing forward reads that could not be assembled. Can be compressed with gzip.", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--unassembled_reverse", + "description" : "The output file containing reverse reads that could not be assembled. Can be compressed with gzip.", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--discarded", + "description" : "The output file containing reads that were discarded due to too low quality or too many uncalled bases. Can be compressed with gzip.", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Arguments", + "arguments" : [ + { + "type" : "double", + "name" : "--p_value", + "alternatives" : [ + "-p" + ], + "description" : "Specify a p-value for the statistical test. If the computed p-value of a possible assembly exceeds the specified p-value then paired-end read will not be assembled. Valid options are: 0.0001, 0.001, 0.01, 0.05 and 1.0. Setting 1.0 disables the test.\n", + "example" : [ + 0.01 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_overlap", + "alternatives" : [ + "-v" + ], + "description" : "Specify the minimum overlap size. The minimum overlap may be set to 1 when the statistical test is used. However, further restricting the minimum overlap size to a proper value may reduce false-positive assembles.\n", + "example" : [ + 10 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--max_assembly_length", + "alternatives" : [ + "-m" + ], + "description" : "Specify the maximum possible length of the assembled sequences. Setting this value to 0 disables the restriction and assembled sequences may be arbitrary long.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_assembly_length", + "alternatives" : [ + "-n" + ], + "description" : "Specify the minimum possible length of the assembled sequences. Setting this value to 0 disables the restriction and assembled sequences may be arbitrary short.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_trim_length", + "alternatives" : [ + "-t" + ], + "description" : "Specify the minimum length of reads after trimming the low quality part (see option -q)\n", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--quality_threshold", + "alternatives" : [ + "-q" + ], + "description" : "Specify the quality threshold for trimming the low quality part of a read. If the quality scores of two consecutive bases are strictly less than the specified threshold, the rest of the read will be trimmed.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--max_uncalled_base", + "alternatives" : [ + "-u" + ], + "description" : "Specify the maximal proportion of uncalled bases in a read. Setting this value to 0 will cause PEAR to discard all reads containing uncalled bases. The other extreme setting is 1 which causes PEAR to process all reads independent on the number of uncalled bases.\n", + "example" : [ + 1.0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--test_method", + "alternatives" : [ + "-g" + ], + "description" : "Specify the type of statistical test. Two options are available. 1: Given the minimum allowed overlap, test using the highest OES. Note that due to its discrete nature, this test usually yields a lower p-value for the assembled read than the cut- off (specified by -p). For example, setting the cut-off to 0.05 using this test, the assembled reads might have an actual p-value of 0.02.\n2. Use the acceptance probability (m.a.p). This test methods computes the same probability as test method 1. However, it assumes that the minimal overlap is the observed overlap with the highest OES, instead of the one specified by -v. Therefore, this is not a valid statistical test and the 'p-value' is in fact the maximal probability for accepting the assembly. Nevertheless, we observed in practice that for the case the actual overlap sizes are relatively small, test 2 can correctly assemble more reads with only slightly higher false-positive rate.\n", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--emperical_freqs", + "alternatives" : [ + "-e" + ], + "description" : "Disable empirical base frequencies.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--score_method", + "alternatives" : [ + "-s" + ], + "description" : "Specify the scoring method. 1. OES with +1 for match and -1 for mismatch. 2: Assembly score (AS). Use +1 for match and -1 for mismatch multiplied by base quality scores. 3: Ignore quality scores and use +1 for a match and -1 for a mismatch.\n", + "example" : [ + 2 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--phred_base", + "alternatives" : [ + "-b" + ], + "description" : "Base PHRED quality score.\n", + "example" : [ + 33 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--cap", + "alternatives" : [ + "-c" + ], + "description" : "Specify the upper bound for the resulting quality score. If set to zero, capping is disabled.\n", + "example" : [ + 40 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--nbase", + "alternatives" : [ + "-z" + ], + "description" : "When merging a base-pair that consists of two non-equal bases out of which none is degenerate, set the merged base to N and use the highest quality score of the two bases\n", + "direction" : "input" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "PEAR is an ultrafast, memory-efficient and highly accurate pair-end read merger. It is fully parallelized and can run with as low as just a few kilobytes of memory.\n\nPEAR evaluates all possible paired-end read overlaps and without requiring the target fragment size as input. In addition, it implements a statistical test for minimizing false-positive results. Together with a highly optimized implementation, it can merge millions of paired end reads within a couple of minutes on a standard desktop computer.\n", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "pair-end", + "read", + "merge" + ], + "license" : "CC-BY-NC-SA-3.0", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btt593" + ] + }, + "links" : { + "repository" : "https://github.com/tseemann/PEAR", + "homepage" : "https://cme.h-its.org/exelixis/web/software/pear", + "documentation" : "https://cme.h-its.org/exelixis/web/software/pear/doc.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/pear:0.9.6--h9d449c0_10", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "version=$(pear -h | grep 'PEAR v' | sed 's/PEAR v//' | sed 's/ .*//') && \\\\\necho \\"pear: $version\\" > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/pear/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/pear", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_FORWARD_FASTQ+x} ]; then echo "${VIASH_PAR_FORWARD_FASTQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_forward_fastq='&'#" ; else echo "# par_forward_fastq="; fi ) +$( if [ ! -z ${VIASH_PAR_REVERSE_FASTQ+x} ]; then echo "${VIASH_PAR_REVERSE_FASTQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_reverse_fastq='&'#" ; else echo "# par_reverse_fastq="; fi ) +$( if [ ! -z ${VIASH_PAR_ASSEMBLED+x} ]; then echo "${VIASH_PAR_ASSEMBLED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_assembled='&'#" ; else echo "# par_assembled="; fi ) +$( if [ ! -z ${VIASH_PAR_UNASSEMBLED_FORWARD+x} ]; then echo "${VIASH_PAR_UNASSEMBLED_FORWARD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_unassembled_forward='&'#" ; else echo "# par_unassembled_forward="; fi ) +$( if [ ! -z ${VIASH_PAR_UNASSEMBLED_REVERSE+x} ]; then echo "${VIASH_PAR_UNASSEMBLED_REVERSE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_unassembled_reverse='&'#" ; else echo "# par_unassembled_reverse="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARDED+x} ]; then echo "${VIASH_PAR_DISCARDED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_discarded='&'#" ; else echo "# par_discarded="; fi ) +$( if [ ! -z ${VIASH_PAR_P_VALUE+x} ]; then echo "${VIASH_PAR_P_VALUE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_p_value='&'#" ; else echo "# par_p_value="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_OVERLAP+x} ]; then echo "${VIASH_PAR_MIN_OVERLAP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_overlap='&'#" ; else echo "# par_min_overlap="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_ASSEMBLY_LENGTH+x} ]; then echo "${VIASH_PAR_MAX_ASSEMBLY_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_assembly_length='&'#" ; else echo "# par_max_assembly_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ASSEMBLY_LENGTH+x} ]; then echo "${VIASH_PAR_MIN_ASSEMBLY_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_assembly_length='&'#" ; else echo "# par_min_assembly_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_TRIM_LENGTH+x} ]; then echo "${VIASH_PAR_MIN_TRIM_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_trim_length='&'#" ; else echo "# par_min_trim_length="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALITY_THRESHOLD+x} ]; then echo "${VIASH_PAR_QUALITY_THRESHOLD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_quality_threshold='&'#" ; else echo "# par_quality_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_UNCALLED_BASE+x} ]; then echo "${VIASH_PAR_MAX_UNCALLED_BASE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_uncalled_base='&'#" ; else echo "# par_max_uncalled_base="; fi ) +$( if [ ! -z ${VIASH_PAR_TEST_METHOD+x} ]; then echo "${VIASH_PAR_TEST_METHOD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_test_method='&'#" ; else echo "# par_test_method="; fi ) +$( if [ ! -z ${VIASH_PAR_EMPERICAL_FREQS+x} ]; then echo "${VIASH_PAR_EMPERICAL_FREQS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_emperical_freqs='&'#" ; else echo "# par_emperical_freqs="; fi ) +$( if [ ! -z ${VIASH_PAR_SCORE_METHOD+x} ]; then echo "${VIASH_PAR_SCORE_METHOD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_score_method='&'#" ; else echo "# par_score_method="; fi ) +$( if [ ! -z ${VIASH_PAR_PHRED_BASE+x} ]; then echo "${VIASH_PAR_PHRED_BASE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_phred_base='&'#" ; else echo "# par_phred_base="; fi ) +$( if [ ! -z ${VIASH_PAR_CAP+x} ]; then echo "${VIASH_PAR_CAP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cap='&'#" ; else echo "# par_cap="; fi ) +$( if [ ! -z ${VIASH_PAR_NBASE+x} ]; then echo "${VIASH_PAR_NBASE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_nbase='&'#" ; else echo "# par_nbase="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +[[ "\\$par_emperical_freqs" == "false" ]] && unset par_emperical_freqs +[[ "\\$par_nbase" == "false" ]] && unset par_nbase + +if [[ "\\${par_forward_fastq##*.}" == "gz" ]]; then + gunzip \\$par_forward_fastq + par_forward_fastq=\\${par_forward_fastq%.*} +fi +if [[ "\\${par_reverse_fastq##*.}" == "gz" ]]; then + gunzip \\$par_reverse_fastq + par_reverse_fastq=\\${par_reverse_fastq%.*} +fi + +output_dir=\\$(mktemp -d -p "\\$meta_temp_dir" "pear.XXXXXX") + +pear \\\\ + -f "\\$par_forward_fastq" \\\\ + -r "\\$par_reverse_fastq" \\\\ + -o "\\$output_dir" \\\\ + \\${par_p_value:+-p "\\${par_p_value}"} \\\\ + \\${par_min_overlap:+-v "\\${par_min_overlap}"} \\\\ + \\${par_max_assembly_length:+-m "\\${par_max_assembly_length}"} \\\\ + \\${par_min_assembly_length:+-n "\\${par_min_assembly_length}"} \\\\ + \\${par_min_trim_length:+-t "\\${par_min_trim_length}"} \\\\ + \\${par_quality_threshold:+-q "\\${par_quality_threshold}"} \\\\ + \\${par_max_uncalled_base:+-u "\\${par_max_uncalled_base}"} \\\\ + \\${par_test_method:+-g "\\${par_test_method}"} \\\\ + \\${par_score_method:+-s "\\${par_score_method}"} \\\\ + \\${par_phred_base:+-b "\\${par_phred_base}"} \\\\ + \\${meta_memory_mb:+--memory "\\${meta_memory_mb}M"} \\\\ + \\${par_cap:+-c "\\${par_cap}"} \\\\ + \\${meta_cpus:+-j "\\${meta_cpus}"} \\\\ + \\${par_emperical_freqs:+-e} \\\\ + \\${par_nbase:+-z} + + +if [[ "\\${par_assembled##*.}" == "gz" ]]; then + gzip -9 -c \\${output_dir}.assembled.fastq > \\${par_assembled} +else + mv \\${output_dir}.assembled.fastq \\${par_assembled} +fi + +if [[ "\\${par_unassembled_forward##*.}" == "gz" ]]; then + gzip -9 -c \\${output_dir}.unassembled.forward.fastq > \\${par_unassembled_forward} +else + mv \\${output_dir}.unassembled.forward.fastq \\${par_unassembled_forward} +fi + +if [[ "\\${par_unassembled_reverse##*.}" == "gz" ]]; then + gzip -9 -c \\${output_dir}.unassembled.reverse.fastq > \\${par_unassembled_reverse} +else + mv \\${output_dir}.unassembled.reverse.fastq \\${par_unassembled_reverse} +fi + +if [[ "\\${par_discarded##*.}" == "gz" ]]; then + gzip -9 -c \\${output_dir}.discarded.fastq > \\${par_discarded} +else + mv \\${output_dir}.discarded.fastq \\${par_discarded} +fi +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/pear", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/pear/nextflow.config b/target/nextflow/pear/nextflow.config new file mode 100644 index 00000000..ecc03e52 --- /dev/null +++ b/target/nextflow/pear/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'pear' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'PEAR is an ultrafast, memory-efficient and highly accurate pair-end read merger. It is fully parallelized and can run with as low as just a few kilobytes of memory.\n\nPEAR evaluates all possible paired-end read overlaps and without requiring the target fragment size as input. In addition, it implements a statistical test for minimizing false-positive results. Together with a highly optimized implementation, it can merge millions of paired end reads within a couple of minutes on a standard desktop computer.\n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/pear/nextflow_schema.json b/target/nextflow/pear/nextflow_schema.json new file mode 100644 index 00000000..77dee5c8 --- /dev/null +++ b/target/nextflow/pear/nextflow_schema.json @@ -0,0 +1,284 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "pear", +"description": "PEAR is an ultrafast, memory-efficient and highly accurate pair-end read merger. It is fully parallelized and can run with as low as just a few kilobytes of memory.\n\nPEAR evaluates all possible paired-end read overlaps and without requiring the target fragment size as input. In addition, it implements a statistical test for minimizing false-positive results. Together with a highly optimized implementation, it can merge millions of paired end reads within a couple of minutes on a standard desktop computer.\n", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "forward_fastq": { + "type": + "string", + "description": "Type: `file`, required, example: `forward.fastq`. Forward paired-end FASTQ file", + "help_text": "Type: `file`, required, example: `forward.fastq`. Forward paired-end FASTQ file" + + } + + + , + "reverse_fastq": { + "type": + "string", + "description": "Type: `file`, required, example: `reverse.fastq`. Reverse paired-end FASTQ file", + "help_text": "Type: `file`, required, example: `reverse.fastq`. Reverse paired-end FASTQ file" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "assembled": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.assembled.assembled`. The output file containing assembled reads", + "help_text": "Type: `file`, required, default: `$id.$key.assembled.assembled`. The output file containing assembled reads. Can be compressed with gzip." + , + "default": "$id.$key.assembled.assembled" + } + + + , + "unassembled_forward": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.unassembled_forward.unassembled_forward`. The output file containing forward reads that could not be assembled", + "help_text": "Type: `file`, required, default: `$id.$key.unassembled_forward.unassembled_forward`. The output file containing forward reads that could not be assembled. Can be compressed with gzip." + , + "default": "$id.$key.unassembled_forward.unassembled_forward" + } + + + , + "unassembled_reverse": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.unassembled_reverse.unassembled_reverse`. The output file containing reverse reads that could not be assembled", + "help_text": "Type: `file`, required, default: `$id.$key.unassembled_reverse.unassembled_reverse`. The output file containing reverse reads that could not be assembled. Can be compressed with gzip." + , + "default": "$id.$key.unassembled_reverse.unassembled_reverse" + } + + + , + "discarded": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.discarded.discarded`. The output file containing reads that were discarded due to too low quality or too many uncalled bases", + "help_text": "Type: `file`, required, default: `$id.$key.discarded.discarded`. The output file containing reads that were discarded due to too low quality or too many uncalled bases. Can be compressed with gzip." + , + "default": "$id.$key.discarded.discarded" + } + + +} +}, + + + "arguments" : { + "title": "Arguments", + "type": "object", + "description": "No description", + "properties": { + + + "p_value": { + "type": + "number", + "description": "Type: `double`, example: `0.01`. Specify a p-value for the statistical test", + "help_text": "Type: `double`, example: `0.01`. Specify a p-value for the statistical test. If the computed p-value of a possible assembly exceeds the specified p-value then paired-end read will not be assembled. Valid options are: 0.0001, 0.001, 0.01, 0.05 and 1.0. Setting 1.0 disables the test.\n" + + } + + + , + "min_overlap": { + "type": + "integer", + "description": "Type: `integer`, example: `10`. Specify the minimum overlap size", + "help_text": "Type: `integer`, example: `10`. Specify the minimum overlap size. The minimum overlap may be set to 1 when the statistical test is used. However, further restricting the minimum overlap size to a proper value may reduce false-positive assembles.\n" + + } + + + , + "max_assembly_length": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Specify the maximum possible length of the assembled sequences", + "help_text": "Type: `integer`, example: `0`. Specify the maximum possible length of the assembled sequences. Setting this value to 0 disables the restriction and assembled sequences may be arbitrary long.\n" + + } + + + , + "min_assembly_length": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Specify the minimum possible length of the assembled sequences", + "help_text": "Type: `integer`, example: `0`. Specify the minimum possible length of the assembled sequences. Setting this value to 0 disables the restriction and assembled sequences may be arbitrary short.\n" + + } + + + , + "min_trim_length": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. Specify the minimum length of reads after trimming the low quality part (see option -q)\n", + "help_text": "Type: `integer`, example: `1`. Specify the minimum length of reads after trimming the low quality part (see option -q)\n" + + } + + + , + "quality_threshold": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Specify the quality threshold for trimming the low quality part of a read", + "help_text": "Type: `integer`, example: `0`. Specify the quality threshold for trimming the low quality part of a read. If the quality scores of two consecutive bases are strictly less than the specified threshold, the rest of the read will be trimmed.\n" + + } + + + , + "max_uncalled_base": { + "type": + "number", + "description": "Type: `double`, example: `1.0`. Specify the maximal proportion of uncalled bases in a read", + "help_text": "Type: `double`, example: `1.0`. Specify the maximal proportion of uncalled bases in a read. Setting this value to 0 will cause PEAR to discard all reads containing uncalled bases. The other extreme setting is 1 which causes PEAR to process all reads independent on the number of uncalled bases.\n" + + } + + + , + "test_method": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. Specify the type of statistical test", + "help_text": "Type: `integer`, example: `1`. Specify the type of statistical test. Two options are available. 1: Given the minimum allowed overlap, test using the highest OES. Note that due to its discrete nature, this test usually yields a lower p-value for the assembled read than the cut- off (specified by -p). For example, setting the cut-off to 0.05 using this test, the assembled reads might have an actual p-value of 0.02.\n2. Use the acceptance probability (m.a.p). This test methods computes the same probability as test method 1. However, it assumes that the minimal overlap is the observed overlap with the highest OES, instead of the one specified by -v. Therefore, this is not a valid statistical test and the \u0027p-value\u0027 is in fact the maximal probability for accepting the assembly. Nevertheless, we observed in practice that for the case the actual overlap sizes are relatively small, test 2 can correctly assemble more reads with only slightly higher false-positive rate.\n" + + } + + + , + "emperical_freqs": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Disable empirical base frequencies", + "help_text": "Type: `boolean_true`, default: `false`. Disable empirical base frequencies.\n" + , + "default": "False" + } + + + , + "score_method": { + "type": + "integer", + "description": "Type: `integer`, example: `2`. Specify the scoring method", + "help_text": "Type: `integer`, example: `2`. Specify the scoring method. 1. OES with +1 for match and -1 for mismatch. 2: Assembly score (AS). Use +1 for match and -1 for mismatch multiplied by base quality scores. 3: Ignore quality scores and use +1 for a match and -1 for a mismatch.\n" + + } + + + , + "phred_base": { + "type": + "integer", + "description": "Type: `integer`, example: `33`. Base PHRED quality score", + "help_text": "Type: `integer`, example: `33`. Base PHRED quality score.\n" + + } + + + , + "cap": { + "type": + "integer", + "description": "Type: `integer`, example: `40`. Specify the upper bound for the resulting quality score", + "help_text": "Type: `integer`, example: `40`. Specify the upper bound for the resulting quality score. If set to zero, capping is disabled.\n" + + } + + + , + "nbase": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. When merging a base-pair that consists of two non-equal bases out of which none is degenerate, set the merged base to N and use the highest quality score of the two bases\n", + "help_text": "Type: `boolean_true`, default: `false`. When merging a base-pair that consists of two non-equal bases out of which none is degenerate, set the merged base to N and use the highest quality score of the two bases\n" + , + "default": "False" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/arguments" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/salmon/salmon_index/.config.vsh.yaml b/target/nextflow/salmon/salmon_index/.config.vsh.yaml new file mode 100644 index 00000000..e91ccd89 --- /dev/null +++ b/target/nextflow/salmon/salmon_index/.config.vsh.yaml @@ -0,0 +1,289 @@ +name: "salmon_index" +namespace: "salmon" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--genome" + description: "Genome of the organism to prepare the set of decoy sequences. Required\ + \ to build decoy-aware transccriptome.\n" + info: null + example: + - "genome.fasta" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--transcripts" + alternatives: + - "-t" + description: "Transcript fasta file.\n" + info: null + example: + - "transcriptome.fasta" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--kmer_len" + alternatives: + - "-k" + description: "The size of k-mers that should be used for the quasi index.\n" + info: null + example: + - 31 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--gencode" + description: "This flag will expect the input transcript fasta to be in GENCODE\ + \ format, and will split the transcript name at the first '|' character. These\ + \ reduced names will be used in the output and when looking for these transcripts\ + \ in a gene to transcript GTF.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--features" + description: "This flag will expect the input reference to be in the tsv file\ + \ format, and will split the feature name at the first 'tab' character. These\ + \ reduced names will be used in the output and when looking for the sequence\ + \ of the features.GTF.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--keep_duplicates" + description: "This flag will disable the default indexing behavior of discarding\ + \ sequence-identical duplicate transcripts. If this flag is passed, then duplicate\ + \ transcripts that appear in the input will be retained and quantified separately.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--keep_fixed_fasta" + description: "Retain the fixed fasta file (without short transcripts and duplicates,\ + \ clipped, etc.) generated during indexing.\n" + info: null + direction: "input" + - type: "integer" + name: "--filter_size" + alternatives: + - "-f" + description: "The size of the Bloom filter that will be used by TwoPaCo during\ + \ indexing. The filter will be of size 2^{filter_size}. The default value of\ + \ -1 means that the filter size will be automatically set based on the number\ + \ of distinct k-mers in the input, as estimated by nthll.\n" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--sparse" + description: "Build the index using a sparse sampling of k-mer positions This\ + \ will require less memory (especially during quantification), but will take\ + \ longer to construct and can slow down mapping / alignment.\n" + info: null + direction: "input" + - type: "file" + name: "--decoys" + alternatives: + - "-d" + description: "Treat these sequences ids from the reference as the decoys that\ + \ may have sequence homologous to some known transcript. For example in case\ + \ of the genome, provide a list of chromosome names (one per line).\n" + info: null + example: + - "decoys.txt" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_clip" + description: "Don't clip poly-A tails from the ends of target sequences.\n" + info: null + direction: "input" + - type: "string" + name: "--type" + alternatives: + - "-n" + description: "The type of index to build; the only option is \"puff\" in this\ + \ version of salmon.\n" + info: null + example: + - "puff" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output" + arguments: + - type: "file" + name: "--index" + alternatives: + - "-i" + description: "Salmon index\n" + info: null + example: + - "Salmon_index" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Salmon is a tool for wicked-fast transcript quantification from RNA-seq\ + \ data. It can either make use of pre-computed alignments (in the form of a SAM/BAM\ + \ file) to the transcripts rather than the raw reads, or can be run in the mapping-based\ + \ mode. This component creates a salmon index for the transcriptome to use Salmon\ + \ in the mapping-based mode. It is generally recommend that you build a decoy-aware\ + \ transcriptome file. This is done using the entire genome of the organism as the\ + \ decoy sequence by concatenating the genome to the end of the transcriptome to\ + \ be indexed and populating the decoys.txt file with the chromosome names.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "Transcriptome" +- "Index" +license: "GPL-3.0" +references: + doi: + - "10.1038/nmeth.4197" +links: + repository: "https://github.com/COMBINE-lab/salmon" + homepage: "https://salmon.readthedocs.io/en/latest/salmon.html" + documentation: "https://salmon.readthedocs.io/en/latest/salmon.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/salmon:1.10.2--hecfa306_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "salmon index -v 2>&1 | sed 's/salmon \\([0-9.]*\\)/salmon: \\1/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/salmon/salmon_index/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/salmon/salmon_index" + executable: "target/nextflow/salmon/salmon_index/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/salmon/salmon_index/main.nf b/target/nextflow/salmon/salmon_index/main.nf new file mode 100644 index 00000000..d75113e7 --- /dev/null +++ b/target/nextflow/salmon/salmon_index/main.nf @@ -0,0 +1,3677 @@ +// salmon_index main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "salmon_index", + "namespace" : "salmon", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--genome", + "description" : "Genome of the organism to prepare the set of decoy sequences. Required to build decoy-aware transccriptome.\n", + "example" : [ + "genome.fasta" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--transcripts", + "alternatives" : [ + "-t" + ], + "description" : "Transcript fasta file.\n", + "example" : [ + "transcriptome.fasta" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--kmer_len", + "alternatives" : [ + "-k" + ], + "description" : "The size of k-mers that should be used for the quasi index.\n", + "example" : [ + 31 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--gencode", + "description" : "This flag will expect the input transcript fasta to be in GENCODE format, and will split the transcript name at the first '|' character. These reduced names will be used in the output and when looking for these transcripts in a gene to transcript GTF.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--features", + "description" : "This flag will expect the input reference to be in the tsv file format, and will split the feature name at the first 'tab' character. These reduced names will be used in the output and when looking for the sequence of the features.GTF.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--keep_duplicates", + "description" : "This flag will disable the default indexing behavior of discarding sequence-identical duplicate transcripts. If this flag is passed, then duplicate transcripts that appear in the input will be retained and quantified separately.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--keep_fixed_fasta", + "description" : "Retain the fixed fasta file (without short transcripts and duplicates, clipped, etc.) generated during indexing.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--filter_size", + "alternatives" : [ + "-f" + ], + "description" : "The size of the Bloom filter that will be used by TwoPaCo during indexing. The filter will be of size 2^{filter_size}. The default value of -1 means that the filter size will be automatically set based on the number of distinct k-mers in the input, as estimated by nthll.\n", + "example" : [ + -1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--sparse", + "description" : "Build the index using a sparse sampling of k-mer positions This will require less memory (especially during quantification), but will take longer to construct and can slow down mapping / alignment.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--decoys", + "alternatives" : [ + "-d" + ], + "description" : "Treat these sequences ids from the reference as the decoys that may have sequence homologous to some known transcript. For example in case of the genome, provide a list of chromosome names (one per line).\n", + "example" : [ + "decoys.txt" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--no_clip", + "description" : "Don't clip poly-A tails from the ends of target sequences.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--type", + "alternatives" : [ + "-n" + ], + "description" : "The type of index to build; the only option is \\"puff\\" in this version of salmon.\n", + "example" : [ + "puff" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Output", + "arguments" : [ + { + "type" : "file", + "name" : "--index", + "alternatives" : [ + "-i" + ], + "description" : "Salmon index\n", + "example" : [ + "Salmon_index" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Salmon is a tool for wicked-fast transcript quantification from RNA-seq data. It can either make use of pre-computed alignments (in the form of a SAM/BAM file) to the transcripts rather than the raw reads, or can be run in the mapping-based mode. This component creates a salmon index for the transcriptome to use Salmon in the mapping-based mode. It is generally recommend that you build a decoy-aware transcriptome file. This is done using the entire genome of the organism as the decoy sequence by concatenating the genome to the end of the transcriptome to be indexed and populating the decoys.txt file with the chromosome names.\n", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "Transcriptome", + "Index" + ], + "license" : "GPL-3.0", + "references" : { + "doi" : [ + "10.1038/nmeth.4197" + ] + }, + "links" : { + "repository" : "https://github.com/COMBINE-lab/salmon", + "homepage" : "https://salmon.readthedocs.io/en/latest/salmon.html", + "documentation" : "https://salmon.readthedocs.io/en/latest/salmon.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/salmon:1.10.2--hecfa306_0", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "salmon index -v 2>&1 | sed 's/salmon \\\\([0-9.]*\\\\)/salmon: \\\\1/' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/salmon/salmon_index/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/salmon/salmon_index", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +set -e + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_GENOME+x} ]; then echo "${VIASH_PAR_GENOME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_genome='&'#" ; else echo "# par_genome="; fi ) +$( if [ ! -z ${VIASH_PAR_TRANSCRIPTS+x} ]; then echo "${VIASH_PAR_TRANSCRIPTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_transcripts='&'#" ; else echo "# par_transcripts="; fi ) +$( if [ ! -z ${VIASH_PAR_KMER_LEN+x} ]; then echo "${VIASH_PAR_KMER_LEN}" | sed "s#'#'\\"'\\"'#g;s#.*#par_kmer_len='&'#" ; else echo "# par_kmer_len="; fi ) +$( if [ ! -z ${VIASH_PAR_GENCODE+x} ]; then echo "${VIASH_PAR_GENCODE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_gencode='&'#" ; else echo "# par_gencode="; fi ) +$( if [ ! -z ${VIASH_PAR_FEATURES+x} ]; then echo "${VIASH_PAR_FEATURES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_features='&'#" ; else echo "# par_features="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_DUPLICATES+x} ]; then echo "${VIASH_PAR_KEEP_DUPLICATES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_keep_duplicates='&'#" ; else echo "# par_keep_duplicates="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_FIXED_FASTA+x} ]; then echo "${VIASH_PAR_KEEP_FIXED_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_keep_fixed_fasta='&'#" ; else echo "# par_keep_fixed_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTER_SIZE+x} ]; then echo "${VIASH_PAR_FILTER_SIZE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_filter_size='&'#" ; else echo "# par_filter_size="; fi ) +$( if [ ! -z ${VIASH_PAR_SPARSE+x} ]; then echo "${VIASH_PAR_SPARSE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sparse='&'#" ; else echo "# par_sparse="; fi ) +$( if [ ! -z ${VIASH_PAR_DECOYS+x} ]; then echo "${VIASH_PAR_DECOYS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_decoys='&'#" ; else echo "# par_decoys="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_CLIP+x} ]; then echo "${VIASH_PAR_NO_CLIP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_clip='&'#" ; else echo "# par_no_clip="; fi ) +$( if [ ! -z ${VIASH_PAR_TYPE+x} ]; then echo "${VIASH_PAR_TYPE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_type='&'#" ; else echo "# par_type="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX+x} ]; then echo "${VIASH_PAR_INDEX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_index='&'#" ; else echo "# par_index="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +[[ "\\$par_gencode" == "false" ]] && unset par_gencode +[[ "\\$par_features" == "false" ]] && unset par_features +[[ "\\$par_keep_duplicates" == "false" ]] && unset par_keep_duplicates +[[ "\\$par_keep_fixed_fasta" == "false" ]] && unset par_keep_fixed_fasta +[[ "\\$par_sparse" == "false" ]] && unset par_sparse +[[ "\\$par_no_clip" == "false" ]] && unset par_no_clip + +tmp_dir=\\$(mktemp -d -p "\\$meta_temp_dir" "\\${meta_functionality_name}_XXXXXX") +mkdir -p "\\$tmp_dir/temp" + +if [[ -f "\\$par_genome" ]] && [[ ! "\\$par_decoys" ]]; then + filename="\\$(basename -- \\$par_genome)" + decoys="decoys.txt" + if [ \\${filename##*.} == "gz" ]; then + grep '^>' <(gunzip -c \\$par_genome) | cut -d ' ' -f 1 > \\$decoys + gentrome="gentrome.fa.gz" + else + grep '^>' \\$par_genome | cut -d ' ' -f 1 > \\$decoys + gentrome="gentrome.fa" + fi + sed -i.bak -e 's/>//g' \\$decoys + cat \\$par_transcripts \\$par_genome > \\$gentrome +else + gentrome=\\$par_transcripts + decoys=\\$par_decoys +fi + +salmon index \\\\ + -t "\\$gentrome" \\\\ + --tmpdir "\\$tmp_dir/temp" \\\\ + \\${meta_cpus:+--threads "\\${meta_cpus}"} \\\\ + -i "\\$par_index" \\\\ + \\${par_kmer_len:+-k "\\${par_kmer_len}"} \\\\ + \\${par_gencode:+--gencode} \\\\ + \\${par_features:+--features} \\\\ + \\${par_keep_duplicates:+--keepDuplicates} \\\\ + \\${par_keep_fixed_fasta:+--keepFixedFasta} \\\\ + \\${par_filter_size:+-f "\\${par_filter_size}"} \\\\ + \\${par_sparse:+--sparse} \\\\ + \\${decoys:+-d "\\${decoys}"} \\\\ + \\${par_no_clip:+--no-clip} \\\\ + \\${par_type:+--type "\\${par_type}"} +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/salmon/salmon_index", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/salmon/salmon_index/nextflow.config b/target/nextflow/salmon/salmon_index/nextflow.config new file mode 100644 index 00000000..e05d080c --- /dev/null +++ b/target/nextflow/salmon/salmon_index/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'salmon/salmon_index' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Salmon is a tool for wicked-fast transcript quantification from RNA-seq data. It can either make use of pre-computed alignments (in the form of a SAM/BAM file) to the transcripts rather than the raw reads, or can be run in the mapping-based mode. This component creates a salmon index for the transcriptome to use Salmon in the mapping-based mode. It is generally recommend that you build a decoy-aware transcriptome file. This is done using the entire genome of the organism as the decoy sequence by concatenating the genome to the end of the transcriptome to be indexed and populating the decoys.txt file with the chromosome names.\n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/salmon/salmon_index/nextflow_schema.json b/target/nextflow/salmon/salmon_index/nextflow_schema.json new file mode 100644 index 00000000..3018d516 --- /dev/null +++ b/target/nextflow/salmon/salmon_index/nextflow_schema.json @@ -0,0 +1,211 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "salmon_index", +"description": "Salmon is a tool for wicked-fast transcript quantification from RNA-seq data. It can either make use of pre-computed alignments (in the form of a SAM/BAM file) to the transcripts rather than the raw reads, or can be run in the mapping-based mode. This component creates a salmon index for the transcriptome to use Salmon in the mapping-based mode. It is generally recommend that you build a decoy-aware transcriptome file. This is done using the entire genome of the organism as the decoy sequence by concatenating the genome to the end of the transcriptome to be indexed and populating the decoys.txt file with the chromosome names.\n", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "genome": { + "type": + "string", + "description": "Type: `file`, example: `genome.fasta`. Genome of the organism to prepare the set of decoy sequences", + "help_text": "Type: `file`, example: `genome.fasta`. Genome of the organism to prepare the set of decoy sequences. Required to build decoy-aware transccriptome.\n" + + } + + + , + "transcripts": { + "type": + "string", + "description": "Type: `file`, required, example: `transcriptome.fasta`. Transcript fasta file", + "help_text": "Type: `file`, required, example: `transcriptome.fasta`. Transcript fasta file.\n" + + } + + + , + "kmer_len": { + "type": + "integer", + "description": "Type: `integer`, example: `31`. The size of k-mers that should be used for the quasi index", + "help_text": "Type: `integer`, example: `31`. The size of k-mers that should be used for the quasi index.\n" + + } + + + , + "gencode": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. This flag will expect the input transcript fasta to be in GENCODE format, and will split the transcript name at the first \u0027|\u0027 character", + "help_text": "Type: `boolean_true`, default: `false`. This flag will expect the input transcript fasta to be in GENCODE format, and will split the transcript name at the first \u0027|\u0027 character. These reduced names will be used in the output and when looking for these transcripts in a gene to transcript GTF.\n" + , + "default": "False" + } + + + , + "features": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. This flag will expect the input reference to be in the tsv file format, and will split the feature name at the first \u0027tab\u0027 character", + "help_text": "Type: `boolean_true`, default: `false`. This flag will expect the input reference to be in the tsv file format, and will split the feature name at the first \u0027tab\u0027 character. These reduced names will be used in the output and when looking for the sequence of the features.GTF.\n" + , + "default": "False" + } + + + , + "keep_duplicates": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. This flag will disable the default indexing behavior of discarding sequence-identical duplicate transcripts", + "help_text": "Type: `boolean_true`, default: `false`. This flag will disable the default indexing behavior of discarding sequence-identical duplicate transcripts. If this flag is passed, then duplicate transcripts that appear in the input will be retained and quantified separately.\n" + , + "default": "False" + } + + + , + "keep_fixed_fasta": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Retain the fixed fasta file (without short transcripts and duplicates, clipped, etc", + "help_text": "Type: `boolean_true`, default: `false`. Retain the fixed fasta file (without short transcripts and duplicates, clipped, etc.) generated during indexing.\n" + , + "default": "False" + } + + + , + "filter_size": { + "type": + "integer", + "description": "Type: `integer`, example: `-1`. The size of the Bloom filter that will be used by TwoPaCo during indexing", + "help_text": "Type: `integer`, example: `-1`. The size of the Bloom filter that will be used by TwoPaCo during indexing. The filter will be of size 2^{filter_size}. The default value of -1 means that the filter size will be automatically set based on the number of distinct k-mers in the input, as estimated by nthll.\n" + + } + + + , + "sparse": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Build the index using a sparse sampling of k-mer positions This will require less memory (especially during quantification), but will take longer to construct and can slow down mapping / alignment", + "help_text": "Type: `boolean_true`, default: `false`. Build the index using a sparse sampling of k-mer positions This will require less memory (especially during quantification), but will take longer to construct and can slow down mapping / alignment.\n" + , + "default": "False" + } + + + , + "decoys": { + "type": + "string", + "description": "Type: `file`, example: `decoys.txt`. Treat these sequences ids from the reference as the decoys that may have sequence homologous to some known transcript", + "help_text": "Type: `file`, example: `decoys.txt`. Treat these sequences ids from the reference as the decoys that may have sequence homologous to some known transcript. For example in case of the genome, provide a list of chromosome names (one per line).\n" + + } + + + , + "no_clip": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Don\u0027t clip poly-A tails from the ends of target sequences", + "help_text": "Type: `boolean_true`, default: `false`. Don\u0027t clip poly-A tails from the ends of target sequences.\n" + , + "default": "False" + } + + + , + "type": { + "type": + "string", + "description": "Type: `string`, example: `puff`. The type of index to build; the only option is \"puff\" in this version of salmon", + "help_text": "Type: `string`, example: `puff`. The type of index to build; the only option is \"puff\" in this version of salmon.\n" + + } + + +} +}, + + + "output" : { + "title": "Output", + "type": "object", + "description": "No description", + "properties": { + + + "index": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.index.index`, example: `Salmon_index`. Salmon index\n", + "help_text": "Type: `file`, required, default: `$id.$key.index.index`, example: `Salmon_index`. Salmon index\n" + , + "default": "$id.$key.index.index" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/output" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/salmon/salmon_quant/.config.vsh.yaml b/target/nextflow/salmon/salmon_quant/.config.vsh.yaml new file mode 100644 index 00000000..39a27b49 --- /dev/null +++ b/target/nextflow/salmon/salmon_quant/.config.vsh.yaml @@ -0,0 +1,1185 @@ +name: "salmon_quant" +namespace: "salmon" +version: "main" +argument_groups: +- name: "Common input options" + arguments: + - type: "string" + name: "--lib_type" + alternatives: + - "-l" + description: "Format string describing the library.\nThe library type string consists\ + \ of three parts: \n1. Relative orientation of the reads: This part is only\ + \ provided if the library is paired-end, THe possible options are\n I = inward\n\ + \ O = outward\n M = matching\n2. Strandedness of the library: This part specifies\ + \ whether the protocol is stranded or unstranded. The options are:\n S = stranded\n\ + \ U = unstranded\n3. Directionality of the reads: If the library is stranded,\ + \ the final part of the library string is used to specify the strand from which\ + \ the read originates. The possible values are\n F = read 1 (or single-end\ + \ read) comes from the forward strand\n R = read 1 (or single-end read) comes\ + \ from the reverse strand\n" + info: null + default: + - "A" + required: false + choices: + - "A" + - "U" + - "SF" + - "SR" + - "IU" + - "IS" + - "ISF" + - "ISR" + - "OU" + - "OS" + - "OSF" + - "OSR" + - "MU" + - "MS" + - "MSF" + - "MSR" + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Mapping input options" + arguments: + - type: "file" + name: "--index" + alternatives: + - "-i" + description: "Salmon index.\n" + info: null + example: + - "transcriptome_index" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unmated_reads" + alternatives: + - "-r" + description: "List of files containing unmated reads of (e.g. single-end reads).\n" + info: null + example: + - "sample.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--mates1" + alternatives: + - "-m1" + description: "File containing the #1 mates.\n" + info: null + example: + - "sample_1.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--mates2" + alternatives: + - "-m2" + description: "File containing the #2 mates.\n" + info: null + example: + - "sample_2.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Alignment input options" + arguments: + - type: "boolean_true" + name: "--discard_orphans" + description: "Discard orphan alignments in the input [for alignment-based mode\ + \ only]. If this flag is passed, then only paired alignments will be considered\ + \ toward quantification estimates. The default behavior is to consider orphan\ + \ alignments if no valid paired mappings exist.\n" + info: null + direction: "input" + - type: "file" + name: "--alignments" + alternatives: + - "-a" + description: "Input alignment (BAM) file(s).\n" + info: null + example: + - "sample.fq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--eqclasses" + alternatives: + - "-e" + description: "input salmon weighted equivalence class file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--targets" + alternatives: + - "-t" + description: "FASTA format file containing target transcripts.\n" + info: null + example: + - "transcripts.fasta" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--ont" + description: "Use alignment model for Oxford Nanopore long reads\n" + info: null + direction: "input" +- name: "Output" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output quantification directory.\n" + info: null + example: + - "quant_output" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--quant_results" + description: "Salmon quantification file.\n" + info: null + example: + - "quant.sf" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Basic options" + arguments: + - type: "boolean_true" + name: "--seq_bias" + description: "Perform sequence-specific bias correction.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--gc_bias" + description: "Perform fragment GC bias correction [beta for single-end reads].\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--pos_bias" + description: "Perform positional bias correction.\n" + info: null + direction: "input" + - type: "double" + name: "--incompat_prior" + description: "Set the prior probability that an alignment that disagrees with\ + \ the specified library type (--lib_type) results from the true fragment origin.\ + \ Setting this to 0 specifies that alignments that disagree with the library\ + \ type should be \"impossible\", while setting it to 1 says that alignments\ + \ that disagree with the library type are no less likely than those that do.\n" + info: null + example: + - 0.0 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--gene_map" + alternatives: + - "-g" + description: "File containing a mapping of transcripts to genes. If this file\ + \ is provided salmon will output both quant.sf and quant.genes.sf files, where\ + \ the latter contains aggregated gene-level abundance estimates. The transcript\ + \ to gene mapping should be provided as either a GTF file, or a in a simple\ + \ tab-delimited format where each line contains the name of a transcript and\ + \ the gene to which it belongs separated by a tab. The extension of the file\ + \ is used to determine how the file should be parsed. Files ending in '.gtf',\ + \ '.gff' or '.gff3' are assumed to be in GTF format; files with any other extension\ + \ are assumed to be in the simple format. In GTF / GFF format, the \"transcript_id\"\ + \ is assumed to contain the transcript identifier and the \"gene_id\" is assumed\ + \ to contain the corresponding gene identifier.\n" + info: null + example: + - "gene_map.gtf" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--aux_target_file" + description: "A file containing a list of \"auxiliary\" targets. These are valid\ + \ targets (i.e., not decoys) to which fragments are allowed to map and be assigned,\ + \ and which will be quantified, but for which auxiliary models like sequence-specific\ + \ and fragment-GC bias correction should not be applied.\n" + info: null + example: + - "auxilary_targets.txt" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--meta" + description: "If you're using Salmon on a metagenomic dataset, consider setting\ + \ this flag to disable parts of the abundance estimation model that make less\ + \ sense for metagenomic data.\n" + info: null + direction: "input" + - type: "double" + name: "--score_exp" + description: "The factor by which sub-optimal alignment scores are downweighted\ + \ to produce a probability. If the best alignment score for the current read\ + \ is S, and the score for a particular alignment is w, then the probability\ + \ will be computed porportional to exp( - scoreExp * (S-w) ).\n" + info: null + example: + - 1.0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Options specific to mapping mode" + arguments: + - type: "boolean_true" + name: "--discard_orphans_quasi" + description: "[selective-alignment mode only] \nDiscard orphan mappings in selective-alignment\ + \ mode. If this flag is passed then only paired mappings will be considered\ + \ toward quantification estimates. The default behavior is to consider orphan\ + \ mappings if no valid paired mappings exist. This flag is independent of the\ + \ option to write the orphaned mappings to file (--writeOrphanLinks).\n" + info: null + direction: "input" + - type: "double" + name: "--consensus_slack" + description: "[selective-alignment mode only] \nThe amount of slack allowed in\ + \ the selective-alignment filtering mechanism. If this is set to a fraction,\ + \ X, greater than 0 (and in [0,1)), then uniMEM chains with scores below (100\ + \ * X)% of the best chain score for a read, and read pairs with a sum of chain\ + \ scores below (100 * X)% of the best chain score for a read pair will be discounted\ + \ as a mapping candidates. The default value of this option is 0.35.\n" + info: null + example: + - 0.35 + required: false + min: 0.0 + max: 0.999999999 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--pre_merge_chain_sub_thresh" + description: "[selective-alignment mode only] \nThe threshold of sub-optimal chains,\ + \ compared to the best chain on a given target, that will be retained and passed\ + \ to the next phase of mapping. Specifically, if the best chain for a read (or\ + \ read-end in paired-end mode) to target t has score X_t, then all chains for\ + \ this read with score >= X_t * preMergeChainSubThresh will be retained and\ + \ passed to subsequent mapping phases. This value must be in the range [0,\ + \ 1].\n" + info: null + example: + - 0.75 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--post_merge_chain_sub_thresh" + description: "[selective-alignment mode only] \nThe threshold of sub-optimal chains,\ + \ compared to the best chain on a given target, that will be retained and passed\ + \ to the next phase of mapping. This is different than post_merge_chain_sub_thresh,\ + \ because this is applied to pairs of chains (from the ends of paired-end reads)\ + \ after merging (i.e. after checking concordancy constraints etc.). Specifically,\ + \ if the best chain pair to target t has score X_t, then all chain pairs for\ + \ this read pair with score >= X_t * post_merge_chain_sub_thresh will be retained\ + \ and passed to subsequent mapping phases. This value must be in the range [0,\ + \ 1]. Note: This option is only meaningful for paired-end libraries, and is\ + \ ignored for single-end libraries.\n" + info: null + example: + - 0.9 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--orphan_chain_sub_thresh" + description: "[selective-alignment mode only]\nThis threshold sets a global sub-optimality\ + \ threshold for chains corresponding to orphan mappings. That is, if the merging\ + \ procedure results in no concordant mappings then only orphan mappings with\ + \ a chain score >= orphan_chain_sub_thresh * bestChainScore will be retained\ + \ and passed to subsequent mapping phases. This value must be in the range [0,\ + \ 1]. Note: This option is only meaningful for paired-end libraries, and is\ + \ ignored for single-end libraries.\n" + info: null + example: + - 0.95 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--min_score_fraction" + description: "[selective-alignment mode only]\nThe fraction of the optimal possible\ + \ alignment score that a mapping must achieve in order to be considered \"valid\"\ + \ --- should be in (0,1]. Default 0.65\n" + info: null + example: + - 0.65 + required: false + min: 1.0E-9 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--mismatch_seed_skip" + description: "[selective-alignment mode only]\nAfter a k-mer hit is extended to\ + \ a uni-MEM, the uni-MEM extension can terminate for one of 3 reasons; the end\ + \ of the read, the end of the unitig, or a mismatch. If the extension ends because\ + \ of a mismatch, this is likely the result of a sequencing error. To avoid looking\ + \ up many k-mers that will likely fail to be located in the index, the search\ + \ procedure skips by a factor of mismatch_seed_skip until it either (1) finds\ + \ another match or (2) is k-bases past the mismatch position. This value controls\ + \ that skip length. A smaller value can increase sensitivity, while a larger\ + \ value can speed up seeding.\n" + info: null + example: + - 3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--disable_chaining_heuristic" + description: "[selective-alignment mode only] \nBy default, the heuristic of (Li\ + \ 2018) is implemented, which terminates the chaining DP once a given number\ + \ of valid backpointers are found. This speeds up the seed (MEM) chaining step,\ + \ but may result in sub-optimal chains in complex situations (e.g. sequences\ + \ with many repeats and overlapping repeats). Passing this flag will disable\ + \ the chaining heuristic, and perform the full chaining dynamic program, guaranteeing\ + \ the optimal chain is found in this step.\n" + info: null + direction: "input" + - type: "double" + name: "--decoy_threshold" + description: "[selective-alignment mode only]\nFor an alignemnt to an annotated\ + \ transcript to be considered invalid, it must have an alignment score < (decoy_threshold\ + \ * bestDecoyScore). A value of 1.0 means that any alignment strictly worse\ + \ than the best decoy alignment will be discarded. A smaller value will allow\ + \ reads to be allocated to transcripts even if they strictly align better to\ + \ the decoy sequence.\n" + info: null + example: + - 1.0 + required: false + min: 0.0 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--ma" + description: "[selective-alignment mode only]\nThe value given to a match between\ + \ read and reference nucleotides in an alignment.\n" + info: null + example: + - 2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--mp" + description: "[selective-alignment mode only]\nThe value given to a mis-match\ + \ between read and reference nucleotides in an alignment.\n" + info: null + example: + - -4 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--go" + description: "[selective-alignment mode only]\nThe value given to a gap opening\ + \ in an alignment.\n" + info: null + example: + - 6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--ge" + description: "[selective-alignment mode only]\nThe value given to a gap extension\ + \ in an alignment.\n" + info: null + example: + - 2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bandwidth" + description: "[selective-alignment mode only]\nThe value used for the bandwidth\ + \ passed to ksw2. A smaller bandwidth can make the alignment verification run\ + \ more quickly, but could possibly miss valid alignments.\n" + info: null + example: + - 15 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--allow_dovetail" + description: "[selective-alignment mode only] \nAllow dovetailing mappings.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--recover_orphans" + description: "[selective-alignment mode only] \nAttempt to recover the mates of\ + \ orphaned reads. This uses edlib for orphan recovery, and so introduces some\ + \ computational overhead, but it can improve sensitivity.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--mimicBT2" + description: "[selective-alignment mode only] \nSet flags to mimic parameters\ + \ similar to Bowtie2 with --no-discordant and --no-mixed flags. This increases\ + \ disallows dovetailing reads, and discards orphans. Note, this does not impose\ + \ the very strict parameters assumed by RSEM+Bowtie2, like gapless alignments.\ + \ For that behavior, use the --mimic_strictBT2 flag below.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--mimic_strictBT2" + description: "[selective-alignment mode only] \nSet flags to mimic the very strict\ + \ parameters used by RSEM+Bowtie2. This increases --min_score_fraction to 0.8,\ + \ disallows dovetailing reads, discards orphans, and disallows gaps in alignments.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--softclip" + description: "[selective-alignment mode only] \nAllos soft-clipping of reads during\ + \ selective-alignment. If this option is provided, then regions at the beginning\ + \ or end of the read can be withheld from alignment without any effect on the\ + \ resulting score (i.e. neither adding nor removing from the score). This will\ + \ drastically reduce the penalty if there are mismatches at the beginning or\ + \ end of the read due to e.g. low-quality bases or adapters. NOTE: Even with\ + \ soft-clipping enabled, the read must still achieve a score of at least min_score_fraction\ + \ * maximum achievable score, where the maximum achievable score is computed\ + \ based on the full (un-clipped) read length.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--softclip_overhangs" + description: "[selective-alignment mode only] \nAllow soft-clipping of reads that\ + \ overhang the beginning or ends of the transcript. In this case, the overhaning\ + \ section of the read will simply be unaligned, and will not contribute or detract\ + \ from the alignment score. The default policy is to force an end-to-end alignment\ + \ of the entire read, so that overhanings will result in some deletion of nucleotides\ + \ from the read.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--full_length_alignment" + description: "[selective-alignment mode only] \nPerform selective alignment over\ + \ the full length of the read, beginning from the (approximate) initial mapping\ + \ location and using extension alignment. This is in contrast with the default\ + \ behavior which is to only perform alignment between the MEMs in the optimal\ + \ chain (and before the first and after the last MEM if applicable). The default\ + \ strategy forces the MEMs to belong to the alignment, but has the benefit that\ + \ it can discover indels prior to the first hit shared between the read and\ + \ reference. Except in very rare circumstances, the default mode should be more\ + \ accurate.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--hard_filter" + description: "[selective-alignment mode only] \nInstead of weighting mappings\ + \ by their alignment score, this flag will discard any mappings with sub-optimal\ + \ alignment score. The default option of soft-filtering (i.e. weighting mappings\ + \ by their alignment score) usually yields slightly more accurate abundance\ + \ estimates but this flag may be desirable if you want more accurate 'naive'\ + \ equivalence classes, rather than range factorized equivalence classes.\n" + info: null + direction: "input" + - type: "double" + name: "--min_aln_prob" + description: "The minimum number of fragments that must be assigned to the transcriptome\ + \ for quantification to proceed.\n" + info: null + example: + - 1.0E-5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--write_mappings" + alternatives: + - "-z" + description: "If this option is provided, then the selective-alignment results\ + \ will be written out in SAM-compatible format. By default, output will be directed\ + \ to stdout, but an alternative file name can be provided instead.\n" + info: null + direction: "input" + - type: "file" + name: "--mapping_sam" + description: "Path to file that should output the selective-alignment results\ + \ in SAM-compatible format. THis option must be provided while using --write_mappings" + info: null + example: + - "mappings.sam" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--write_qualities" + description: "This flag only has meaning if mappings are being written (with --write_mappings/-z).\ + \ If this flag is provided, then the output SAM file will contain quality strings\ + \ as well as read sequences. Note that this can greatly increase the size of\ + \ the output file.\n" + info: null + direction: "input" + - type: "string" + name: "--hit_filter_policy" + description: "[selective-alignment mode only]\nDetermines the policy by which\ + \ hits are filtered in selective alignment. Filtering hits after chaining (the\ + \ default) is more sensitive, but more computationally intensive, because it\ + \ performs the chaining dynamic program for all hits. Filtering before chaining\ + \ is faster, but some true hits may be missed. The options are BEFORE, AFTER,\ + \ BOTH and NONE.\n" + info: null + example: + - "AFTER" + required: false + choices: + - "BEFORE" + - "AFTER" + - "BOTH" + - "NONE" + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Advance options" + arguments: + - type: "boolean_true" + name: "--alternative_init_mode" + description: "Use an alternative strategy (rather than simple interpolation between)\ + \ the online and uniform abundance estimates to initialize the EM / VBEM algorithm.\n" + info: null + direction: "input" + - type: "file" + name: "--aux_dir" + description: "The sub-directory of the quantification directory where auxiliary\ + \ information e.g. bootstraps, bias parameters, etc. will be written.\n" + info: null + example: + - "aux_info" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--skip_quant" + description: "Skip performing the actual transcript quantification (including\ + \ any Gibbs sampling or bootstrapping).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--dump_eq" + description: "Dump the simple equivalence class counts that were computed during\ + \ mapping or alignment.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--dump_eq_weights" + alternatives: + - "-d" + description: "Dump conditional probabilities associated with transcripts when\ + \ equivalence class information is being dumped to file. Note, this will dump\ + \ the factorization that is actually used by salmon's offline phase for inference.\ + \ If you are using range-factorized equivalence classes (the default) then the\ + \ same transcript set may appear multiple times with different associated conditional\ + \ probabilities.\n" + info: null + direction: "input" + - type: "integer" + name: "--min_assigned_frags" + description: "The minimum number of fragments that must be assigned to the transcriptome\ + \ for quantification to proceed.\n" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--reduce_GC_memory" + description: "If this option is selected, a more memory efficient (but slightly\ + \ slower) representation is used to compute fragment GC content. Enabling this\ + \ will reduce memory usage, but can also reduce speed. However, the results\ + \ themselves will remain the same.\n" + info: null + direction: "input" + - type: "integer" + name: "--bias_speed_samp" + description: "The value at which the fragment length PMF is down-sampled when\ + \ evaluating sequence-specific & GC fragment bias. Larger values speed up effective\ + \ length correction, but may decrease the fidelity of bias modeling results.\n" + info: null + example: + - 5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--fld_max" + description: "The maximum fragment length to consider when building the empirical\ + \ distribution\n" + info: null + example: + - 1000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--fld_mean" + description: "The mean used in the fragment length distribution prior\n" + info: null + example: + - 250 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--fld_SD" + description: "The standard deviation used in the fragment length distribution\ + \ prior\n" + info: null + example: + - 25 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--forgetting_factor" + alternatives: + - "-f" + description: "The forgetting factor used in the online learning schedule. A smallervalue\ + \ results in quicker learning, but higher variance and may be unstable. A larger\ + \ value results in slower learning but may be more stable. Value should be\ + \ in the interval (0.5, 1.0].\n" + info: null + example: + - 0.65 + required: false + min: 0.500000001 + max: 1.0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--init_uniform" + description: "Initialize the offline inference with uniform parameters, rather\ + \ than seeding with online parameters.\n" + info: null + direction: "input" + - type: "integer" + name: "--max_occs_per_hit" + description: "When collecting \"hits\" (MEMs), hits having more than max_occs_per_hit\ + \ occurrences won't be considered.\n" + info: null + example: + - 1000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--max_read_occ" + description: "Reads \"mapping\" to more than this many places won't be considered.\n" + info: null + example: + - 200 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_length_correction" + description: "Entirely disables length correction when estimating the abundance\ + \ of transcripts. This option can be used with protocols where one expects that\ + \ fragments derive from their underlying targets without regard to that target's\ + \ length (e.g. QuantSeq)\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_effective_length_correction" + description: "Disables effective length correction when computing the probability\ + \ that a fragment was generated from a transcript. If this flag is passed in,the\ + \ fragment length distribution is not taken into account when computing this\ + \ probability.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_single_frag_prob" + description: "Disables the estimation of an associated fragment length probability\ + \ for single-end reads or for orphaned mappings in paired-end libraries. The\ + \ default behavior is to consider the probability of all possible fragment\ + \ lengths associated with the retained mapping. Enabling this flag (i.e. turning\ + \ this default behavior off) will simply not attempt to estimate a fragment\ + \ length probability in such cases.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_frag_length_dist" + description: "Don't consider concordance with the learned fragment length distribution\ + \ when trying to determine the probability that a fragment has originated from\ + \ a specified location. Normally, Fragments with unlikely lengths will be assigned\ + \ a smaller relative probability than those with more likely lengths. When this\ + \ flag is passed in, the observed fragment length has no effect on that fragment's\ + \ a priori probability.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_bias_length_threshold" + description: "If this option is enabled, then no (lower) threshold will be set\ + \ on how short bias correction can make effective lengths. This can increase\ + \ the precision of bias correction, but harm robustness. The default correction\ + \ applies a threshold.\n" + info: null + direction: "input" + - type: "integer" + name: "--num_bias_samples" + description: "Number of fragment mappings to use when learning the sequence-specific\ + \ bias model.\n" + info: null + example: + - 2000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--num_aux_model_samples" + description: "The first are used to train the auxiliary\ + \ model parameters (e.g. fragment length distribution, bias, etc.). After ther\ + \ first observations the auxiliary model parameters\ + \ will be assumed to have converged and will be fixed.\n" + info: null + example: + - 5000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--num_pre_aux_model_samples" + description: "The first will have their assignment likelihoods\ + \ and contributions to the transcript abundances computed without applying any\ + \ auxiliary models. The purpose of ignoring the auxiliary models for the first\ + \ observations is to avoid applying these models\ + \ before their parameters have been learned sufficiently well.\n" + info: null + example: + - 5000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--useEM" + description: "Use the traditional EM algorithm for optimization in the batch passes.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--useVBOpt" + description: "Use the Variational Bayesian EM [default]\n" + info: null + direction: "input" + - type: "integer" + name: "--range_factorization_bins" + description: "Factorizes the likelihood used in quantification by adopting a new\ + \ notion of equivalence classes based on the conditional probabilities with\ + \ which fragments are generated from different transcripts. This is a more fine-grained\ + \ factorization than the normal rich equivalence classes. The default value\ + \ (4) corresponds to the default used in Zakeri et al. 2017 (doi: 10.1093/bioinformatics/btx262),\ + \ and larger values imply a more fine-grained factorization. If range factorization\ + \ is enabled, a common value to select for this parameter is 4. A value of 0\ + \ signifies the use of basic rich equivalence classes.\n" + info: null + example: + - 4 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--num_Gibbs_samples" + description: "Number of Gibbs sampling rounds to perform.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_Gamma_draw" + description: "This switch will disable drawing transcript fractions from a Gamma\ + \ distribution during Gibbs sampling. In this case the sampler does not account\ + \ for shot-noise, but only assignment ambiguity\n" + info: null + direction: "input" + - type: "integer" + name: "--num_bootstraps" + description: "Number of bootstrap samples to generate. Note: This is mutually\ + \ exclusive with Gibbs sampling.\n" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--bootstrap_reproject" + description: "This switch will learn the parameter distribution from the bootstrapped\ + \ counts for each sample, but will reproject those parameters onto the original\ + \ equivalence class counts.\n" + info: null + direction: "input" + - type: "integer" + name: "--thinning_factor" + description: "Number of steps to discard for every sample kept from the Gibbs\ + \ chain. The larger this number, the less chance that subsequent samples are\ + \ auto-correlated, but the slower sampling becomes.\n" + info: null + example: + - 16 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--quiet" + alternatives: + - "-q" + description: "Be quiet while doing quantification (don't write informative output\ + \ to the console unless something goes wrong).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--per_transcript_prior" + description: "The prior (either the default or the argument provided via --vb_prior)\ + \ will be interpreted as a transcript-level prior (i.e. each transcript will\ + \ be given a prior read count of this value)\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--per_nucleotide_prior" + description: "The prior (either the default or the argument provided via --vb_prior)\ + \ will be interpreted as a nucleotide-level prior (i.e. each nucleotide will\ + \ be given a prior read count of this value)\n" + info: null + direction: "input" + - type: "integer" + name: "--sig_digits" + description: "The number of significant digits to write when outputting the EffectiveLength\ + \ and NumReads columns\n" + info: null + example: + - 3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--vb_prior" + description: "The prior that will be used in the VBEM algorithm. This is interpreted\ + \ as a per-transcript prior, unless the --per_nucleotide_prior flag is also\ + \ given. If the --per_nucleotide_prior flag is given, this is used as a nucleotide-level\ + \ prior. If the default is used, it will be divided by 1000 before being used\ + \ as a nucleotide-level prior, i.e. the default per-nucleotide prior will be\ + \ 1e-5.\n" + info: null + example: + - 0.01 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--write_orphan_links" + description: "Write the transcripts that are linked by orphaned reads.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--write_unmapped_names" + description: "Write the names of un-mapped reads to the file unmapped_names.txt\ + \ in the auxiliary directory.\n" + info: null + direction: "input" +- name: "Alignment-specific options" + arguments: + - type: "boolean_true" + name: "--no_error_model" + description: "Turn off the alignment error model, which takes into account the\ + \ the observed frequency of different types of mismatches / indels when computing\ + \ the likelihood of a given alignment. Turning this off can speed up alignment-based\ + \ salmon, but can harm quantification accuracy.\n" + info: null + direction: "input" + - type: "integer" + name: "--num_error_bins" + description: "The number of bins into which to divide each read when learning\ + \ and applying the error model. For example, a value of 10 would mean that\ + \ effectively, a separate error model is leared and applied to each 10th of\ + \ the read, while a value of 3 would mean that a separate error model is applied\ + \ to the read beginning (first third), middle (second third) and end (final\ + \ third).\n" + info: null + example: + - 6 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--sample_out" + alternatives: + - "-s" + description: "Write a \"postSample.bam\" file in the output directory that will\ + \ sample the input alignments according to the estimated transcript abundances.\ + \ If you're going to perform downstream analysis of the alignments with tools\ + \ which don't, themselves, take fragment assignment ambiguity into account,\ + \ you should use this output.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--sample_unaligned" + alternatives: + - "-u" + description: "In addition to sampling the aligned reads, also write the un-aligned\ + \ reads to \"postSample.bam\".\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--gencode" + description: "This flag will expect the input transcript fasta to be in GENCODE\ + \ format, and will split the transcript name at the first '|' character. These\ + \ reduced names will be used in the output and when looking for these transcripts\ + \ in a gene to transcript GTF.\n" + info: null + direction: "input" + - type: "integer" + name: "--mapping_cache_memory_limit" + description: "If the file contained fewer than this many mapped reads, then just\ + \ keep the data in memory for subsequent rounds of inference. Obviously, this\ + \ value should not be too large if you wish to keep a low memory usage, but\ + \ setting it large enough to accommodate all of the mapped read can substantially\ + \ speed up inference on \"small\" files that contain only a few million reads.\n" + info: null + example: + - 2000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Salmon is a tool for wicked-fast transcript quantification from RNA-seq\ + \ data. It can either make use of pre-computed alignments (in the form of a SAM/BAM\ + \ file) to the transcripts rather than the raw reads, or can be run in the mapping-based\ + \ mode. \n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "Transcriptome" +- "Quantification" +license: "GPL-3.0" +references: + doi: + - "10.1038/nmeth.4197" +links: + repository: "https://github.com/COMBINE-lab/salmon" + homepage: "https://salmon.readthedocs.io/en/latest/salmon.html" + documentation: "https://salmon.readthedocs.io/en/latest/salmon.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/salmon:1.10.2--hecfa306_0" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "salmon index -v 2>&1 | sed 's/salmon \\([0-9.]*\\)/salmon: \\1/' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/salmon/salmon_quant/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/salmon/salmon_quant" + executable: "target/nextflow/salmon/salmon_quant/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/salmon/salmon_quant/main.nf b/target/nextflow/salmon/salmon_quant/main.nf new file mode 100644 index 00000000..e678eae2 --- /dev/null +++ b/target/nextflow/salmon/salmon_quant/main.nf @@ -0,0 +1,4693 @@ +// salmon_quant main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "salmon_quant", + "namespace" : "salmon", + "version" : "main", + "argument_groups" : [ + { + "name" : "Common input options", + "arguments" : [ + { + "type" : "string", + "name" : "--lib_type", + "alternatives" : [ + "-l" + ], + "description" : "Format string describing the library.\nThe library type string consists of three parts: \n1. Relative orientation of the reads: This part is only provided if the library is paired-end, THe possible options are\n I = inward\n O = outward\n M = matching\n2. Strandedness of the library: This part specifies whether the protocol is stranded or unstranded. The options are:\n S = stranded\n U = unstranded\n3. Directionality of the reads: If the library is stranded, the final part of the library string is used to specify the strand from which the read originates. The possible values are\n F = read 1 (or single-end read) comes from the forward strand\n R = read 1 (or single-end read) comes from the reverse strand\n", + "default" : [ + "A" + ], + "required" : false, + "choices" : [ + "A", + "U", + "SF", + "SR", + "IU", + "IS", + "ISF", + "ISR", + "OU", + "OS", + "OSF", + "OSR", + "MU", + "MS", + "MSF", + "MSR" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Mapping input options", + "arguments" : [ + { + "type" : "file", + "name" : "--index", + "alternatives" : [ + "-i" + ], + "description" : "Salmon index.\n", + "example" : [ + "transcriptome_index" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--unmated_reads", + "alternatives" : [ + "-r" + ], + "description" : "List of files containing unmated reads of (e.g. single-end reads).\n", + "example" : [ + "sample.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--mates1", + "alternatives" : [ + "-m1" + ], + "description" : "File containing the #1 mates.\n", + "example" : [ + "sample_1.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--mates2", + "alternatives" : [ + "-m2" + ], + "description" : "File containing the #2 mates.\n", + "example" : [ + "sample_2.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Alignment input options", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--discard_orphans", + "description" : "Discard orphan alignments in the input [for alignment-based mode only]. If this flag is passed, then only paired alignments will be considered toward quantification estimates. The default behavior is to consider orphan alignments if no valid paired mappings exist.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--alignments", + "alternatives" : [ + "-a" + ], + "description" : "Input alignment (BAM) file(s).\n", + "example" : [ + "sample.fq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--eqclasses", + "alternatives" : [ + "-e" + ], + "description" : "input salmon weighted equivalence class file.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--targets", + "alternatives" : [ + "-t" + ], + "description" : "FASTA format file containing target transcripts.\n", + "example" : [ + "transcripts.fasta" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--ont", + "description" : "Use alignment model for Oxford Nanopore long reads\n", + "direction" : "input" + } + ] + }, + { + "name" : "Output", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "alternatives" : [ + "-o" + ], + "description" : "Output quantification directory.\n", + "example" : [ + "quant_output" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--quant_results", + "description" : "Salmon quantification file.\n", + "example" : [ + "quant.sf" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Basic options", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--seq_bias", + "description" : "Perform sequence-specific bias correction.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--gc_bias", + "description" : "Perform fragment GC bias correction [beta for single-end reads].\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--pos_bias", + "description" : "Perform positional bias correction.\n", + "direction" : "input" + }, + { + "type" : "double", + "name" : "--incompat_prior", + "description" : "Set the prior probability that an alignment that disagrees with the specified library type (--lib_type) results from the true fragment origin. Setting this to 0 specifies that alignments that disagree with the library type should be \\"impossible\\", while setting it to 1 says that alignments that disagree with the library type are no less likely than those that do.\n", + "example" : [ + 0.0 + ], + "required" : false, + "min" : 0.0, + "max" : 1.0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--gene_map", + "alternatives" : [ + "-g" + ], + "description" : "File containing a mapping of transcripts to genes. If this file is provided salmon will output both quant.sf and quant.genes.sf files, where the latter contains aggregated gene-level abundance estimates. The transcript to gene mapping should be provided as either a GTF file, or a in a simple tab-delimited format where each line contains the name of a transcript and the gene to which it belongs separated by a tab. The extension of the file is used to determine how the file should be parsed. Files ending in '.gtf', '.gff' or '.gff3' are assumed to be in GTF format; files with any other extension are assumed to be in the simple format. In GTF / GFF format, the \\"transcript_id\\" is assumed to contain the transcript identifier and the \\"gene_id\\" is assumed to contain the corresponding gene identifier.\n", + "example" : [ + "gene_map.gtf" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--aux_target_file", + "description" : "A file containing a list of \\"auxiliary\\" targets. These are valid targets (i.e., not decoys) to which fragments are allowed to map and be assigned, and which will be quantified, but for which auxiliary models like sequence-specific and fragment-GC bias correction should not be applied.\n", + "example" : [ + "auxilary_targets.txt" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--meta", + "description" : "If you're using Salmon on a metagenomic dataset, consider setting this flag to disable parts of the abundance estimation model that make less sense for metagenomic data.\n", + "direction" : "input" + }, + { + "type" : "double", + "name" : "--score_exp", + "description" : "The factor by which sub-optimal alignment scores are downweighted to produce a probability. If the best alignment score for the current read is S, and the score for a particular alignment is w, then the probability will be computed porportional to exp( - scoreExp * (S-w) ).\n", + "example" : [ + 1.0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Options specific to mapping mode", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--discard_orphans_quasi", + "description" : "[selective-alignment mode only] \nDiscard orphan mappings in selective-alignment mode. If this flag is passed then only paired mappings will be considered toward quantification estimates. The default behavior is to consider orphan mappings if no valid paired mappings exist. This flag is independent of the option to write the orphaned mappings to file (--writeOrphanLinks).\n", + "direction" : "input" + }, + { + "type" : "double", + "name" : "--consensus_slack", + "description" : "[selective-alignment mode only] \nThe amount of slack allowed in the selective-alignment filtering mechanism. If this is set to a fraction, X, greater than 0 (and in [0,1)), then uniMEM chains with scores below (100 * X)% of the best chain score for a read, and read pairs with a sum of chain scores below (100 * X)% of the best chain score for a read pair will be discounted as a mapping candidates. The default value of this option is 0.35.\n", + "example" : [ + 0.35 + ], + "required" : false, + "min" : 0.0, + "max" : 0.999999999, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--pre_merge_chain_sub_thresh", + "description" : "[selective-alignment mode only] \nThe threshold of sub-optimal chains, compared to the best chain on a given target, that will be retained and passed to the next phase of mapping. Specifically, if the best chain for a read (or read-end in paired-end mode) to target t has score X_t, then all chains for this read with score >= X_t * preMergeChainSubThresh will be retained and passed to subsequent mapping phases. This value must be in the range [0, 1].\n", + "example" : [ + 0.75 + ], + "required" : false, + "min" : 0.0, + "max" : 1.0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--post_merge_chain_sub_thresh", + "description" : "[selective-alignment mode only] \nThe threshold of sub-optimal chains, compared to the best chain on a given target, that will be retained and passed to the next phase of mapping. This is different than post_merge_chain_sub_thresh, because this is applied to pairs of chains (from the ends of paired-end reads) after merging (i.e. after checking concordancy constraints etc.). Specifically, if the best chain pair to target t has score X_t, then all chain pairs for this read pair with score >= X_t * post_merge_chain_sub_thresh will be retained and passed to subsequent mapping phases. This value must be in the range [0, 1]. Note: This option is only meaningful for paired-end libraries, and is ignored for single-end libraries.\n", + "example" : [ + 0.9 + ], + "required" : false, + "min" : 0.0, + "max" : 1.0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--orphan_chain_sub_thresh", + "description" : "[selective-alignment mode only]\nThis threshold sets a global sub-optimality threshold for chains corresponding to orphan mappings. That is, if the merging procedure results in no concordant mappings then only orphan mappings with a chain score >= orphan_chain_sub_thresh * bestChainScore will be retained and passed to subsequent mapping phases. This value must be in the range [0, 1]. Note: This option is only meaningful for paired-end libraries, and is ignored for single-end libraries.\n", + "example" : [ + 0.95 + ], + "required" : false, + "min" : 0.0, + "max" : 1.0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--min_score_fraction", + "description" : "[selective-alignment mode only]\nThe fraction of the optimal possible alignment score that a mapping must achieve in order to be considered \\"valid\\" --- should be in (0,1]. Default 0.65\n", + "example" : [ + 0.65 + ], + "required" : false, + "min" : 1.0E-9, + "max" : 1.0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--mismatch_seed_skip", + "description" : "[selective-alignment mode only]\nAfter a k-mer hit is extended to a uni-MEM, the uni-MEM extension can terminate for one of 3 reasons; the end of the read, the end of the unitig, or a mismatch. If the extension ends because of a mismatch, this is likely the result of a sequencing error. To avoid looking up many k-mers that will likely fail to be located in the index, the search procedure skips by a factor of mismatch_seed_skip until it either (1) finds another match or (2) is k-bases past the mismatch position. This value controls that skip length. A smaller value can increase sensitivity, while a larger value can speed up seeding.\n", + "example" : [ + 3 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--disable_chaining_heuristic", + "description" : "[selective-alignment mode only] \nBy default, the heuristic of (Li 2018) is implemented, which terminates the chaining DP once a given number of valid backpointers are found. This speeds up the seed (MEM) chaining step, but may result in sub-optimal chains in complex situations (e.g. sequences with many repeats and overlapping repeats). Passing this flag will disable the chaining heuristic, and perform the full chaining dynamic program, guaranteeing the optimal chain is found in this step.\n", + "direction" : "input" + }, + { + "type" : "double", + "name" : "--decoy_threshold", + "description" : "[selective-alignment mode only]\nFor an alignemnt to an annotated transcript to be considered invalid, it must have an alignment score < (decoy_threshold * bestDecoyScore). A value of 1.0 means that any alignment strictly worse than the best decoy alignment will be discarded. A smaller value will allow reads to be allocated to transcripts even if they strictly align better to the decoy sequence.\n", + "example" : [ + 1.0 + ], + "required" : false, + "min" : 0.0, + "max" : 1.0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--ma", + "description" : "[selective-alignment mode only]\nThe value given to a match between read and reference nucleotides in an alignment.\n", + "example" : [ + 2 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--mp", + "description" : "[selective-alignment mode only]\nThe value given to a mis-match between read and reference nucleotides in an alignment.\n", + "example" : [ + -4 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--go", + "description" : "[selective-alignment mode only]\nThe value given to a gap opening in an alignment.\n", + "example" : [ + 6 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--ge", + "description" : "[selective-alignment mode only]\nThe value given to a gap extension in an alignment.\n", + "example" : [ + 2 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--bandwidth", + "description" : "[selective-alignment mode only]\nThe value used for the bandwidth passed to ksw2. A smaller bandwidth can make the alignment verification run more quickly, but could possibly miss valid alignments.\n", + "example" : [ + 15 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--allow_dovetail", + "description" : "[selective-alignment mode only] \nAllow dovetailing mappings.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--recover_orphans", + "description" : "[selective-alignment mode only] \nAttempt to recover the mates of orphaned reads. This uses edlib for orphan recovery, and so introduces some computational overhead, but it can improve sensitivity.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--mimicBT2", + "description" : "[selective-alignment mode only] \nSet flags to mimic parameters similar to Bowtie2 with --no-discordant and --no-mixed flags. This increases disallows dovetailing reads, and discards orphans. Note, this does not impose the very strict parameters assumed by RSEM+Bowtie2, like gapless alignments. For that behavior, use the --mimic_strictBT2 flag below.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--mimic_strictBT2", + "description" : "[selective-alignment mode only] \nSet flags to mimic the very strict parameters used by RSEM+Bowtie2. This increases --min_score_fraction to 0.8, disallows dovetailing reads, discards orphans, and disallows gaps in alignments.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--softclip", + "description" : "[selective-alignment mode only] \nAllos soft-clipping of reads during selective-alignment. If this option is provided, then regions at the beginning or end of the read can be withheld from alignment without any effect on the resulting score (i.e. neither adding nor removing from the score). This will drastically reduce the penalty if there are mismatches at the beginning or end of the read due to e.g. low-quality bases or adapters. NOTE: Even with soft-clipping enabled, the read must still achieve a score of at least min_score_fraction * maximum achievable score, where the maximum achievable score is computed based on the full (un-clipped) read length.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--softclip_overhangs", + "description" : "[selective-alignment mode only] \nAllow soft-clipping of reads that overhang the beginning or ends of the transcript. In this case, the overhaning section of the read will simply be unaligned, and will not contribute or detract from the alignment score. The default policy is to force an end-to-end alignment of the entire read, so that overhanings will result in some deletion of nucleotides from the read.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--full_length_alignment", + "description" : "[selective-alignment mode only] \nPerform selective alignment over the full length of the read, beginning from the (approximate) initial mapping location and using extension alignment. This is in contrast with the default behavior which is to only perform alignment between the MEMs in the optimal chain (and before the first and after the last MEM if applicable). The default strategy forces the MEMs to belong to the alignment, but has the benefit that it can discover indels prior to the first hit shared between the read and reference. Except in very rare circumstances, the default mode should be more accurate.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--hard_filter", + "description" : "[selective-alignment mode only] \nInstead of weighting mappings by their alignment score, this flag will discard any mappings with sub-optimal alignment score. The default option of soft-filtering (i.e. weighting mappings by their alignment score) usually yields slightly more accurate abundance estimates but this flag may be desirable if you want more accurate 'naive' equivalence classes, rather than range factorized equivalence classes.\n", + "direction" : "input" + }, + { + "type" : "double", + "name" : "--min_aln_prob", + "description" : "The minimum number of fragments that must be assigned to the transcriptome for quantification to proceed.\n", + "example" : [ + 1.0E-5 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--write_mappings", + "alternatives" : [ + "-z" + ], + "description" : "If this option is provided, then the selective-alignment results will be written out in SAM-compatible format. By default, output will be directed to stdout, but an alternative file name can be provided instead.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--mapping_sam", + "description" : "Path to file that should output the selective-alignment results in SAM-compatible format. THis option must be provided while using --write_mappings", + "example" : [ + "mappings.sam" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--write_qualities", + "description" : "This flag only has meaning if mappings are being written (with --write_mappings/-z). If this flag is provided, then the output SAM file will contain quality strings as well as read sequences. Note that this can greatly increase the size of the output file.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--hit_filter_policy", + "description" : "[selective-alignment mode only]\nDetermines the policy by which hits are filtered in selective alignment. Filtering hits after chaining (the default) is more sensitive, but more computationally intensive, because it performs the chaining dynamic program for all hits. Filtering before chaining is faster, but some true hits may be missed. The options are BEFORE, AFTER, BOTH and NONE.\n", + "example" : [ + "AFTER" + ], + "required" : false, + "choices" : [ + "BEFORE", + "AFTER", + "BOTH", + "NONE" + ], + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Advance options", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--alternative_init_mode", + "description" : "Use an alternative strategy (rather than simple interpolation between) the online and uniform abundance estimates to initialize the EM / VBEM algorithm.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--aux_dir", + "description" : "The sub-directory of the quantification directory where auxiliary information e.g. bootstraps, bias parameters, etc. will be written.\n", + "example" : [ + "aux_info" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--skip_quant", + "description" : "Skip performing the actual transcript quantification (including any Gibbs sampling or bootstrapping).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--dump_eq", + "description" : "Dump the simple equivalence class counts that were computed during mapping or alignment.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--dump_eq_weights", + "alternatives" : [ + "-d" + ], + "description" : "Dump conditional probabilities associated with transcripts when equivalence class information is being dumped to file. Note, this will dump the factorization that is actually used by salmon's offline phase for inference. If you are using range-factorized equivalence classes (the default) then the same transcript set may appear multiple times with different associated conditional probabilities.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--min_assigned_frags", + "description" : "The minimum number of fragments that must be assigned to the transcriptome for quantification to proceed.\n", + "example" : [ + 10 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--reduce_GC_memory", + "description" : "If this option is selected, a more memory efficient (but slightly slower) representation is used to compute fragment GC content. Enabling this will reduce memory usage, but can also reduce speed. However, the results themselves will remain the same.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--bias_speed_samp", + "description" : "The value at which the fragment length PMF is down-sampled when evaluating sequence-specific & GC fragment bias. Larger values speed up effective length correction, but may decrease the fidelity of bias modeling results.\n", + "example" : [ + 5 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--fld_max", + "description" : "The maximum fragment length to consider when building the empirical distribution\n", + "example" : [ + 1000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--fld_mean", + "description" : "The mean used in the fragment length distribution prior\n", + "example" : [ + 250 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--fld_SD", + "description" : "The standard deviation used in the fragment length distribution prior\n", + "example" : [ + 25 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--forgetting_factor", + "alternatives" : [ + "-f" + ], + "description" : "The forgetting factor used in the online learning schedule. A smallervalue results in quicker learning, but higher variance and may be unstable. A larger value results in slower learning but may be more stable. Value should be in the interval (0.5, 1.0].\n", + "example" : [ + 0.65 + ], + "required" : false, + "min" : 0.500000001, + "max" : 1.0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--init_uniform", + "description" : "Initialize the offline inference with uniform parameters, rather than seeding with online parameters.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--max_occs_per_hit", + "description" : "When collecting \\"hits\\" (MEMs), hits having more than max_occs_per_hit occurrences won't be considered.\n", + "example" : [ + 1000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--max_read_occ", + "description" : "Reads \\"mapping\\" to more than this many places won't be considered.\n", + "example" : [ + 200 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--no_length_correction", + "description" : "Entirely disables length correction when estimating the abundance of transcripts. This option can be used with protocols where one expects that fragments derive from their underlying targets without regard to that target's length (e.g. QuantSeq)\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_effective_length_correction", + "description" : "Disables effective length correction when computing the probability that a fragment was generated from a transcript. If this flag is passed in,the fragment length distribution is not taken into account when computing this probability.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_single_frag_prob", + "description" : "Disables the estimation of an associated fragment length probability for single-end reads or for orphaned mappings in paired-end libraries. The default behavior is to consider the probability of all possible fragment lengths associated with the retained mapping. Enabling this flag (i.e. turning this default behavior off) will simply not attempt to estimate a fragment length probability in such cases.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_frag_length_dist", + "description" : "Don't consider concordance with the learned fragment length distribution when trying to determine the probability that a fragment has originated from a specified location. Normally, Fragments with unlikely lengths will be assigned a smaller relative probability than those with more likely lengths. When this flag is passed in, the observed fragment length has no effect on that fragment's a priori probability.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_bias_length_threshold", + "description" : "If this option is enabled, then no (lower) threshold will be set on how short bias correction can make effective lengths. This can increase the precision of bias correction, but harm robustness. The default correction applies a threshold.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--num_bias_samples", + "description" : "Number of fragment mappings to use when learning the sequence-specific bias model.\n", + "example" : [ + 2000000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--num_aux_model_samples", + "description" : "The first are used to train the auxiliary model parameters (e.g. fragment length distribution, bias, etc.). After ther first observations the auxiliary model parameters will be assumed to have converged and will be fixed.\n", + "example" : [ + 5000000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--num_pre_aux_model_samples", + "description" : "The first will have their assignment likelihoods and contributions to the transcript abundances computed without applying any auxiliary models. The purpose of ignoring the auxiliary models for the first observations is to avoid applying these models before their parameters have been learned sufficiently well.\n", + "example" : [ + 5000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--useEM", + "description" : "Use the traditional EM algorithm for optimization in the batch passes.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--useVBOpt", + "description" : "Use the Variational Bayesian EM [default]\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--range_factorization_bins", + "description" : "Factorizes the likelihood used in quantification by adopting a new notion of equivalence classes based on the conditional probabilities with which fragments are generated from different transcripts. This is a more fine-grained factorization than the normal rich equivalence classes. The default value (4) corresponds to the default used in Zakeri et al. 2017 (doi: 10.1093/bioinformatics/btx262), and larger values imply a more fine-grained factorization. If range factorization is enabled, a common value to select for this parameter is 4. A value of 0 signifies the use of basic rich equivalence classes.\n", + "example" : [ + 4 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--num_Gibbs_samples", + "description" : "Number of Gibbs sampling rounds to perform.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--no_Gamma_draw", + "description" : "This switch will disable drawing transcript fractions from a Gamma distribution during Gibbs sampling. In this case the sampler does not account for shot-noise, but only assignment ambiguity\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--num_bootstraps", + "description" : "Number of bootstrap samples to generate. Note: This is mutually exclusive with Gibbs sampling.\n", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--bootstrap_reproject", + "description" : "This switch will learn the parameter distribution from the bootstrapped counts for each sample, but will reproject those parameters onto the original equivalence class counts.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--thinning_factor", + "description" : "Number of steps to discard for every sample kept from the Gibbs chain. The larger this number, the less chance that subsequent samples are auto-correlated, but the slower sampling becomes.\n", + "example" : [ + 16 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--quiet", + "alternatives" : [ + "-q" + ], + "description" : "Be quiet while doing quantification (don't write informative output to the console unless something goes wrong).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--per_transcript_prior", + "description" : "The prior (either the default or the argument provided via --vb_prior) will be interpreted as a transcript-level prior (i.e. each transcript will be given a prior read count of this value)\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--per_nucleotide_prior", + "description" : "The prior (either the default or the argument provided via --vb_prior) will be interpreted as a nucleotide-level prior (i.e. each nucleotide will be given a prior read count of this value)\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--sig_digits", + "description" : "The number of significant digits to write when outputting the EffectiveLength and NumReads columns\n", + "example" : [ + 3 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--vb_prior", + "description" : "The prior that will be used in the VBEM algorithm. This is interpreted as a per-transcript prior, unless the --per_nucleotide_prior flag is also given. If the --per_nucleotide_prior flag is given, this is used as a nucleotide-level prior. If the default is used, it will be divided by 1000 before being used as a nucleotide-level prior, i.e. the default per-nucleotide prior will be 1e-5.\n", + "example" : [ + 0.01 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--write_orphan_links", + "description" : "Write the transcripts that are linked by orphaned reads.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--write_unmapped_names", + "description" : "Write the names of un-mapped reads to the file unmapped_names.txt in the auxiliary directory.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Alignment-specific options", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--no_error_model", + "description" : "Turn off the alignment error model, which takes into account the the observed frequency of different types of mismatches / indels when computing the likelihood of a given alignment. Turning this off can speed up alignment-based salmon, but can harm quantification accuracy.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--num_error_bins", + "description" : "The number of bins into which to divide each read when learning and applying the error model. For example, a value of 10 would mean that effectively, a separate error model is leared and applied to each 10th of the read, while a value of 3 would mean that a separate error model is applied to the read beginning (first third), middle (second third) and end (final third).\n", + "example" : [ + 6 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--sample_out", + "alternatives" : [ + "-s" + ], + "description" : "Write a \\"postSample.bam\\" file in the output directory that will sample the input alignments according to the estimated transcript abundances. If you're going to perform downstream analysis of the alignments with tools which don't, themselves, take fragment assignment ambiguity into account, you should use this output.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--sample_unaligned", + "alternatives" : [ + "-u" + ], + "description" : "In addition to sampling the aligned reads, also write the un-aligned reads to \\"postSample.bam\\".\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--gencode", + "description" : "This flag will expect the input transcript fasta to be in GENCODE format, and will split the transcript name at the first '|' character. These reduced names will be used in the output and when looking for these transcripts in a gene to transcript GTF.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--mapping_cache_memory_limit", + "description" : "If the file contained fewer than this many mapped reads, then just keep the data in memory for subsequent rounds of inference. Obviously, this value should not be too large if you wish to keep a low memory usage, but setting it large enough to accommodate all of the mapped read can substantially speed up inference on \\"small\\" files that contain only a few million reads.\n", + "example" : [ + 2000000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Salmon is a tool for wicked-fast transcript quantification from RNA-seq data. It can either make use of pre-computed alignments (in the form of a SAM/BAM file) to the transcripts rather than the raw reads, or can be run in the mapping-based mode. \n", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "Transcriptome", + "Quantification" + ], + "license" : "GPL-3.0", + "references" : { + "doi" : [ + "10.1038/nmeth.4197" + ] + }, + "links" : { + "repository" : "https://github.com/COMBINE-lab/salmon", + "homepage" : "https://salmon.readthedocs.io/en/latest/salmon.html", + "documentation" : "https://salmon.readthedocs.io/en/latest/salmon.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/salmon:1.10.2--hecfa306_0", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "salmon index -v 2>&1 | sed 's/salmon \\\\([0-9.]*\\\\)/salmon: \\\\1/' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/salmon/salmon_quant/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/salmon/salmon_quant", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +set -e + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_LIB_TYPE+x} ]; then echo "${VIASH_PAR_LIB_TYPE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_lib_type='&'#" ; else echo "# par_lib_type="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX+x} ]; then echo "${VIASH_PAR_INDEX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_index='&'#" ; else echo "# par_index="; fi ) +$( if [ ! -z ${VIASH_PAR_UNMATED_READS+x} ]; then echo "${VIASH_PAR_UNMATED_READS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_unmated_reads='&'#" ; else echo "# par_unmated_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_MATES1+x} ]; then echo "${VIASH_PAR_MATES1}" | sed "s#'#'\\"'\\"'#g;s#.*#par_mates1='&'#" ; else echo "# par_mates1="; fi ) +$( if [ ! -z ${VIASH_PAR_MATES2+x} ]; then echo "${VIASH_PAR_MATES2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_mates2='&'#" ; else echo "# par_mates2="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARD_ORPHANS+x} ]; then echo "${VIASH_PAR_DISCARD_ORPHANS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_discard_orphans='&'#" ; else echo "# par_discard_orphans="; fi ) +$( if [ ! -z ${VIASH_PAR_ALIGNMENTS+x} ]; then echo "${VIASH_PAR_ALIGNMENTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_alignments='&'#" ; else echo "# par_alignments="; fi ) +$( if [ ! -z ${VIASH_PAR_EQCLASSES+x} ]; then echo "${VIASH_PAR_EQCLASSES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_eqclasses='&'#" ; else echo "# par_eqclasses="; fi ) +$( if [ ! -z ${VIASH_PAR_TARGETS+x} ]; then echo "${VIASH_PAR_TARGETS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_targets='&'#" ; else echo "# par_targets="; fi ) +$( if [ ! -z ${VIASH_PAR_ONT+x} ]; then echo "${VIASH_PAR_ONT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ont='&'#" ; else echo "# par_ont="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_QUANT_RESULTS+x} ]; then echo "${VIASH_PAR_QUANT_RESULTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_quant_results='&'#" ; else echo "# par_quant_results="; fi ) +$( if [ ! -z ${VIASH_PAR_SEQ_BIAS+x} ]; then echo "${VIASH_PAR_SEQ_BIAS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_seq_bias='&'#" ; else echo "# par_seq_bias="; fi ) +$( if [ ! -z ${VIASH_PAR_GC_BIAS+x} ]; then echo "${VIASH_PAR_GC_BIAS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_gc_bias='&'#" ; else echo "# par_gc_bias="; fi ) +$( if [ ! -z ${VIASH_PAR_POS_BIAS+x} ]; then echo "${VIASH_PAR_POS_BIAS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_pos_bias='&'#" ; else echo "# par_pos_bias="; fi ) +$( if [ ! -z ${VIASH_PAR_INCOMPAT_PRIOR+x} ]; then echo "${VIASH_PAR_INCOMPAT_PRIOR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_incompat_prior='&'#" ; else echo "# par_incompat_prior="; fi ) +$( if [ ! -z ${VIASH_PAR_GENE_MAP+x} ]; then echo "${VIASH_PAR_GENE_MAP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_gene_map='&'#" ; else echo "# par_gene_map="; fi ) +$( if [ ! -z ${VIASH_PAR_AUX_TARGET_FILE+x} ]; then echo "${VIASH_PAR_AUX_TARGET_FILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_aux_target_file='&'#" ; else echo "# par_aux_target_file="; fi ) +$( if [ ! -z ${VIASH_PAR_META+x} ]; then echo "${VIASH_PAR_META}" | sed "s#'#'\\"'\\"'#g;s#.*#par_meta='&'#" ; else echo "# par_meta="; fi ) +$( if [ ! -z ${VIASH_PAR_SCORE_EXP+x} ]; then echo "${VIASH_PAR_SCORE_EXP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_score_exp='&'#" ; else echo "# par_score_exp="; fi ) +$( if [ ! -z ${VIASH_PAR_DISCARD_ORPHANS_QUASI+x} ]; then echo "${VIASH_PAR_DISCARD_ORPHANS_QUASI}" | sed "s#'#'\\"'\\"'#g;s#.*#par_discard_orphans_quasi='&'#" ; else echo "# par_discard_orphans_quasi="; fi ) +$( if [ ! -z ${VIASH_PAR_CONSENSUS_SLACK+x} ]; then echo "${VIASH_PAR_CONSENSUS_SLACK}" | sed "s#'#'\\"'\\"'#g;s#.*#par_consensus_slack='&'#" ; else echo "# par_consensus_slack="; fi ) +$( if [ ! -z ${VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH+x} ]; then echo "${VIASH_PAR_PRE_MERGE_CHAIN_SUB_THRESH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_pre_merge_chain_sub_thresh='&'#" ; else echo "# par_pre_merge_chain_sub_thresh="; fi ) +$( if [ ! -z ${VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH+x} ]; then echo "${VIASH_PAR_POST_MERGE_CHAIN_SUB_THRESH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_post_merge_chain_sub_thresh='&'#" ; else echo "# par_post_merge_chain_sub_thresh="; fi ) +$( if [ ! -z ${VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH+x} ]; then echo "${VIASH_PAR_ORPHAN_CHAIN_SUB_THRESH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_orphan_chain_sub_thresh='&'#" ; else echo "# par_orphan_chain_sub_thresh="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_SCORE_FRACTION+x} ]; then echo "${VIASH_PAR_MIN_SCORE_FRACTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_score_fraction='&'#" ; else echo "# par_min_score_fraction="; fi ) +$( if [ ! -z ${VIASH_PAR_MISMATCH_SEED_SKIP+x} ]; then echo "${VIASH_PAR_MISMATCH_SEED_SKIP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_mismatch_seed_skip='&'#" ; else echo "# par_mismatch_seed_skip="; fi ) +$( if [ ! -z ${VIASH_PAR_DISABLE_CHAINING_HEURISTIC+x} ]; then echo "${VIASH_PAR_DISABLE_CHAINING_HEURISTIC}" | sed "s#'#'\\"'\\"'#g;s#.*#par_disable_chaining_heuristic='&'#" ; else echo "# par_disable_chaining_heuristic="; fi ) +$( if [ ! -z ${VIASH_PAR_DECOY_THRESHOLD+x} ]; then echo "${VIASH_PAR_DECOY_THRESHOLD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_decoy_threshold='&'#" ; else echo "# par_decoy_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_MA+x} ]; then echo "${VIASH_PAR_MA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ma='&'#" ; else echo "# par_ma="; fi ) +$( if [ ! -z ${VIASH_PAR_MP+x} ]; then echo "${VIASH_PAR_MP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_mp='&'#" ; else echo "# par_mp="; fi ) +$( if [ ! -z ${VIASH_PAR_GO+x} ]; then echo "${VIASH_PAR_GO}" | sed "s#'#'\\"'\\"'#g;s#.*#par_go='&'#" ; else echo "# par_go="; fi ) +$( if [ ! -z ${VIASH_PAR_GE+x} ]; then echo "${VIASH_PAR_GE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ge='&'#" ; else echo "# par_ge="; fi ) +$( if [ ! -z ${VIASH_PAR_BANDWIDTH+x} ]; then echo "${VIASH_PAR_BANDWIDTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bandwidth='&'#" ; else echo "# par_bandwidth="; fi ) +$( if [ ! -z ${VIASH_PAR_ALLOW_DOVETAIL+x} ]; then echo "${VIASH_PAR_ALLOW_DOVETAIL}" | sed "s#'#'\\"'\\"'#g;s#.*#par_allow_dovetail='&'#" ; else echo "# par_allow_dovetail="; fi ) +$( if [ ! -z ${VIASH_PAR_RECOVER_ORPHANS+x} ]; then echo "${VIASH_PAR_RECOVER_ORPHANS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_recover_orphans='&'#" ; else echo "# par_recover_orphans="; fi ) +$( if [ ! -z ${VIASH_PAR_MIMICBT2+x} ]; then echo "${VIASH_PAR_MIMICBT2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_mimicBT2='&'#" ; else echo "# par_mimicBT2="; fi ) +$( if [ ! -z ${VIASH_PAR_MIMIC_STRICTBT2+x} ]; then echo "${VIASH_PAR_MIMIC_STRICTBT2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_mimic_strictBT2='&'#" ; else echo "# par_mimic_strictBT2="; fi ) +$( if [ ! -z ${VIASH_PAR_SOFTCLIP+x} ]; then echo "${VIASH_PAR_SOFTCLIP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_softclip='&'#" ; else echo "# par_softclip="; fi ) +$( if [ ! -z ${VIASH_PAR_SOFTCLIP_OVERHANGS+x} ]; then echo "${VIASH_PAR_SOFTCLIP_OVERHANGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_softclip_overhangs='&'#" ; else echo "# par_softclip_overhangs="; fi ) +$( if [ ! -z ${VIASH_PAR_FULL_LENGTH_ALIGNMENT+x} ]; then echo "${VIASH_PAR_FULL_LENGTH_ALIGNMENT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_full_length_alignment='&'#" ; else echo "# par_full_length_alignment="; fi ) +$( if [ ! -z ${VIASH_PAR_HARD_FILTER+x} ]; then echo "${VIASH_PAR_HARD_FILTER}" | sed "s#'#'\\"'\\"'#g;s#.*#par_hard_filter='&'#" ; else echo "# par_hard_filter="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ALN_PROB+x} ]; then echo "${VIASH_PAR_MIN_ALN_PROB}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_aln_prob='&'#" ; else echo "# par_min_aln_prob="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_MAPPINGS+x} ]; then echo "${VIASH_PAR_WRITE_MAPPINGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_write_mappings='&'#" ; else echo "# par_write_mappings="; fi ) +$( if [ ! -z ${VIASH_PAR_MAPPING_SAM+x} ]; then echo "${VIASH_PAR_MAPPING_SAM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_mapping_sam='&'#" ; else echo "# par_mapping_sam="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_QUALITIES+x} ]; then echo "${VIASH_PAR_WRITE_QUALITIES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_write_qualities='&'#" ; else echo "# par_write_qualities="; fi ) +$( if [ ! -z ${VIASH_PAR_HIT_FILTER_POLICY+x} ]; then echo "${VIASH_PAR_HIT_FILTER_POLICY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_hit_filter_policy='&'#" ; else echo "# par_hit_filter_policy="; fi ) +$( if [ ! -z ${VIASH_PAR_ALTERNATIVE_INIT_MODE+x} ]; then echo "${VIASH_PAR_ALTERNATIVE_INIT_MODE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_alternative_init_mode='&'#" ; else echo "# par_alternative_init_mode="; fi ) +$( if [ ! -z ${VIASH_PAR_AUX_DIR+x} ]; then echo "${VIASH_PAR_AUX_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_aux_dir='&'#" ; else echo "# par_aux_dir="; fi ) +$( if [ ! -z ${VIASH_PAR_SKIP_QUANT+x} ]; then echo "${VIASH_PAR_SKIP_QUANT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_skip_quant='&'#" ; else echo "# par_skip_quant="; fi ) +$( if [ ! -z ${VIASH_PAR_DUMP_EQ+x} ]; then echo "${VIASH_PAR_DUMP_EQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_dump_eq='&'#" ; else echo "# par_dump_eq="; fi ) +$( if [ ! -z ${VIASH_PAR_DUMP_EQ_WEIGHTS+x} ]; then echo "${VIASH_PAR_DUMP_EQ_WEIGHTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_dump_eq_weights='&'#" ; else echo "# par_dump_eq_weights="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_ASSIGNED_FRAGS+x} ]; then echo "${VIASH_PAR_MIN_ASSIGNED_FRAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_assigned_frags='&'#" ; else echo "# par_min_assigned_frags="; fi ) +$( if [ ! -z ${VIASH_PAR_REDUCE_GC_MEMORY+x} ]; then echo "${VIASH_PAR_REDUCE_GC_MEMORY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_reduce_GC_memory='&'#" ; else echo "# par_reduce_GC_memory="; fi ) +$( if [ ! -z ${VIASH_PAR_BIAS_SPEED_SAMP+x} ]; then echo "${VIASH_PAR_BIAS_SPEED_SAMP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bias_speed_samp='&'#" ; else echo "# par_bias_speed_samp="; fi ) +$( if [ ! -z ${VIASH_PAR_FLD_MAX+x} ]; then echo "${VIASH_PAR_FLD_MAX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fld_max='&'#" ; else echo "# par_fld_max="; fi ) +$( if [ ! -z ${VIASH_PAR_FLD_MEAN+x} ]; then echo "${VIASH_PAR_FLD_MEAN}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fld_mean='&'#" ; else echo "# par_fld_mean="; fi ) +$( if [ ! -z ${VIASH_PAR_FLD_SD+x} ]; then echo "${VIASH_PAR_FLD_SD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fld_SD='&'#" ; else echo "# par_fld_SD="; fi ) +$( if [ ! -z ${VIASH_PAR_FORGETTING_FACTOR+x} ]; then echo "${VIASH_PAR_FORGETTING_FACTOR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_forgetting_factor='&'#" ; else echo "# par_forgetting_factor="; fi ) +$( if [ ! -z ${VIASH_PAR_INIT_UNIFORM+x} ]; then echo "${VIASH_PAR_INIT_UNIFORM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_init_uniform='&'#" ; else echo "# par_init_uniform="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_OCCS_PER_HIT+x} ]; then echo "${VIASH_PAR_MAX_OCCS_PER_HIT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_occs_per_hit='&'#" ; else echo "# par_max_occs_per_hit="; fi ) +$( if [ ! -z ${VIASH_PAR_MAX_READ_OCC+x} ]; then echo "${VIASH_PAR_MAX_READ_OCC}" | sed "s#'#'\\"'\\"'#g;s#.*#par_max_read_occ='&'#" ; else echo "# par_max_read_occ="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_LENGTH_CORRECTION+x} ]; then echo "${VIASH_PAR_NO_LENGTH_CORRECTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_length_correction='&'#" ; else echo "# par_no_length_correction="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_EFFECTIVE_LENGTH_CORRECTION+x} ]; then echo "${VIASH_PAR_NO_EFFECTIVE_LENGTH_CORRECTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_effective_length_correction='&'#" ; else echo "# par_no_effective_length_correction="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_SINGLE_FRAG_PROB+x} ]; then echo "${VIASH_PAR_NO_SINGLE_FRAG_PROB}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_single_frag_prob='&'#" ; else echo "# par_no_single_frag_prob="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_FRAG_LENGTH_DIST+x} ]; then echo "${VIASH_PAR_NO_FRAG_LENGTH_DIST}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_frag_length_dist='&'#" ; else echo "# par_no_frag_length_dist="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_BIAS_LENGTH_THRESHOLD+x} ]; then echo "${VIASH_PAR_NO_BIAS_LENGTH_THRESHOLD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_bias_length_threshold='&'#" ; else echo "# par_no_bias_length_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_BIAS_SAMPLES+x} ]; then echo "${VIASH_PAR_NUM_BIAS_SAMPLES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_num_bias_samples='&'#" ; else echo "# par_num_bias_samples="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_AUX_MODEL_SAMPLES+x} ]; then echo "${VIASH_PAR_NUM_AUX_MODEL_SAMPLES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_num_aux_model_samples='&'#" ; else echo "# par_num_aux_model_samples="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES+x} ]; then echo "${VIASH_PAR_NUM_PRE_AUX_MODEL_SAMPLES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_num_pre_aux_model_samples='&'#" ; else echo "# par_num_pre_aux_model_samples="; fi ) +$( if [ ! -z ${VIASH_PAR_USEEM+x} ]; then echo "${VIASH_PAR_USEEM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_useEM='&'#" ; else echo "# par_useEM="; fi ) +$( if [ ! -z ${VIASH_PAR_USEVBOPT+x} ]; then echo "${VIASH_PAR_USEVBOPT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_useVBOpt='&'#" ; else echo "# par_useVBOpt="; fi ) +$( if [ ! -z ${VIASH_PAR_RANGE_FACTORIZATION_BINS+x} ]; then echo "${VIASH_PAR_RANGE_FACTORIZATION_BINS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_range_factorization_bins='&'#" ; else echo "# par_range_factorization_bins="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_GIBBS_SAMPLES+x} ]; then echo "${VIASH_PAR_NUM_GIBBS_SAMPLES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_num_Gibbs_samples='&'#" ; else echo "# par_num_Gibbs_samples="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_GAMMA_DRAW+x} ]; then echo "${VIASH_PAR_NO_GAMMA_DRAW}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_Gamma_draw='&'#" ; else echo "# par_no_Gamma_draw="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_BOOTSTRAPS+x} ]; then echo "${VIASH_PAR_NUM_BOOTSTRAPS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_num_bootstraps='&'#" ; else echo "# par_num_bootstraps="; fi ) +$( if [ ! -z ${VIASH_PAR_BOOTSTRAP_REPROJECT+x} ]; then echo "${VIASH_PAR_BOOTSTRAP_REPROJECT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bootstrap_reproject='&'#" ; else echo "# par_bootstrap_reproject="; fi ) +$( if [ ! -z ${VIASH_PAR_THINNING_FACTOR+x} ]; then echo "${VIASH_PAR_THINNING_FACTOR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_thinning_factor='&'#" ; else echo "# par_thinning_factor="; fi ) +$( if [ ! -z ${VIASH_PAR_QUIET+x} ]; then echo "${VIASH_PAR_QUIET}" | sed "s#'#'\\"'\\"'#g;s#.*#par_quiet='&'#" ; else echo "# par_quiet="; fi ) +$( if [ ! -z ${VIASH_PAR_PER_TRANSCRIPT_PRIOR+x} ]; then echo "${VIASH_PAR_PER_TRANSCRIPT_PRIOR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_per_transcript_prior='&'#" ; else echo "# par_per_transcript_prior="; fi ) +$( if [ ! -z ${VIASH_PAR_PER_NUCLEOTIDE_PRIOR+x} ]; then echo "${VIASH_PAR_PER_NUCLEOTIDE_PRIOR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_per_nucleotide_prior='&'#" ; else echo "# par_per_nucleotide_prior="; fi ) +$( if [ ! -z ${VIASH_PAR_SIG_DIGITS+x} ]; then echo "${VIASH_PAR_SIG_DIGITS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sig_digits='&'#" ; else echo "# par_sig_digits="; fi ) +$( if [ ! -z ${VIASH_PAR_VB_PRIOR+x} ]; then echo "${VIASH_PAR_VB_PRIOR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_vb_prior='&'#" ; else echo "# par_vb_prior="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_ORPHAN_LINKS+x} ]; then echo "${VIASH_PAR_WRITE_ORPHAN_LINKS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_write_orphan_links='&'#" ; else echo "# par_write_orphan_links="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_UNMAPPED_NAMES+x} ]; then echo "${VIASH_PAR_WRITE_UNMAPPED_NAMES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_write_unmapped_names='&'#" ; else echo "# par_write_unmapped_names="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_ERROR_MODEL+x} ]; then echo "${VIASH_PAR_NO_ERROR_MODEL}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_error_model='&'#" ; else echo "# par_no_error_model="; fi ) +$( if [ ! -z ${VIASH_PAR_NUM_ERROR_BINS+x} ]; then echo "${VIASH_PAR_NUM_ERROR_BINS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_num_error_bins='&'#" ; else echo "# par_num_error_bins="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_OUT+x} ]; then echo "${VIASH_PAR_SAMPLE_OUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sample_out='&'#" ; else echo "# par_sample_out="; fi ) +$( if [ ! -z ${VIASH_PAR_SAMPLE_UNALIGNED+x} ]; then echo "${VIASH_PAR_SAMPLE_UNALIGNED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sample_unaligned='&'#" ; else echo "# par_sample_unaligned="; fi ) +$( if [ ! -z ${VIASH_PAR_GENCODE+x} ]; then echo "${VIASH_PAR_GENCODE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_gencode='&'#" ; else echo "# par_gencode="; fi ) +$( if [ ! -z ${VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT+x} ]; then echo "${VIASH_PAR_MAPPING_CACHE_MEMORY_LIMIT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_mapping_cache_memory_limit='&'#" ; else echo "# par_mapping_cache_memory_limit="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +[[ "\\$par_discard_orphans" == "false" ]] && unset par_discard_orphans +[[ "\\$par_ont" == "false" ]] && unset par_ont +[[ "\\$par_seq_bias" == "false" ]] && unset par_seq_bias +[[ "\\$par_gc_bias" == "false" ]] && unset par_gc_bias +[[ "\\$par_pos_bias" == "false" ]] && unset par_pos_bias +[[ "\\$par_meta" == "false" ]] && unset par_meta +[[ "\\$par_discard_orphans_quasi" == "false" ]] && unset par_discard_orphans_quasi +[[ "\\$par_disable_chaining_heuristic" == "false" ]] && unset par_disable_chaining_heuristic +[[ "\\$par_allow_dovetail" == "false" ]] && unset par_allow_dovetail +[[ "\\$par_recover_orphans" == "false" ]] && unset par_recover_orphans +[[ "\\$par_mimicBT2" == "false" ]] && unset par_mimicBT2 +[[ "\\$par_mimic_strictBT2" == "false" ]] && unset par_mimic_strictBT2 +[[ "\\$par_softclip" == "false" ]] && unset par_softclip +[[ "\\$par_softclip_overhangs" == "false" ]] && unset par_softclip_overhangs +[[ "\\$par_full_length_alignment" == "false" ]] && unset par_full_length_alignment +[[ "\\$par_hard_filter" == "false" ]] && unset par_hard_filter +[[ "\\$par_write_mappings" == "false" ]] && unset par_write_mappings +[[ "\\$par_write_qualities" == "false" ]] && unset par_write_qualities +[[ "\\$par_alternative_init_mode" == "false" ]] && unset par_alternative_init_mode +[[ "\\$par_skip_quant" == "false" ]] && unset par_skip_quant +[[ "\\$par_dump_eq" == "false" ]] && unset par_dump_eq +[[ "\\$par_dump_eq_weights" == "false" ]] && unset par_dump_eq_weights +[[ "\\$par_reduce_GC_memory" == "false" ]] && unset par_reduce_GC_memory +[[ "\\$par_init_uniform" == "false" ]] && unset par_init_uniform +[[ "\\$par_no_length_correction" == "false" ]] && unset par_no_length_correction +[[ "\\$par_no_effective_length_correction" == "false" ]] && unset par_no_effective_length_correction +[[ "\\$par_no_single_frag_prob" == "false" ]] && unset par_no_single_frag_prob +[[ "\\$par_no_frag_length_dist" == "false" ]] && unset par_no_frag_length_dist +[[ "\\$par_no_bias_length_threshold" == "false" ]] && unset par_no_bias_length_threshold +[[ "\\$par_useEM" == "false" ]] && unset par_useEM +[[ "\\$par_useVBOpt" == "false" ]] && unset par_useVBOpt +[[ "\\$par_no_Gamma_draw" == "false" ]] && unset par_no_Gamma_draw +[[ "\\$par_bootstrap_reproject" == "false" ]] && unset par_bootstrap_reproject +[[ "\\$par_quiet" == "false" ]] && unset par_quiet +[[ "\\$par_per_transcript_prior" == "false" ]] && unset par_per_transcript_prior +[[ "\\$par_per_nucleotide_prior" == "false" ]] && unset par_per_nucleotide_prior +[[ "\\$par_write_orphan_links" == "false" ]] && unset par_write_orphan_links +[[ "\\$par_write_unmapped_names" == "false" ]] && unset par_write_unmapped_names +[[ "\\$par_no_error_model" == "false" ]] && unset par_no_error_model +[[ "\\$par_sample_out" == "false" ]] && unset par_sample_out +[[ "\\$par_sample_unaligned" == "false" ]] && unset par_sample_unaligned +[[ "\\$par_gencode" == "false" ]] && unset par_gencode + +IFS=";" read -ra unmated_reads <<< \\$par_unmated_reads +IFS=";" read -ra mates1 <<< \\$par_mates1 +IFS=";" read -ra mates2 <<< \\$par_mates2 +IFS=";" read -ra alignment <<< \\$par_alignments + +salmon quant \\\\ + \\${par_lib_type:+-l "\\${par_lib_type}"} \\\\ + \\${par_index:+-i "\\${par_index}"} \\\\ + \\${par_unmated_reads:+-r \\${unmated_reads[*]}} \\\\ + \\${par_mates1:+-1 \\${mates1[*]}} \\\\ + \\${par_mates2:+-2 \\${mates2[*]}} \\\\ + \\${par_alignments:+-a \\${alignment[*]}} \\\\ + \\${par_discard_orphans:+--discardOrphans} \\\\ + \\${par_eqclasses:+-e "\\${par_eqclasses}"} \\\\ + \\${par_targets:+-t "\\${par_targets}"} \\\\ + \\${par_ont:+--ont} \\\\ + \\${par_output:+-o "\\${par_output}"} \\\\ + \\${par_seq_bias:+--seqBias} \\\\ + \\${par_gc_bias:+--gcBias} \\\\ + \\${par_pos_bias:+--posBias} \\\\ + \\${meta_cpus:+-p "\\${meta_cpus}"} \\\\ + \\${par_incompat_prior:+--incompatPrior "\\${par_incompat_prior}"} \\\\ + \\${par_gene_map:+-g "\\${par_gene_map}"} \\\\ + \\${par_aux_target_file:+--auxTargetFile "\\${par_aux_target_file}"} \\\\ + \\${par_meta:+--meta} \\\\ + \\${par_score_exp:+--scoreExp "\\${par_score_exp}"} \\\\ + \\${par_discard_orphans_quasi:+--discardOrphansQuasi} \\\\ + \\${par_consensus_slack:+--consensusSlack "\\${par_consensus_slack}"} \\\\ + \\${par_pre_merge_chain_sub_thresh:+--preMergeChainSubThresh "\\${par_pre_merge_chain_sub_thresh}"} \\\\ + \\${par_post_merge_chain_sub_thresh:+--postMergeChainSubThresh "\\${par_post_merge_chain_sub_thresh}"} \\\\ + \\${par_orphan_chain_sub_thresh:+--orphanChainSubThresh "\\${par_orphan_chain_sub_thresh}"} \\\\ + \\${par_min_score_fraction:+--minScoreFraction "\\${par_min_score_fraction}"} \\\\ + \\${par_mismatch_seed_skip:+--mismatchSeedSkip "\\${par_mismatch_seed_skip}"} \\\\ + \\${par_disable_chaining_heuristic:+--disableChainingHeuristic} \\\\ + \\${par_decoy_threshold:+--decoyThreshold "\\${par_decoy_threshold}"} \\\\ + \\${par_ma:+--ma "\\${par_ma}"} \\\\ + \\${par_mp:+--mp "\\${par_mp}"} \\\\ + \\${par_go:+--go "\\${par_go}"} \\\\ + \\${par_ge:+--ge "\\${par_ge}"} \\\\ + \\${par_bandwidth:+--bandwidth "\\${par_bandwidth}"} \\\\ + \\${par_allow_dovetail:+--allowDovetail} \\\\ + \\${par_recover_orphans:+--recoverOrphans} \\\\ + \\${par_mimicBT2:+--mimicBT2} \\\\ + \\${par_mimic_strictBT2:+--mimicStrictBT2} \\\\ + \\${par_softclip:+--softclip} \\\\ + \\${par_softclip_overhangs:+--softclipOverhangs} \\\\ + \\${par_full_length_alignment:+--fullLengthAlignment} \\\\ + \\${par_hard_filter:+--hardFilter} \\\\ + \\${par_min_aln_prob:+--minAlnProb "\\${par_min_aln_prob}"} \\\\ + \\${par_write_mappings:+--write_mappings="\\${par_mappings_sam}"} \\\\ + \\${par_write_qualities:+--writeQualities} \\\\ + \\${par_hit_filter_policy:+--hitFilterPolicy "\\${par_hit_filter_policy}"} \\\\ + \\${par_alternative_init_mode:+--alternativeInitMode} \\\\ + \\${par_aux_dir:+--auxDir "\\${par_aux_dir}"} \\\\ + \\${par_skip_quant:+--skipQuant} \\\\ + \\${par_dump_eq:+--dumpEq} \\\\ + \\${par_dump_eq_weights:+-d "\\${par_dump_eq_weights}"} \\\\ + \\${par_min_assigned_frags:+--minAssignedFrags "\\${par_min_assigned_frags}"} \\\\ + \\${par_reduce_GC_memory:+--reduceGCMemory} \\\\ + \\${par_bias_speed_samp:+--biasSpeedSamp "\\${par_bias_speed_samp}"} \\\\ + \\${par_fld_max:+--fldMax "\\${par_fld_max}"} \\\\ + \\${par_fld_mean:+--fldMean "\\${par_fld_mean}"} \\\\ + \\${par_fld_SD:+--fldSD "\\${par_fld_SD}"} \\\\ + \\${par_forgetting_factor:+-f "\\${par_forgetting_factor}"} \\\\ + \\${par_init_uniform:+--initUniform} \\\\ + \\${par_max_occs_per_hit:+--maxOccsPerHit "\\${par_max_occs_per_hit}"} \\\\ + \\${par_max_read_occ:+-w "\\${par_max_read_occ}"} \\\\ + \\${par_no_length_correction:+--noLengthCorrection} \\\\ + \\${par_no_effective_length_correction:+--noEffectiveLengthCorrection} \\\\ + \\${par_no_single_frag_prob:+--noSingleFragProb} \\\\ + \\${par_no_frag_length_dist:+--noFragLengthDist} \\\\ + \\${par_no_bias_length_threshold:+--noBiasLengthThreshold} \\\\ + \\${par_num_bias_samples:+--numBiasSamples "\\${par_num_bias_samples}"} \\\\ + \\${par_num_aux_model_samples:+--numAuxModelSamples "\\${par_num_aux_model_samples}"} \\\\ + \\${par_num_pre_aux_model_samples:+--numPreAuxModelSamples "\\${par_num_pre_aux_model_samples}"} \\\\ + \\${par_useEM:+--useEM} \\\\ + \\${par_useVBOpt:+--useVBOpt} \\\\ + \\${par_range_factorization_bins:+--rangeFactorizationBins "\\${par_range_factorization_bins}"} \\\\ + \\${par_num_Gibbs_samples:+--numGibbsSamples "\\${par_num_Gibbs_samples}"} \\\\ + \\${par_no_Gamma_draw:+--noGammaDraw} \\\\ + \\${par_num_bootstraps:+--numBootstraps "\\${par_num_bootstraps}"} \\\\ + \\${par_bootstrap_reproject:+--bootstrapReproject} \\\\ + \\${par_thinning_factor:+--thinningFactor "\\${par_thinning_factor}"} \\\\ + \\${par_quiet:+--quiet} \\\\ + \\${par_per_transcript_prior:+--perTranscriptPrior} \\\\ + \\${par_per_nucleotide_prior:+--perNucleotidePrior} \\\\ + \\${par_sig_digits:+--sigDigits "\\${par_sig_digits}"} \\\\ + \\${par_vb_prior:+--vbPrior "\\${par_vb_prior}"} \\\\ + \\${par_write_orphan_links:+--writeOrphanLinks} \\\\ + \\${par_write_unmapped_names:+--writeUnmappedNames} \\\\ + \\${par_no_error_model:+--noErrorModel} \\\\ + \\${par_num_error_bins:+--numErrorBins "\\${par_num_error_bins}"} \\\\ + \\${par_sample_out:+--sampleOut} \\\\ + \\${par_sample_unaligned:+--sampleUnaligned} \\\\ + \\${par_gencode:+--gencode} \\\\ + \\${par_mapping_cache_memory_limit:+--mappingCacheMemoryLimit "\\${par_mapping_cache_memory_limit}"} + +if [ -f "\\$par_output/quant.sf" ]; then + mv \\$par_output/quant.sf \\$par_quant_results +else + echo "Quantification file not generated!" +fi +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/salmon/salmon_quant", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/salmon/salmon_quant/nextflow.config b/target/nextflow/salmon/salmon_quant/nextflow.config new file mode 100644 index 00000000..277ab594 --- /dev/null +++ b/target/nextflow/salmon/salmon_quant/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'salmon/salmon_quant' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Salmon is a tool for wicked-fast transcript quantification from RNA-seq data. It can either make use of pre-computed alignments (in the form of a SAM/BAM file) to the transcripts rather than the raw reads, or can be run in the mapping-based mode. \n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/salmon/salmon_quant/nextflow_schema.json b/target/nextflow/salmon/salmon_quant/nextflow_schema.json new file mode 100644 index 00000000..80b21398 --- /dev/null +++ b/target/nextflow/salmon/salmon_quant/nextflow_schema.json @@ -0,0 +1,1119 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "salmon_quant", +"description": "Salmon is a tool for wicked-fast transcript quantification from RNA-seq data. It can either make use of pre-computed alignments (in the form of a SAM/BAM file) to the transcripts rather than the raw reads, or can be run in the mapping-based mode. \n", +"type": "object", +"definitions": { + + + + "common input options" : { + "title": "Common input options", + "type": "object", + "description": "No description", + "properties": { + + + "lib_type": { + "type": + "string", + "description": "Type: `string`, default: `A`, choices: ``A`, `U`, `SF`, `SR`, `IU`, `IS`, `ISF`, `ISR`, `OU`, `OS`, `OSF`, `OSR`, `MU`, `MS`, `MSF`, `MSR``. Format string describing the library", + "help_text": "Type: `string`, default: `A`, choices: ``A`, `U`, `SF`, `SR`, `IU`, `IS`, `ISF`, `ISR`, `OU`, `OS`, `OSF`, `OSR`, `MU`, `MS`, `MSF`, `MSR``. Format string describing the library.\nThe library type string consists of three parts: \n1. Relative orientation of the reads: This part is only provided if the library is paired-end, THe possible options are\n I = inward\n O = outward\n M = matching\n2. Strandedness of the library: This part specifies whether the protocol is stranded or unstranded. The options are:\n S = stranded\n U = unstranded\n3. Directionality of the reads: If the library is stranded, the final part of the library string is used to specify the strand from which the read originates. The possible values are\n F = read 1 (or single-end read) comes from the forward strand\n R = read 1 (or single-end read) comes from the reverse strand\n", + "enum": ["A", "U", "SF", "SR", "IU", "IS", "ISF", "ISR", "OU", "OS", "OSF", "OSR", "MU", "MS", "MSF", "MSR"] + + , + "default": "A" + } + + +} +}, + + + "mapping input options" : { + "title": "Mapping input options", + "type": "object", + "description": "No description", + "properties": { + + + "index": { + "type": + "string", + "description": "Type: `file`, example: `transcriptome_index`. Salmon index", + "help_text": "Type: `file`, example: `transcriptome_index`. Salmon index.\n" + + } + + + , + "unmated_reads": { + "type": + "string", + "description": "Type: List of `file`, example: `sample.fq.gz`, multiple_sep: `\":\"`. List of files containing unmated reads of (e", + "help_text": "Type: List of `file`, example: `sample.fq.gz`, multiple_sep: `\":\"`. List of files containing unmated reads of (e.g. single-end reads).\n" + + } + + + , + "mates1": { + "type": + "string", + "description": "Type: List of `file`, example: `sample_1.fq.gz`, multiple_sep: `\":\"`. File containing the #1 mates", + "help_text": "Type: List of `file`, example: `sample_1.fq.gz`, multiple_sep: `\":\"`. File containing the #1 mates.\n" + + } + + + , + "mates2": { + "type": + "string", + "description": "Type: List of `file`, example: `sample_2.fq.gz`, multiple_sep: `\":\"`. File containing the #2 mates", + "help_text": "Type: List of `file`, example: `sample_2.fq.gz`, multiple_sep: `\":\"`. File containing the #2 mates.\n" + + } + + +} +}, + + + "alignment input options" : { + "title": "Alignment input options", + "type": "object", + "description": "No description", + "properties": { + + + "discard_orphans": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Discard orphan alignments in the input [for alignment-based mode only]", + "help_text": "Type: `boolean_true`, default: `false`. Discard orphan alignments in the input [for alignment-based mode only]. If this flag is passed, then only paired alignments will be considered toward quantification estimates. The default behavior is to consider orphan alignments if no valid paired mappings exist.\n" + , + "default": "False" + } + + + , + "alignments": { + "type": + "string", + "description": "Type: List of `file`, example: `sample.fq.gz`, multiple_sep: `\":\"`. Input alignment (BAM) file(s)", + "help_text": "Type: List of `file`, example: `sample.fq.gz`, multiple_sep: `\":\"`. Input alignment (BAM) file(s).\n" + + } + + + , + "eqclasses": { + "type": + "string", + "description": "Type: `file`. input salmon weighted equivalence class file", + "help_text": "Type: `file`. input salmon weighted equivalence class file.\n" + + } + + + , + "targets": { + "type": + "string", + "description": "Type: `file`, example: `transcripts.fasta`. FASTA format file containing target transcripts", + "help_text": "Type: `file`, example: `transcripts.fasta`. FASTA format file containing target transcripts.\n" + + } + + + , + "ont": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use alignment model for Oxford Nanopore long reads\n", + "help_text": "Type: `boolean_true`, default: `false`. Use alignment model for Oxford Nanopore long reads\n" + , + "default": "False" + } + + +} +}, + + + "output" : { + "title": "Output", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output.output`, example: `quant_output`. Output quantification directory", + "help_text": "Type: `file`, required, default: `$id.$key.output.output`, example: `quant_output`. Output quantification directory.\n" + , + "default": "$id.$key.output.output" + } + + + , + "quant_results": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.quant_results.sf`, example: `quant.sf`. Salmon quantification file", + "help_text": "Type: `file`, required, default: `$id.$key.quant_results.sf`, example: `quant.sf`. Salmon quantification file.\n" + , + "default": "$id.$key.quant_results.sf" + } + + +} +}, + + + "basic options" : { + "title": "Basic options", + "type": "object", + "description": "No description", + "properties": { + + + "seq_bias": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Perform sequence-specific bias correction", + "help_text": "Type: `boolean_true`, default: `false`. Perform sequence-specific bias correction.\n" + , + "default": "False" + } + + + , + "gc_bias": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Perform fragment GC bias correction [beta for single-end reads]", + "help_text": "Type: `boolean_true`, default: `false`. Perform fragment GC bias correction [beta for single-end reads].\n" + , + "default": "False" + } + + + , + "pos_bias": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Perform positional bias correction", + "help_text": "Type: `boolean_true`, default: `false`. Perform positional bias correction.\n" + , + "default": "False" + } + + + , + "incompat_prior": { + "type": + "number", + "description": "Type: `double`, example: `0`. Set the prior probability that an alignment that disagrees with the specified library type (--lib_type) results from the true fragment origin", + "help_text": "Type: `double`, example: `0`. Set the prior probability that an alignment that disagrees with the specified library type (--lib_type) results from the true fragment origin. Setting this to 0 specifies that alignments that disagree with the library type should be \"impossible\", while setting it to 1 says that alignments that disagree with the library type are no less likely than those that do.\n" + + } + + + , + "gene_map": { + "type": + "string", + "description": "Type: `file`, example: `gene_map.gtf`. File containing a mapping of transcripts to genes", + "help_text": "Type: `file`, example: `gene_map.gtf`. File containing a mapping of transcripts to genes. If this file is provided salmon will output both quant.sf and quant.genes.sf files, where the latter contains aggregated gene-level abundance estimates. The transcript to gene mapping should be provided as either a GTF file, or a in a simple tab-delimited format where each line contains the name of a transcript and the gene to which it belongs separated by a tab. The extension of the file is used to determine how the file should be parsed. Files ending in \u0027.gtf\u0027, \u0027.gff\u0027 or \u0027.gff3\u0027 are assumed to be in GTF format; files with any other extension are assumed to be in the simple format. In GTF / GFF format, the \"transcript_id\" is assumed to contain the transcript identifier and the \"gene_id\" is assumed to contain the corresponding gene identifier.\n" + + } + + + , + "aux_target_file": { + "type": + "string", + "description": "Type: `file`, example: `auxilary_targets.txt`. A file containing a list of \"auxiliary\" targets", + "help_text": "Type: `file`, example: `auxilary_targets.txt`. A file containing a list of \"auxiliary\" targets. These are valid targets (i.e., not decoys) to which fragments are allowed to map and be assigned, and which will be quantified, but for which auxiliary models like sequence-specific and fragment-GC bias correction should not be applied.\n" + + } + + + , + "meta": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. If you\u0027re using Salmon on a metagenomic dataset, consider setting this flag to disable parts of the abundance estimation model that make less sense for metagenomic data", + "help_text": "Type: `boolean_true`, default: `false`. If you\u0027re using Salmon on a metagenomic dataset, consider setting this flag to disable parts of the abundance estimation model that make less sense for metagenomic data.\n" + , + "default": "False" + } + + + , + "score_exp": { + "type": + "number", + "description": "Type: `double`, example: `1`. The factor by which sub-optimal alignment scores are downweighted to produce a probability", + "help_text": "Type: `double`, example: `1`. The factor by which sub-optimal alignment scores are downweighted to produce a probability. If the best alignment score for the current read is S, and the score for a particular alignment is w, then the probability will be computed porportional to exp( - scoreExp * (S-w) ).\n" + + } + + +} +}, + + + "options specific to mapping mode" : { + "title": "Options specific to mapping mode", + "type": "object", + "description": "No description", + "properties": { + + + "discard_orphans_quasi": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nDiscard orphan mappings in selective-alignment mode", + "help_text": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nDiscard orphan mappings in selective-alignment mode. If this flag is passed then only paired mappings will be considered toward quantification estimates. The default behavior is to consider orphan mappings if no valid paired mappings exist. This flag is independent of the option to write the orphaned mappings to file (--writeOrphanLinks).\n" + , + "default": "False" + } + + + , + "consensus_slack": { + "type": + "number", + "description": "Type: `double`, example: `0.35`. [selective-alignment mode only] \nThe amount of slack allowed in the selective-alignment filtering mechanism", + "help_text": "Type: `double`, example: `0.35`. [selective-alignment mode only] \nThe amount of slack allowed in the selective-alignment filtering mechanism. If this is set to a fraction, X, greater than 0 (and in [0,1)), then uniMEM chains with scores below (100 * X)% of the best chain score for a read, and read pairs with a sum of chain scores below (100 * X)% of the best chain score for a read pair will be discounted as a mapping candidates. The default value of this option is 0.35.\n" + + } + + + , + "pre_merge_chain_sub_thresh": { + "type": + "number", + "description": "Type: `double`, example: `0.75`. [selective-alignment mode only] \nThe threshold of sub-optimal chains, compared to the best chain on a given target, that will be retained and passed to the next phase of mapping", + "help_text": "Type: `double`, example: `0.75`. [selective-alignment mode only] \nThe threshold of sub-optimal chains, compared to the best chain on a given target, that will be retained and passed to the next phase of mapping. Specifically, if the best chain for a read (or read-end in paired-end mode) to target t has score X_t, then all chains for this read with score \u003e= X_t * preMergeChainSubThresh will be retained and passed to subsequent mapping phases. This value must be in the range [0, 1].\n" + + } + + + , + "post_merge_chain_sub_thresh": { + "type": + "number", + "description": "Type: `double`, example: `0.9`. [selective-alignment mode only] \nThe threshold of sub-optimal chains, compared to the best chain on a given target, that will be retained and passed to the next phase of mapping", + "help_text": "Type: `double`, example: `0.9`. [selective-alignment mode only] \nThe threshold of sub-optimal chains, compared to the best chain on a given target, that will be retained and passed to the next phase of mapping. This is different than post_merge_chain_sub_thresh, because this is applied to pairs of chains (from the ends of paired-end reads) after merging (i.e. after checking concordancy constraints etc.). Specifically, if the best chain pair to target t has score X_t, then all chain pairs for this read pair with score \u003e= X_t * post_merge_chain_sub_thresh will be retained and passed to subsequent mapping phases. This value must be in the range [0, 1]. Note: This option is only meaningful for paired-end libraries, and is ignored for single-end libraries.\n" + + } + + + , + "orphan_chain_sub_thresh": { + "type": + "number", + "description": "Type: `double`, example: `0.95`. [selective-alignment mode only]\nThis threshold sets a global sub-optimality threshold for chains corresponding to orphan mappings", + "help_text": "Type: `double`, example: `0.95`. [selective-alignment mode only]\nThis threshold sets a global sub-optimality threshold for chains corresponding to orphan mappings. That is, if the merging procedure results in no concordant mappings then only orphan mappings with a chain score \u003e= orphan_chain_sub_thresh * bestChainScore will be retained and passed to subsequent mapping phases. This value must be in the range [0, 1]. Note: This option is only meaningful for paired-end libraries, and is ignored for single-end libraries.\n" + + } + + + , + "min_score_fraction": { + "type": + "number", + "description": "Type: `double`, example: `0.65`. [selective-alignment mode only]\nThe fraction of the optimal possible alignment score that a mapping must achieve in order to be considered \"valid\" --- should be in (0,1]", + "help_text": "Type: `double`, example: `0.65`. [selective-alignment mode only]\nThe fraction of the optimal possible alignment score that a mapping must achieve in order to be considered \"valid\" --- should be in (0,1]. Default 0.65\n" + + } + + + , + "mismatch_seed_skip": { + "type": + "integer", + "description": "Type: `integer`, example: `3`. [selective-alignment mode only]\nAfter a k-mer hit is extended to a uni-MEM, the uni-MEM extension can terminate for one of 3 reasons; the end of the read, the end of the unitig, or a mismatch", + "help_text": "Type: `integer`, example: `3`. [selective-alignment mode only]\nAfter a k-mer hit is extended to a uni-MEM, the uni-MEM extension can terminate for one of 3 reasons; the end of the read, the end of the unitig, or a mismatch. If the extension ends because of a mismatch, this is likely the result of a sequencing error. To avoid looking up many k-mers that will likely fail to be located in the index, the search procedure skips by a factor of mismatch_seed_skip until it either (1) finds another match or (2) is k-bases past the mismatch position. This value controls that skip length. A smaller value can increase sensitivity, while a larger value can speed up seeding.\n" + + } + + + , + "disable_chaining_heuristic": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nBy default, the heuristic of (Li 2018) is implemented, which terminates the chaining DP once a given number of valid backpointers are found", + "help_text": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nBy default, the heuristic of (Li 2018) is implemented, which terminates the chaining DP once a given number of valid backpointers are found. This speeds up the seed (MEM) chaining step, but may result in sub-optimal chains in complex situations (e.g. sequences with many repeats and overlapping repeats). Passing this flag will disable the chaining heuristic, and perform the full chaining dynamic program, guaranteeing the optimal chain is found in this step.\n" + , + "default": "False" + } + + + , + "decoy_threshold": { + "type": + "number", + "description": "Type: `double`, example: `1`. [selective-alignment mode only]\nFor an alignemnt to an annotated transcript to be considered invalid, it must have an alignment score \u003c (decoy_threshold * bestDecoyScore)", + "help_text": "Type: `double`, example: `1`. [selective-alignment mode only]\nFor an alignemnt to an annotated transcript to be considered invalid, it must have an alignment score \u003c (decoy_threshold * bestDecoyScore). A value of 1.0 means that any alignment strictly worse than the best decoy alignment will be discarded. A smaller value will allow reads to be allocated to transcripts even if they strictly align better to the decoy sequence.\n" + + } + + + , + "ma": { + "type": + "integer", + "description": "Type: `integer`, example: `2`. [selective-alignment mode only]\nThe value given to a match between read and reference nucleotides in an alignment", + "help_text": "Type: `integer`, example: `2`. [selective-alignment mode only]\nThe value given to a match between read and reference nucleotides in an alignment.\n" + + } + + + , + "mp": { + "type": + "integer", + "description": "Type: `integer`, example: `-4`. [selective-alignment mode only]\nThe value given to a mis-match between read and reference nucleotides in an alignment", + "help_text": "Type: `integer`, example: `-4`. [selective-alignment mode only]\nThe value given to a mis-match between read and reference nucleotides in an alignment.\n" + + } + + + , + "go": { + "type": + "integer", + "description": "Type: `integer`, example: `6`. [selective-alignment mode only]\nThe value given to a gap opening in an alignment", + "help_text": "Type: `integer`, example: `6`. [selective-alignment mode only]\nThe value given to a gap opening in an alignment.\n" + + } + + + , + "ge": { + "type": + "integer", + "description": "Type: `integer`, example: `2`. [selective-alignment mode only]\nThe value given to a gap extension in an alignment", + "help_text": "Type: `integer`, example: `2`. [selective-alignment mode only]\nThe value given to a gap extension in an alignment.\n" + + } + + + , + "bandwidth": { + "type": + "integer", + "description": "Type: `integer`, example: `15`. [selective-alignment mode only]\nThe value used for the bandwidth passed to ksw2", + "help_text": "Type: `integer`, example: `15`. [selective-alignment mode only]\nThe value used for the bandwidth passed to ksw2. A smaller bandwidth can make the alignment verification run more quickly, but could possibly miss valid alignments.\n" + + } + + + , + "allow_dovetail": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nAllow dovetailing mappings", + "help_text": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nAllow dovetailing mappings.\n" + , + "default": "False" + } + + + , + "recover_orphans": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nAttempt to recover the mates of orphaned reads", + "help_text": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nAttempt to recover the mates of orphaned reads. This uses edlib for orphan recovery, and so introduces some computational overhead, but it can improve sensitivity.\n" + , + "default": "False" + } + + + , + "mimicBT2": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nSet flags to mimic parameters similar to Bowtie2 with --no-discordant and --no-mixed flags", + "help_text": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nSet flags to mimic parameters similar to Bowtie2 with --no-discordant and --no-mixed flags. This increases disallows dovetailing reads, and discards orphans. Note, this does not impose the very strict parameters assumed by RSEM+Bowtie2, like gapless alignments. For that behavior, use the --mimic_strictBT2 flag below.\n" + , + "default": "False" + } + + + , + "mimic_strictBT2": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nSet flags to mimic the very strict parameters used by RSEM+Bowtie2", + "help_text": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nSet flags to mimic the very strict parameters used by RSEM+Bowtie2. This increases --min_score_fraction to 0.8, disallows dovetailing reads, discards orphans, and disallows gaps in alignments.\n" + , + "default": "False" + } + + + , + "softclip": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nAllos soft-clipping of reads during selective-alignment", + "help_text": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nAllos soft-clipping of reads during selective-alignment. If this option is provided, then regions at the beginning or end of the read can be withheld from alignment without any effect on the resulting score (i.e. neither adding nor removing from the score). This will drastically reduce the penalty if there are mismatches at the beginning or end of the read due to e.g. low-quality bases or adapters. NOTE: Even with soft-clipping enabled, the read must still achieve a score of at least min_score_fraction * maximum achievable score, where the maximum achievable score is computed based on the full (un-clipped) read length.\n" + , + "default": "False" + } + + + , + "softclip_overhangs": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nAllow soft-clipping of reads that overhang the beginning or ends of the transcript", + "help_text": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nAllow soft-clipping of reads that overhang the beginning or ends of the transcript. In this case, the overhaning section of the read will simply be unaligned, and will not contribute or detract from the alignment score. The default policy is to force an end-to-end alignment of the entire read, so that overhanings will result in some deletion of nucleotides from the read.\n" + , + "default": "False" + } + + + , + "full_length_alignment": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nPerform selective alignment over the full length of the read, beginning from the (approximate) initial mapping location and using extension alignment", + "help_text": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nPerform selective alignment over the full length of the read, beginning from the (approximate) initial mapping location and using extension alignment. This is in contrast with the default behavior which is to only perform alignment between the MEMs in the optimal chain (and before the first and after the last MEM if applicable). The default strategy forces the MEMs to belong to the alignment, but has the benefit that it can discover indels prior to the first hit shared between the read and reference. Except in very rare circumstances, the default mode should be more accurate.\n" + , + "default": "False" + } + + + , + "hard_filter": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nInstead of weighting mappings by their alignment score, this flag will discard any mappings with sub-optimal alignment score", + "help_text": "Type: `boolean_true`, default: `false`. [selective-alignment mode only] \nInstead of weighting mappings by their alignment score, this flag will discard any mappings with sub-optimal alignment score. The default option of soft-filtering (i.e. weighting mappings by their alignment score) usually yields slightly more accurate abundance estimates but this flag may be desirable if you want more accurate \u0027naive\u0027 equivalence classes, rather than range factorized equivalence classes.\n" + , + "default": "False" + } + + + , + "min_aln_prob": { + "type": + "number", + "description": "Type: `double`, example: `1.0E-5`. The minimum number of fragments that must be assigned to the transcriptome for quantification to proceed", + "help_text": "Type: `double`, example: `1.0E-5`. The minimum number of fragments that must be assigned to the transcriptome for quantification to proceed.\n" + + } + + + , + "write_mappings": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. If this option is provided, then the selective-alignment results will be written out in SAM-compatible format", + "help_text": "Type: `boolean_true`, default: `false`. If this option is provided, then the selective-alignment results will be written out in SAM-compatible format. By default, output will be directed to stdout, but an alternative file name can be provided instead.\n" + , + "default": "False" + } + + + , + "mapping_sam": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.mapping_sam.sam`, example: `mappings.sam`. Path to file that should output the selective-alignment results in SAM-compatible format", + "help_text": "Type: `file`, default: `$id.$key.mapping_sam.sam`, example: `mappings.sam`. Path to file that should output the selective-alignment results in SAM-compatible format. THis option must be provided while using --write_mappings" + , + "default": "$id.$key.mapping_sam.sam" + } + + + , + "write_qualities": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. This flag only has meaning if mappings are being written (with --write_mappings/-z)", + "help_text": "Type: `boolean_true`, default: `false`. This flag only has meaning if mappings are being written (with --write_mappings/-z). If this flag is provided, then the output SAM file will contain quality strings as well as read sequences. Note that this can greatly increase the size of the output file.\n" + , + "default": "False" + } + + + , + "hit_filter_policy": { + "type": + "string", + "description": "Type: `string`, example: `AFTER`, choices: ``BEFORE`, `AFTER`, `BOTH`, `NONE``. [selective-alignment mode only]\nDetermines the policy by which hits are filtered in selective alignment", + "help_text": "Type: `string`, example: `AFTER`, choices: ``BEFORE`, `AFTER`, `BOTH`, `NONE``. [selective-alignment mode only]\nDetermines the policy by which hits are filtered in selective alignment. Filtering hits after chaining (the default) is more sensitive, but more computationally intensive, because it performs the chaining dynamic program for all hits. Filtering before chaining is faster, but some true hits may be missed. The options are BEFORE, AFTER, BOTH and NONE.\n", + "enum": ["BEFORE", "AFTER", "BOTH", "NONE"] + + + } + + +} +}, + + + "advance options" : { + "title": "Advance options", + "type": "object", + "description": "No description", + "properties": { + + + "alternative_init_mode": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use an alternative strategy (rather than simple interpolation between) the online and uniform abundance estimates to initialize the EM / VBEM algorithm", + "help_text": "Type: `boolean_true`, default: `false`. Use an alternative strategy (rather than simple interpolation between) the online and uniform abundance estimates to initialize the EM / VBEM algorithm.\n" + , + "default": "False" + } + + + , + "aux_dir": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.aux_dir.aux_dir`, example: `aux_info`. The sub-directory of the quantification directory where auxiliary information e", + "help_text": "Type: `file`, default: `$id.$key.aux_dir.aux_dir`, example: `aux_info`. The sub-directory of the quantification directory where auxiliary information e.g. bootstraps, bias parameters, etc. will be written.\n" + , + "default": "$id.$key.aux_dir.aux_dir" + } + + + , + "skip_quant": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Skip performing the actual transcript quantification (including any Gibbs sampling or bootstrapping)", + "help_text": "Type: `boolean_true`, default: `false`. Skip performing the actual transcript quantification (including any Gibbs sampling or bootstrapping).\n" + , + "default": "False" + } + + + , + "dump_eq": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Dump the simple equivalence class counts that were computed during mapping or alignment", + "help_text": "Type: `boolean_true`, default: `false`. Dump the simple equivalence class counts that were computed during mapping or alignment.\n" + , + "default": "False" + } + + + , + "dump_eq_weights": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Dump conditional probabilities associated with transcripts when equivalence class information is being dumped to file", + "help_text": "Type: `boolean_true`, default: `false`. Dump conditional probabilities associated with transcripts when equivalence class information is being dumped to file. Note, this will dump the factorization that is actually used by salmon\u0027s offline phase for inference. If you are using range-factorized equivalence classes (the default) then the same transcript set may appear multiple times with different associated conditional probabilities.\n" + , + "default": "False" + } + + + , + "min_assigned_frags": { + "type": + "integer", + "description": "Type: `integer`, example: `10`. The minimum number of fragments that must be assigned to the transcriptome for quantification to proceed", + "help_text": "Type: `integer`, example: `10`. The minimum number of fragments that must be assigned to the transcriptome for quantification to proceed.\n" + + } + + + , + "reduce_GC_memory": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. If this option is selected, a more memory efficient (but slightly slower) representation is used to compute fragment GC content", + "help_text": "Type: `boolean_true`, default: `false`. If this option is selected, a more memory efficient (but slightly slower) representation is used to compute fragment GC content. Enabling this will reduce memory usage, but can also reduce speed. However, the results themselves will remain the same.\n" + , + "default": "False" + } + + + , + "bias_speed_samp": { + "type": + "integer", + "description": "Type: `integer`, example: `5`. The value at which the fragment length PMF is down-sampled when evaluating sequence-specific \u0026 GC fragment bias", + "help_text": "Type: `integer`, example: `5`. The value at which the fragment length PMF is down-sampled when evaluating sequence-specific \u0026 GC fragment bias. Larger values speed up effective length correction, but may decrease the fidelity of bias modeling results.\n" + + } + + + , + "fld_max": { + "type": + "integer", + "description": "Type: `integer`, example: `1000`. The maximum fragment length to consider when building the empirical distribution\n", + "help_text": "Type: `integer`, example: `1000`. The maximum fragment length to consider when building the empirical distribution\n" + + } + + + , + "fld_mean": { + "type": + "integer", + "description": "Type: `integer`, example: `250`. The mean used in the fragment length distribution prior\n", + "help_text": "Type: `integer`, example: `250`. The mean used in the fragment length distribution prior\n" + + } + + + , + "fld_SD": { + "type": + "integer", + "description": "Type: `integer`, example: `25`. The standard deviation used in the fragment length distribution prior\n", + "help_text": "Type: `integer`, example: `25`. The standard deviation used in the fragment length distribution prior\n" + + } + + + , + "forgetting_factor": { + "type": + "number", + "description": "Type: `double`, example: `0.65`. The forgetting factor used in the online learning schedule", + "help_text": "Type: `double`, example: `0.65`. The forgetting factor used in the online learning schedule. A smallervalue results in quicker learning, but higher variance and may be unstable. A larger value results in slower learning but may be more stable. Value should be in the interval (0.5, 1.0].\n" + + } + + + , + "init_uniform": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Initialize the offline inference with uniform parameters, rather than seeding with online parameters", + "help_text": "Type: `boolean_true`, default: `false`. Initialize the offline inference with uniform parameters, rather than seeding with online parameters.\n" + , + "default": "False" + } + + + , + "max_occs_per_hit": { + "type": + "integer", + "description": "Type: `integer`, example: `1000`. When collecting \"hits\" (MEMs), hits having more than max_occs_per_hit occurrences won\u0027t be considered", + "help_text": "Type: `integer`, example: `1000`. When collecting \"hits\" (MEMs), hits having more than max_occs_per_hit occurrences won\u0027t be considered.\n" + + } + + + , + "max_read_occ": { + "type": + "integer", + "description": "Type: `integer`, example: `200`. Reads \"mapping\" to more than this many places won\u0027t be considered", + "help_text": "Type: `integer`, example: `200`. Reads \"mapping\" to more than this many places won\u0027t be considered.\n" + + } + + + , + "no_length_correction": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Entirely disables length correction when estimating the abundance of transcripts", + "help_text": "Type: `boolean_true`, default: `false`. Entirely disables length correction when estimating the abundance of transcripts. This option can be used with protocols where one expects that fragments derive from their underlying targets without regard to that target\u0027s length (e.g. QuantSeq)\n" + , + "default": "False" + } + + + , + "no_effective_length_correction": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Disables effective length correction when computing the probability that a fragment was generated from a transcript", + "help_text": "Type: `boolean_true`, default: `false`. Disables effective length correction when computing the probability that a fragment was generated from a transcript. If this flag is passed in,the fragment length distribution is not taken into account when computing this probability.\n" + , + "default": "False" + } + + + , + "no_single_frag_prob": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Disables the estimation of an associated fragment length probability for single-end reads or for orphaned mappings in paired-end libraries", + "help_text": "Type: `boolean_true`, default: `false`. Disables the estimation of an associated fragment length probability for single-end reads or for orphaned mappings in paired-end libraries. The default behavior is to consider the probability of all possible fragment lengths associated with the retained mapping. Enabling this flag (i.e. turning this default behavior off) will simply not attempt to estimate a fragment length probability in such cases.\n" + , + "default": "False" + } + + + , + "no_frag_length_dist": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Don\u0027t consider concordance with the learned fragment length distribution when trying to determine the probability that a fragment has originated from a specified location", + "help_text": "Type: `boolean_true`, default: `false`. Don\u0027t consider concordance with the learned fragment length distribution when trying to determine the probability that a fragment has originated from a specified location. Normally, Fragments with unlikely lengths will be assigned a smaller relative probability than those with more likely lengths. When this flag is passed in, the observed fragment length has no effect on that fragment\u0027s a priori probability.\n" + , + "default": "False" + } + + + , + "no_bias_length_threshold": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. If this option is enabled, then no (lower) threshold will be set on how short bias correction can make effective lengths", + "help_text": "Type: `boolean_true`, default: `false`. If this option is enabled, then no (lower) threshold will be set on how short bias correction can make effective lengths. This can increase the precision of bias correction, but harm robustness. The default correction applies a threshold.\n" + , + "default": "False" + } + + + , + "num_bias_samples": { + "type": + "integer", + "description": "Type: `integer`, example: `2000000`. Number of fragment mappings to use when learning the sequence-specific bias model", + "help_text": "Type: `integer`, example: `2000000`. Number of fragment mappings to use when learning the sequence-specific bias model.\n" + + } + + + , + "num_aux_model_samples": { + "type": + "integer", + "description": "Type: `integer`, example: `5000000`. The first \u003cnum_aux_model_samples\u003e are used to train the auxiliary model parameters (e", + "help_text": "Type: `integer`, example: `5000000`. The first \u003cnum_aux_model_samples\u003e are used to train the auxiliary model parameters (e.g. fragment length distribution, bias, etc.). After ther first \u003cnum_aux_model_samples\u003e observations the auxiliary model parameters will be assumed to have converged and will be fixed.\n" + + } + + + , + "num_pre_aux_model_samples": { + "type": + "integer", + "description": "Type: `integer`, example: `5000`. The first \u003cnumPreAuxModelSamples\u003e will have their assignment likelihoods and contributions to the transcript abundances computed without applying any auxiliary models", + "help_text": "Type: `integer`, example: `5000`. The first \u003cnumPreAuxModelSamples\u003e will have their assignment likelihoods and contributions to the transcript abundances computed without applying any auxiliary models. The purpose of ignoring the auxiliary models for the first \u003cnum_pre_aux_model_samples\u003e observations is to avoid applying these models before their parameters have been learned sufficiently well.\n" + + } + + + , + "useEM": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use the traditional EM algorithm for optimization in the batch passes", + "help_text": "Type: `boolean_true`, default: `false`. Use the traditional EM algorithm for optimization in the batch passes.\n" + , + "default": "False" + } + + + , + "useVBOpt": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use the Variational Bayesian EM [default]\n", + "help_text": "Type: `boolean_true`, default: `false`. Use the Variational Bayesian EM [default]\n" + , + "default": "False" + } + + + , + "range_factorization_bins": { + "type": + "integer", + "description": "Type: `integer`, example: `4`. Factorizes the likelihood used in quantification by adopting a new notion of equivalence classes based on the conditional probabilities with which fragments are generated from different transcripts", + "help_text": "Type: `integer`, example: `4`. Factorizes the likelihood used in quantification by adopting a new notion of equivalence classes based on the conditional probabilities with which fragments are generated from different transcripts. This is a more fine-grained factorization than the normal rich equivalence classes. The default value (4) corresponds to the default used in Zakeri et al. 2017 (doi: 10.1093/bioinformatics/btx262), and larger values imply a more fine-grained factorization. If range factorization is enabled, a common value to select for this parameter is 4. A value of 0 signifies the use of basic rich equivalence classes.\n" + + } + + + , + "num_Gibbs_samples": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Number of Gibbs sampling rounds to perform", + "help_text": "Type: `integer`, example: `0`. Number of Gibbs sampling rounds to perform.\n" + + } + + + , + "no_Gamma_draw": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. This switch will disable drawing transcript fractions from a Gamma distribution during Gibbs sampling", + "help_text": "Type: `boolean_true`, default: `false`. This switch will disable drawing transcript fractions from a Gamma distribution during Gibbs sampling. In this case the sampler does not account for shot-noise, but only assignment ambiguity\n" + , + "default": "False" + } + + + , + "num_bootstraps": { + "type": + "integer", + "description": "Type: `integer`, example: `0`. Number of bootstrap samples to generate", + "help_text": "Type: `integer`, example: `0`. Number of bootstrap samples to generate. Note: This is mutually exclusive with Gibbs sampling.\n" + + } + + + , + "bootstrap_reproject": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. This switch will learn the parameter distribution from the bootstrapped counts for each sample, but will reproject those parameters onto the original equivalence class counts", + "help_text": "Type: `boolean_true`, default: `false`. This switch will learn the parameter distribution from the bootstrapped counts for each sample, but will reproject those parameters onto the original equivalence class counts.\n" + , + "default": "False" + } + + + , + "thinning_factor": { + "type": + "integer", + "description": "Type: `integer`, example: `16`. Number of steps to discard for every sample kept from the Gibbs chain", + "help_text": "Type: `integer`, example: `16`. Number of steps to discard for every sample kept from the Gibbs chain. The larger this number, the less chance that subsequent samples are auto-correlated, but the slower sampling becomes.\n" + + } + + + , + "quiet": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Be quiet while doing quantification (don\u0027t write informative output to the console unless something goes wrong)", + "help_text": "Type: `boolean_true`, default: `false`. Be quiet while doing quantification (don\u0027t write informative output to the console unless something goes wrong).\n" + , + "default": "False" + } + + + , + "per_transcript_prior": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. The prior (either the default or the argument provided via --vb_prior) will be interpreted as a transcript-level prior (i", + "help_text": "Type: `boolean_true`, default: `false`. The prior (either the default or the argument provided via --vb_prior) will be interpreted as a transcript-level prior (i.e. each transcript will be given a prior read count of this value)\n" + , + "default": "False" + } + + + , + "per_nucleotide_prior": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. The prior (either the default or the argument provided via --vb_prior) will be interpreted as a nucleotide-level prior (i", + "help_text": "Type: `boolean_true`, default: `false`. The prior (either the default or the argument provided via --vb_prior) will be interpreted as a nucleotide-level prior (i.e. each nucleotide will be given a prior read count of this value)\n" + , + "default": "False" + } + + + , + "sig_digits": { + "type": + "integer", + "description": "Type: `integer`, example: `3`. The number of significant digits to write when outputting the EffectiveLength and NumReads columns\n", + "help_text": "Type: `integer`, example: `3`. The number of significant digits to write when outputting the EffectiveLength and NumReads columns\n" + + } + + + , + "vb_prior": { + "type": + "number", + "description": "Type: `double`, example: `0.01`. The prior that will be used in the VBEM algorithm", + "help_text": "Type: `double`, example: `0.01`. The prior that will be used in the VBEM algorithm. This is interpreted as a per-transcript prior, unless the --per_nucleotide_prior flag is also given. If the --per_nucleotide_prior flag is given, this is used as a nucleotide-level prior. If the default is used, it will be divided by 1000 before being used as a nucleotide-level prior, i.e. the default per-nucleotide prior will be 1e-5.\n" + + } + + + , + "write_orphan_links": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Write the transcripts that are linked by orphaned reads", + "help_text": "Type: `boolean_true`, default: `false`. Write the transcripts that are linked by orphaned reads.\n" + , + "default": "False" + } + + + , + "write_unmapped_names": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Write the names of un-mapped reads to the file unmapped_names", + "help_text": "Type: `boolean_true`, default: `false`. Write the names of un-mapped reads to the file unmapped_names.txt in the auxiliary directory.\n" + , + "default": "False" + } + + +} +}, + + + "alignment-specific options" : { + "title": "Alignment-specific options", + "type": "object", + "description": "No description", + "properties": { + + + "no_error_model": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Turn off the alignment error model, which takes into account the the observed frequency of different types of mismatches / indels when computing the likelihood of a given alignment", + "help_text": "Type: `boolean_true`, default: `false`. Turn off the alignment error model, which takes into account the the observed frequency of different types of mismatches / indels when computing the likelihood of a given alignment. Turning this off can speed up alignment-based salmon, but can harm quantification accuracy.\n" + , + "default": "False" + } + + + , + "num_error_bins": { + "type": + "integer", + "description": "Type: `integer`, example: `6`. The number of bins into which to divide each read when learning and applying the error model", + "help_text": "Type: `integer`, example: `6`. The number of bins into which to divide each read when learning and applying the error model. For example, a value of 10 would mean that effectively, a separate error model is leared and applied to each 10th of the read, while a value of 3 would mean that a separate error model is applied to the read beginning (first third), middle (second third) and end (final third).\n" + + } + + + , + "sample_out": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Write a \"postSample", + "help_text": "Type: `boolean_true`, default: `false`. Write a \"postSample.bam\" file in the output directory that will sample the input alignments according to the estimated transcript abundances. If you\u0027re going to perform downstream analysis of the alignments with tools which don\u0027t, themselves, take fragment assignment ambiguity into account, you should use this output.\n" + , + "default": "False" + } + + + , + "sample_unaligned": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. In addition to sampling the aligned reads, also write the un-aligned reads to \"postSample", + "help_text": "Type: `boolean_true`, default: `false`. In addition to sampling the aligned reads, also write the un-aligned reads to \"postSample.bam\".\n" + , + "default": "False" + } + + + , + "gencode": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. This flag will expect the input transcript fasta to be in GENCODE format, and will split the transcript name at the first \u0027|\u0027 character", + "help_text": "Type: `boolean_true`, default: `false`. This flag will expect the input transcript fasta to be in GENCODE format, and will split the transcript name at the first \u0027|\u0027 character. These reduced names will be used in the output and when looking for these transcripts in a gene to transcript GTF.\n" + , + "default": "False" + } + + + , + "mapping_cache_memory_limit": { + "type": + "integer", + "description": "Type: `integer`, example: `2000000`. If the file contained fewer than this many mapped reads, then just keep the data in memory for subsequent rounds of inference", + "help_text": "Type: `integer`, example: `2000000`. If the file contained fewer than this many mapped reads, then just keep the data in memory for subsequent rounds of inference. Obviously, this value should not be too large if you wish to keep a low memory usage, but setting it large enough to accommodate all of the mapped read can substantially speed up inference on \"small\" files that contain only a few million reads.\n" + + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/common input options" + }, + + { + "$ref": "#/definitions/mapping input options" + }, + + { + "$ref": "#/definitions/alignment input options" + }, + + { + "$ref": "#/definitions/output" + }, + + { + "$ref": "#/definitions/basic options" + }, + + { + "$ref": "#/definitions/options specific to mapping mode" + }, + + { + "$ref": "#/definitions/advance options" + }, + + { + "$ref": "#/definitions/alignment-specific options" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/samtools/samtools_collate/.config.vsh.yaml b/target/nextflow/samtools/samtools_collate/.config.vsh.yaml new file mode 100644 index 00000000..e411d54b --- /dev/null +++ b/target/nextflow/samtools/samtools_collate/.config.vsh.yaml @@ -0,0 +1,276 @@ +name: "samtools_collate" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "The input BAM file." + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reference" + description: "Reference sequence FASTA FILE." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "The output filename." + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Options" + arguments: + - type: "boolean_true" + name: "--uncompressed" + alternatives: + - "-u" + description: "Output uncompressed BAM." + info: null + direction: "input" + - type: "boolean_true" + name: "--fast" + alternatives: + - "-f" + description: "Fast mode, only primary alignments." + info: null + direction: "input" + - type: "integer" + name: "--working_reads" + alternatives: + - "-r" + description: "Working reads stored (for use with -f)." + info: null + default: + - 10000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--compression" + alternatives: + - "-l" + description: "Compression level." + info: null + default: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--nb_tmp_files" + alternatives: + - "-n" + description: "Number of temporary files." + info: null + default: + - 64 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--tmp_prefix" + alternatives: + - "-T" + description: "Write temporary files to PREFIX.nnnn.bam." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_pg" + description: "Do not add a PG line." + info: null + direction: "input" + - type: "string" + name: "--input_fmt_option" + description: "Specify a single input file format option in the form of OPTION\ + \ or OPTION=VALUE." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt" + description: "Specify output format (SAM, BAM, CRAM)." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt_option" + description: "Specify a single output file format option in the form of OPTION\ + \ or OPTION=VALUE." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Shuffles and groups reads in SAM/BAM/CRAM files together by their names." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "collate" +- "counts" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-icollate.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_collate/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/samtools/samtools_collate" + executable: "target/nextflow/samtools/samtools_collate/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/samtools/samtools_collate/main.nf b/target/nextflow/samtools/samtools_collate/main.nf new file mode 100644 index 00000000..c0357eca --- /dev/null +++ b/target/nextflow/samtools/samtools_collate/main.nf @@ -0,0 +1,3666 @@ +// samtools_collate main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "samtools_collate", + "namespace" : "samtools", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "The input BAM file.", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--reference", + "description" : "Reference sequence FASTA FILE.", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "alternatives" : [ + "-o" + ], + "description" : "The output filename.", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Options", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--uncompressed", + "alternatives" : [ + "-u" + ], + "description" : "Output uncompressed BAM.", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--fast", + "alternatives" : [ + "-f" + ], + "description" : "Fast mode, only primary alignments.", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--working_reads", + "alternatives" : [ + "-r" + ], + "description" : "Working reads stored (for use with -f).", + "default" : [ + 10000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--compression", + "alternatives" : [ + "-l" + ], + "description" : "Compression level.", + "default" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--nb_tmp_files", + "alternatives" : [ + "-n" + ], + "description" : "Number of temporary files.", + "default" : [ + 64 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--tmp_prefix", + "alternatives" : [ + "-T" + ], + "description" : "Write temporary files to PREFIX.nnnn.bam.", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--no_pg", + "description" : "Do not add a PG line.", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--input_fmt_option", + "description" : "Specify a single input file format option in the form of OPTION or OPTION=VALUE.", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--output_fmt", + "description" : "Specify output format (SAM, BAM, CRAM).", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--output_fmt_option", + "description" : "Specify a single output file format option in the form of OPTION or OPTION=VALUE.", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Shuffles and groups reads in SAM/BAM/CRAM files together by their names.", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "collate", + "counts", + "bam", + "sam", + "cram" + ], + "license" : "MIT/Expat", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btp352", + "10.1093/gigascience/giab008" + ] + }, + "links" : { + "repository" : "https://github.com/samtools/samtools", + "homepage" : "https://www.htslib.org/", + "documentation" : "https://www.htslib.org/doc/samtools-icollate.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\\\nsed 's#Using ##;s# \\\\([0-9\\\\.]*\\\\)$#: \\\\1#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/samtools/samtools_collate/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/samtools/samtools_collate", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_REFERENCE+x} ]; then echo "${VIASH_PAR_REFERENCE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_reference='&'#" ; else echo "# par_reference="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_UNCOMPRESSED+x} ]; then echo "${VIASH_PAR_UNCOMPRESSED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_uncompressed='&'#" ; else echo "# par_uncompressed="; fi ) +$( if [ ! -z ${VIASH_PAR_FAST+x} ]; then echo "${VIASH_PAR_FAST}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fast='&'#" ; else echo "# par_fast="; fi ) +$( if [ ! -z ${VIASH_PAR_WORKING_READS+x} ]; then echo "${VIASH_PAR_WORKING_READS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_working_reads='&'#" ; else echo "# par_working_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPRESSION+x} ]; then echo "${VIASH_PAR_COMPRESSION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_compression='&'#" ; else echo "# par_compression="; fi ) +$( if [ ! -z ${VIASH_PAR_NB_TMP_FILES+x} ]; then echo "${VIASH_PAR_NB_TMP_FILES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_nb_tmp_files='&'#" ; else echo "# par_nb_tmp_files="; fi ) +$( if [ ! -z ${VIASH_PAR_TMP_PREFIX+x} ]; then echo "${VIASH_PAR_TMP_PREFIX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_tmp_prefix='&'#" ; else echo "# par_tmp_prefix="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_PG+x} ]; then echo "${VIASH_PAR_NO_PG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_pg='&'#" ; else echo "# par_no_pg="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_INPUT_FMT_OPTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input_fmt_option='&'#" ; else echo "# par_input_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_fmt='&'#" ; else echo "# par_output_fmt="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT_OPTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_fmt_option='&'#" ; else echo "# par_output_fmt_option="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\\$par_uncompressed" == "false" ]] && unset par_uncompressed +[[ "\\$par_fast" == "false" ]] && unset par_fast +[[ "\\$par_no_pg" == "false" ]] && unset par_no_pg + +samtools collate \\\\ + "\\$par_input" \\\\ + \\${par_output:+-o "\\$par_output"} \\\\ + \\${par_reference:+-T "\\$par_reference"} \\\\ + \\${par_uncompressed:+-u} \\\\ + \\${par_fast:+-f} \\\\ + \\${par_working_reads:+-r "\\$par_working_reads"} \\\\ + \\${par_compression:+-l "\\$par_compression"} \\\\ + \\${par_nb_tmp_files:+-n "\\$par_nb_tmp_files"} \\\\ + \\${par_tmp_prefix:+-T "\\$par_tmp_prefix"} \\\\ + \\${par_no_pg:+-P} \\\\ + \\${par_input_fmt_option:+-O "\\$par_input_fmt_option"} \\\\ + \\${par_output_fmt:+-O "\\$par_output_fmt"} \\\\ + \\${par_output_fmt_option:+-O "\\$par_output_fmt_option"} + +exit 0 +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/samtools/samtools_collate", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/samtools/samtools_collate/nextflow.config b/target/nextflow/samtools/samtools_collate/nextflow.config new file mode 100644 index 00000000..f20dad8b --- /dev/null +++ b/target/nextflow/samtools/samtools_collate/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'samtools/samtools_collate' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Shuffles and groups reads in SAM/BAM/CRAM files together by their names.' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/samtools/samtools_collate/nextflow_schema.json b/target/nextflow/samtools/samtools_collate/nextflow_schema.json new file mode 100644 index 00000000..677a7322 --- /dev/null +++ b/target/nextflow/samtools/samtools_collate/nextflow_schema.json @@ -0,0 +1,225 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "samtools_collate", +"description": "Shuffles and groups reads in SAM/BAM/CRAM files together by their names.", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`, required. The input BAM file", + "help_text": "Type: `file`, required. The input BAM file." + + } + + + , + "reference": { + "type": + "string", + "description": "Type: `file`. Reference sequence FASTA FILE", + "help_text": "Type: `file`. Reference sequence FASTA FILE." + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output.output`. The output filename", + "help_text": "Type: `file`, required, default: `$id.$key.output.output`. The output filename." + , + "default": "$id.$key.output.output" + } + + +} +}, + + + "options" : { + "title": "Options", + "type": "object", + "description": "No description", + "properties": { + + + "uncompressed": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output uncompressed BAM", + "help_text": "Type: `boolean_true`, default: `false`. Output uncompressed BAM." + , + "default": "False" + } + + + , + "fast": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Fast mode, only primary alignments", + "help_text": "Type: `boolean_true`, default: `false`. Fast mode, only primary alignments." + , + "default": "False" + } + + + , + "working_reads": { + "type": + "integer", + "description": "Type: `integer`, default: `10000`. Working reads stored (for use with -f)", + "help_text": "Type: `integer`, default: `10000`. Working reads stored (for use with -f)." + , + "default": "10000" + } + + + , + "compression": { + "type": + "integer", + "description": "Type: `integer`, default: `1`. Compression level", + "help_text": "Type: `integer`, default: `1`. Compression level." + , + "default": "1" + } + + + , + "nb_tmp_files": { + "type": + "integer", + "description": "Type: `integer`, default: `64`. Number of temporary files", + "help_text": "Type: `integer`, default: `64`. Number of temporary files." + , + "default": "64" + } + + + , + "tmp_prefix": { + "type": + "string", + "description": "Type: `string`. Write temporary files to PREFIX", + "help_text": "Type: `string`. Write temporary files to PREFIX.nnnn.bam." + + } + + + , + "no_pg": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Do not add a PG line", + "help_text": "Type: `boolean_true`, default: `false`. Do not add a PG line." + , + "default": "False" + } + + + , + "input_fmt_option": { + "type": + "string", + "description": "Type: `string`. Specify a single input file format option in the form of OPTION or OPTION=VALUE", + "help_text": "Type: `string`. Specify a single input file format option in the form of OPTION or OPTION=VALUE." + + } + + + , + "output_fmt": { + "type": + "string", + "description": "Type: `string`. Specify output format (SAM, BAM, CRAM)", + "help_text": "Type: `string`. Specify output format (SAM, BAM, CRAM)." + + } + + + , + "output_fmt_option": { + "type": + "string", + "description": "Type: `string`. Specify a single output file format option in the form of OPTION or OPTION=VALUE", + "help_text": "Type: `string`. Specify a single output file format option in the form of OPTION or OPTION=VALUE." + + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/options" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/samtools/samtools_faidx/.config.vsh.yaml b/target/nextflow/samtools/samtools_faidx/.config.vsh.yaml new file mode 100644 index 00000000..c1d56b6c --- /dev/null +++ b/target/nextflow/samtools/samtools_faidx/.config.vsh.yaml @@ -0,0 +1,255 @@ +name: "samtools_faidx" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "FASTA input file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--length" + alternatives: + - "-n" + description: "Length for FASTA sequence line wrapping. If zero, this means do\ + \ not\nline wrap. Defaults to the line length in the input file.\n" + info: null + default: + - 60 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--region_file" + alternatives: + - "-r" + description: "File of regions. Format is chr:from-to. One per line.\nMust be used\ + \ with --output to avoid sending output to stdout.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Options" + arguments: + - type: "boolean_true" + name: "--continue" + description: "Continue working if a non-existent region is requested.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--reverse_complement" + alternatives: + - "-i" + description: "Reverse complement sequences.\n" + info: null + direction: "input" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Write output to file.\n" + info: null + example: + - "output.fasta" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--mark_strand" + description: "Add strand indicator to sequence name. Options are:\n[ rc, no, sign,\ + \ custom,, ]\n" + info: null + default: + - "rc" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--fai_idx" + description: "Read/Write to specified index file (default file.fa.fai).\n" + info: null + example: + - "file.fa.fai" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--gzi_idx" + description: "Read/Write to specified compressed file index (used with .gz files,\ + \ default file.fa.gz.gzi).\n" + info: null + example: + - "file.fa.gz.gzi" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--fastq" + description: "Read FASTQ files and output extracted sequences in FASTQ format.\ + \ Same as using samtools fqidx.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Indexes FASTA files to enable random access to fasta and fastq files." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "idex" +- "fasta" +- "faidx" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-faidx.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_faidx/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/samtools/samtools_faidx" + executable: "target/nextflow/samtools/samtools_faidx/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/samtools/samtools_faidx/main.nf b/target/nextflow/samtools/samtools_faidx/main.nf new file mode 100644 index 00000000..841a3afb --- /dev/null +++ b/target/nextflow/samtools/samtools_faidx/main.nf @@ -0,0 +1,3632 @@ +// samtools_faidx main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "samtools_faidx", + "namespace" : "samtools", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "FASTA input file.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--length", + "alternatives" : [ + "-n" + ], + "description" : "Length for FASTA sequence line wrapping. If zero, this means do not\nline wrap. Defaults to the line length in the input file.\n", + "default" : [ + 60 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--region_file", + "alternatives" : [ + "-r" + ], + "description" : "File of regions. Format is chr:from-to. One per line.\nMust be used with --output to avoid sending output to stdout.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Options", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--continue", + "description" : "Continue working if a non-existent region is requested.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--reverse_complement", + "alternatives" : [ + "-i" + ], + "description" : "Reverse complement sequences.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "alternatives" : [ + "-o" + ], + "description" : "Write output to file.\n", + "example" : [ + "output.fasta" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--mark_strand", + "description" : "Add strand indicator to sequence name. Options are:\n[ rc, no, sign, custom,, ]\n", + "default" : [ + "rc" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--fai_idx", + "description" : "Read/Write to specified index file (default file.fa.fai).\n", + "example" : [ + "file.fa.fai" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--gzi_idx", + "description" : "Read/Write to specified compressed file index (used with .gz files, default file.fa.gz.gzi).\n", + "example" : [ + "file.fa.gz.gzi" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--fastq", + "description" : "Read FASTQ files and output extracted sequences in FASTQ format. Same as using samtools fqidx.\n", + "direction" : "input" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Indexes FASTA files to enable random access to fasta and fastq files.", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "idex", + "fasta", + "faidx" + ], + "license" : "MIT/Expat", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btp352", + "10.1093/gigascience/giab008" + ] + }, + "links" : { + "repository" : "https://github.com/samtools/samtools", + "homepage" : "https://www.htslib.org/", + "documentation" : "https://www.htslib.org/doc/samtools-faidx.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\\\nsed 's#Using ##;s# \\\\([0-9\\\\.]*\\\\)$#: \\\\1#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/samtools/samtools_faidx/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/samtools/samtools_faidx", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_LENGTH+x} ]; then echo "${VIASH_PAR_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_length='&'#" ; else echo "# par_length="; fi ) +$( if [ ! -z ${VIASH_PAR_REGION_FILE+x} ]; then echo "${VIASH_PAR_REGION_FILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_region_file='&'#" ; else echo "# par_region_file="; fi ) +$( if [ ! -z ${VIASH_PAR_CONTINUE+x} ]; then echo "${VIASH_PAR_CONTINUE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_continue='&'#" ; else echo "# par_continue="; fi ) +$( if [ ! -z ${VIASH_PAR_REVERSE_COMPLEMENT+x} ]; then echo "${VIASH_PAR_REVERSE_COMPLEMENT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_reverse_complement='&'#" ; else echo "# par_reverse_complement="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_MARK_STRAND+x} ]; then echo "${VIASH_PAR_MARK_STRAND}" | sed "s#'#'\\"'\\"'#g;s#.*#par_mark_strand='&'#" ; else echo "# par_mark_strand="; fi ) +$( if [ ! -z ${VIASH_PAR_FAI_IDX+x} ]; then echo "${VIASH_PAR_FAI_IDX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fai_idx='&'#" ; else echo "# par_fai_idx="; fi ) +$( if [ ! -z ${VIASH_PAR_GZI_IDX+x} ]; then echo "${VIASH_PAR_GZI_IDX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_gzi_idx='&'#" ; else echo "# par_gzi_idx="; fi ) +$( if [ ! -z ${VIASH_PAR_FASTQ+x} ]; then echo "${VIASH_PAR_FASTQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fastq='&'#" ; else echo "# par_fastq="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\\$par_continue" == "false" ]] && unset par_continue +[[ "\\$par_reverse_complement" == "false" ]] && unset par_reverse_complement +[[ "\\$par_fastq" == "false" ]] && unset par_fastq + +samtools faidx \\\\ + "\\$par_input" \\\\ + \\${par_output:+-o "\\$par_output"} \\\\ + \\${par_length:+-n "\\$par_length"} \\\\ + \\${par_continue:+-c} \\\\ + \\${par_region_file:+-r "\\$par_region_file"} \\\\ + \\${par_reverse_complement:+-r} \\\\ + \\${par_mark_strand:+--mark-strand "\\$par_mark_strand"} \\\\ + \\${par_fai_idx:+--fai-idx "\\$par_fai_idx"} \\\\ + \\${par_gzi_idx:+--gzi-idx "\\$par_gzi_idx"} \\\\ + \\${par_fastq:+-f} + +exit 0 +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/samtools/samtools_faidx", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/samtools/samtools_faidx/nextflow.config b/target/nextflow/samtools/samtools_faidx/nextflow.config new file mode 100644 index 00000000..c1ef7de8 --- /dev/null +++ b/target/nextflow/samtools/samtools_faidx/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'samtools/samtools_faidx' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Indexes FASTA files to enable random access to fasta and fastq files.' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/samtools/samtools_faidx/nextflow_schema.json b/target/nextflow/samtools/samtools_faidx/nextflow_schema.json new file mode 100644 index 00000000..5e00f832 --- /dev/null +++ b/target/nextflow/samtools/samtools_faidx/nextflow_schema.json @@ -0,0 +1,196 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "samtools_faidx", +"description": "Indexes FASTA files to enable random access to fasta and fastq files.", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`. FASTA input file", + "help_text": "Type: `file`. FASTA input file.\n" + + } + + + , + "length": { + "type": + "integer", + "description": "Type: `integer`, default: `60`. Length for FASTA sequence line wrapping", + "help_text": "Type: `integer`, default: `60`. Length for FASTA sequence line wrapping. If zero, this means do not\nline wrap. Defaults to the line length in the input file.\n" + , + "default": "60" + } + + + , + "region_file": { + "type": + "string", + "description": "Type: `file`. File of regions", + "help_text": "Type: `file`. File of regions. Format is chr:from-to. One per line.\nMust be used with --output to avoid sending output to stdout.\n" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output.fasta`, example: `output.fasta`. Write output to file", + "help_text": "Type: `file`, required, default: `$id.$key.output.fasta`, example: `output.fasta`. Write output to file.\n" + , + "default": "$id.$key.output.fasta" + } + + + , + "mark_strand": { + "type": + "string", + "description": "Type: `string`, default: `rc`. Add strand indicator to sequence name", + "help_text": "Type: `string`, default: `rc`. Add strand indicator to sequence name. Options are:\n[ rc, no, sign, custom,\u003cpos\u003e,\u003cneg\u003e ]\n" + , + "default": "rc" + } + + + , + "fai_idx": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.fai_idx.fai`, example: `file.fa.fai`. Read/Write to specified index file (default file", + "help_text": "Type: `file`, default: `$id.$key.fai_idx.fai`, example: `file.fa.fai`. Read/Write to specified index file (default file.fa.fai).\n" + , + "default": "$id.$key.fai_idx.fai" + } + + + , + "gzi_idx": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.gzi_idx.gzi`, example: `file.fa.gz.gzi`. Read/Write to specified compressed file index (used with ", + "help_text": "Type: `file`, default: `$id.$key.gzi_idx.gzi`, example: `file.fa.gz.gzi`. Read/Write to specified compressed file index (used with .gz files, default file.fa.gz.gzi).\n" + , + "default": "$id.$key.gzi_idx.gzi" + } + + + , + "fastq": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Read FASTQ files and output extracted sequences in FASTQ format", + "help_text": "Type: `boolean_true`, default: `false`. Read FASTQ files and output extracted sequences in FASTQ format. Same as using samtools fqidx.\n" + , + "default": "False" + } + + +} +}, + + + "options" : { + "title": "Options", + "type": "object", + "description": "No description", + "properties": { + + + "continue": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Continue working if a non-existent region is requested", + "help_text": "Type: `boolean_true`, default: `false`. Continue working if a non-existent region is requested.\n" + , + "default": "False" + } + + + , + "reverse_complement": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Reverse complement sequences", + "help_text": "Type: `boolean_true`, default: `false`. Reverse complement sequences.\n" + , + "default": "False" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/options" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/samtools/samtools_fastq/.config.vsh.yaml b/target/nextflow/samtools/samtools_fastq/.config.vsh.yaml new file mode 100644 index 00000000..7338fd1f --- /dev/null +++ b/target/nextflow/samtools/samtools_fastq/.config.vsh.yaml @@ -0,0 +1,443 @@ +name: "samtools_fastq" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "input SAM/BAM/CRAM file" + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + description: "output FASTQ file" + info: null + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Options" + arguments: + - type: "boolean_true" + name: "--no_suffix" + alternatives: + - "-n" + description: "By default, either '/1' or '/2' is added to the end of read names\ + \ where the corresponding \nREAD1 or READ2 FLAG bit is set. Using -n causes\ + \ read names to be left as they are.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--suffix" + alternatives: + - "-N" + description: "Always add either '/1' or '/2' to the end of read names even when\ + \ put into different files.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--use_oq" + alternatives: + - "-O" + description: "Use quality values from OQ tags in preference to standard quality\ + \ string if available.\n" + info: null + direction: "input" + - type: "file" + name: "--singleton" + alternatives: + - "-s" + description: "write singleton reads to FILE." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--copy_tags" + alternatives: + - "-t" + description: "Copy RG, BC and QT tags to the FASTQ header line, if they exist.\n" + info: null + direction: "input" + - type: "string" + name: "--copy_tags_list" + alternatives: + - "-T" + description: "Specify a comma-separated list of tags to copy to the FASTQ header\ + \ line, if they exist. \nTAGLIST can be blank or * to indicate all tags should\ + \ be copied to the output. If using *, \nbe careful to quote it to avoid unwanted\ + \ shell expansion.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--read1" + alternatives: + - "-1" + description: "Write reads with the READ1 FLAG set (and READ2 not set) to FILE\ + \ instead of outputting them. \nIf the -s option is used, only paired reads\ + \ will be written to this file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--read2" + alternatives: + - "-2" + description: "Write reads with the READ2 FLAG set (and READ1 not set) to FILE\ + \ instead of outputting them. \nIf the -s option is used, only paired reads\ + \ will be written to this file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--output_reads" + alternatives: + - "-o" + description: "Write reads with either READ1 FLAG or READ2 flag set to FILE instead\ + \ of outputting them to stdout. \nThis is equivalent to -1 FILE -2 FILE.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--output_reads_both" + alternatives: + - "0" + description: "Write reads where the READ1 and READ2 FLAG bits set are either both\ + \ set or both unset to FILE \ninstead of outputting them.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--filter_flags" + alternatives: + - "-f" + description: "Only output alignments with all bits set in INT present in the FLAG\ + \ field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/)\ + \ or in octal by beginning with `0' \n(i.e. /^0[0-7]+/).\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--excl_flags" + alternatives: + - "-F" + description: "Do not output alignments with any bits set in INT present in the\ + \ FLAG field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/)\ + \ or in octal by beginning with `0' \n(i.e. /^0[0-7]+/). This defaults to 0x900\ + \ representing filtering of secondary and \nsupplementary alignments.\n" + info: null + default: + - "2304" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--incl_flags" + alternatives: + - "--rf" + description: "Only output alignments with any bits set in INT present in the FLAG\ + \ field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/),\ + \ in octal by beginning with `0' \n(i.e. /^0[0-7]+/), as a decimal number not\ + \ beginning with '0' or as a comma-separated list of \nflag names.\n" + info: null + default: + - "0" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--excl_flags_all" + alternatives: + - "-G" + description: "Only EXCLUDE reads with all of the bits set in INT present in the\ + \ FLAG field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/)\ + \ or in octal by beginning with `0' \n(i.e. /^0[0-7]+/).\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--aux_tag" + alternatives: + - "-d" + description: "Only output alignments containing an auxiliary tag matching both\ + \ TAG and VAL. If VAL is omitted \nthen any value is accepted. The tag types\ + \ supported are i, f, Z, A and H. \"B\" arrays are not \nsupported. This is\ + \ comparable to the method used in samtools view --tag. The option may be specified\ + \ \nmultiple times and is equivalent to using the --aux_tag_file option.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--aux_tag_file" + alternatives: + - "-D" + description: "Only output alignments containing an auxiliary tag matching TAG\ + \ and having a value listed in FILE. \nThe format of the file is one line per\ + \ value. This is equivalent to specifying --aux_tag multiple times.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--casava" + alternatives: + - "-i" + description: "add Illumina Casava 1.8 format entry to header (eg 1:N:0:ATCACG)" + info: null + direction: "input" + - type: "integer" + name: "--compression" + alternatives: + - "-c" + description: "set compression level when writing gz or bgzf fastq files." + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--index1" + alternatives: + - "--i1" + description: "write first index reads to FILE." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--index2" + alternatives: + - "--i2" + description: "write second index reads to FILE." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--barcode_tag" + description: "Auxiliary tag to find index reads in." + info: null + default: + - "BC" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--quality_tag" + description: "Auxiliary tag to find index quality in." + info: null + default: + - "QT" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--index_format" + description: "string to describe how to parse the barcode and quality tags. For\ + \ example:\n[i14i8]: the first 14 characters are index 1, the next 8 characters\ + \ are index 2.\n[n8i14]: ignore the first 8 characters, and use the next 14\ + \ characters for index 1.\nIf the tag contains a separator, then the numeric\ + \ part can be replaced with '*' to mean \n'read until the separator or end of\ + \ tag', for example: [n*i*].\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Converts a SAM, BAM or CRAM to FASTQ format." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "fastq" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-fastq.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_fastq/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/samtools/samtools_fastq" + executable: "target/nextflow/samtools/samtools_fastq/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/samtools/samtools_fastq/main.nf b/target/nextflow/samtools/samtools_fastq/main.nf new file mode 100644 index 00000000..488020e2 --- /dev/null +++ b/target/nextflow/samtools/samtools_fastq/main.nf @@ -0,0 +1,3854 @@ +// samtools_fastq main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "samtools_fastq", + "namespace" : "samtools", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "input SAM/BAM/CRAM file", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "description" : "output FASTQ file", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Options", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--no_suffix", + "alternatives" : [ + "-n" + ], + "description" : "By default, either '/1' or '/2' is added to the end of read names where the corresponding \nREAD1 or READ2 FLAG bit is set. Using -n causes read names to be left as they are.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--suffix", + "alternatives" : [ + "-N" + ], + "description" : "Always add either '/1' or '/2' to the end of read names even when put into different files.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--use_oq", + "alternatives" : [ + "-O" + ], + "description" : "Use quality values from OQ tags in preference to standard quality string if available.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--singleton", + "alternatives" : [ + "-s" + ], + "description" : "write singleton reads to FILE.", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--copy_tags", + "alternatives" : [ + "-t" + ], + "description" : "Copy RG, BC and QT tags to the FASTQ header line, if they exist.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--copy_tags_list", + "alternatives" : [ + "-T" + ], + "description" : "Specify a comma-separated list of tags to copy to the FASTQ header line, if they exist. \nTAGLIST can be blank or * to indicate all tags should be copied to the output. If using *, \nbe careful to quote it to avoid unwanted shell expansion.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--read1", + "alternatives" : [ + "-1" + ], + "description" : "Write reads with the READ1 FLAG set (and READ2 not set) to FILE instead of outputting them. \nIf the -s option is used, only paired reads will be written to this file.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--read2", + "alternatives" : [ + "-2" + ], + "description" : "Write reads with the READ2 FLAG set (and READ1 not set) to FILE instead of outputting them. \nIf the -s option is used, only paired reads will be written to this file.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--output_reads", + "alternatives" : [ + "-o" + ], + "description" : "Write reads with either READ1 FLAG or READ2 flag set to FILE instead of outputting them to stdout. \nThis is equivalent to -1 FILE -2 FILE.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--output_reads_both", + "alternatives" : [ + "0" + ], + "description" : "Write reads where the READ1 and READ2 FLAG bits set are either both set or both unset to FILE \ninstead of outputting them.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--filter_flags", + "alternatives" : [ + "-f" + ], + "description" : "Only output alignments with all bits set in INT present in the FLAG field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/) or in octal by beginning with `0' \n(i.e. /^0[0-7]+/).\n", + "default" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--excl_flags", + "alternatives" : [ + "-F" + ], + "description" : "Do not output alignments with any bits set in INT present in the FLAG field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/) or in octal by beginning with `0' \n(i.e. /^0[0-7]+/). This defaults to 0x900 representing filtering of secondary and \nsupplementary alignments.\n", + "default" : [ + "2304" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--incl_flags", + "alternatives" : [ + "--rf" + ], + "description" : "Only output alignments with any bits set in INT present in the FLAG field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0' \n(i.e. /^0[0-7]+/), as a decimal number not beginning with '0' or as a comma-separated list of \nflag names.\n", + "default" : [ + "0" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--excl_flags_all", + "alternatives" : [ + "-G" + ], + "description" : "Only EXCLUDE reads with all of the bits set in INT present in the FLAG field. INT can be specified \nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/) or in octal by beginning with `0' \n(i.e. /^0[0-7]+/).\n", + "default" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--aux_tag", + "alternatives" : [ + "-d" + ], + "description" : "Only output alignments containing an auxiliary tag matching both TAG and VAL. If VAL is omitted \nthen any value is accepted. The tag types supported are i, f, Z, A and H. \\"B\\" arrays are not \nsupported. This is comparable to the method used in samtools view --tag. The option may be specified \nmultiple times and is equivalent to using the --aux_tag_file option.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--aux_tag_file", + "alternatives" : [ + "-D" + ], + "description" : "Only output alignments containing an auxiliary tag matching TAG and having a value listed in FILE. \nThe format of the file is one line per value. This is equivalent to specifying --aux_tag multiple times.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--casava", + "alternatives" : [ + "-i" + ], + "description" : "add Illumina Casava 1.8 format entry to header (eg 1:N:0:ATCACG)", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--compression", + "alternatives" : [ + "-c" + ], + "description" : "set compression level when writing gz or bgzf fastq files.", + "default" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--index1", + "alternatives" : [ + "--i1" + ], + "description" : "write first index reads to FILE.", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--index2", + "alternatives" : [ + "--i2" + ], + "description" : "write second index reads to FILE.", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--barcode_tag", + "description" : "Auxiliary tag to find index reads in.", + "default" : [ + "BC" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--quality_tag", + "description" : "Auxiliary tag to find index quality in.", + "default" : [ + "QT" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--index_format", + "description" : "string to describe how to parse the barcode and quality tags. For example:\n[i14i8]: the first 14 characters are index 1, the next 8 characters are index 2.\n[n8i14]: ignore the first 8 characters, and use the next 14 characters for index 1.\nIf the tag contains a separator, then the numeric part can be replaced with '*' to mean \n'read until the separator or end of tag', for example: [n*i*].\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Converts a SAM, BAM or CRAM to FASTQ format.", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "fastq", + "bam", + "sam", + "cram" + ], + "license" : "MIT/Expat", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btp352", + "10.1093/gigascience/giab008" + ] + }, + "links" : { + "repository" : "https://github.com/samtools/samtools", + "homepage" : "https://www.htslib.org/", + "documentation" : "https://www.htslib.org/doc/samtools-fastq.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\\\nsed 's#Using ##;s# \\\\([0-9\\\\.]*\\\\)$#: \\\\1#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/samtools/samtools_fastq/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/samtools/samtools_fastq", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_SUFFIX+x} ]; then echo "${VIASH_PAR_NO_SUFFIX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_suffix='&'#" ; else echo "# par_no_suffix="; fi ) +$( if [ ! -z ${VIASH_PAR_SUFFIX+x} ]; then echo "${VIASH_PAR_SUFFIX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_suffix='&'#" ; else echo "# par_suffix="; fi ) +$( if [ ! -z ${VIASH_PAR_USE_OQ+x} ]; then echo "${VIASH_PAR_USE_OQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_use_oq='&'#" ; else echo "# par_use_oq="; fi ) +$( if [ ! -z ${VIASH_PAR_SINGLETON+x} ]; then echo "${VIASH_PAR_SINGLETON}" | sed "s#'#'\\"'\\"'#g;s#.*#par_singleton='&'#" ; else echo "# par_singleton="; fi ) +$( if [ ! -z ${VIASH_PAR_COPY_TAGS+x} ]; then echo "${VIASH_PAR_COPY_TAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_copy_tags='&'#" ; else echo "# par_copy_tags="; fi ) +$( if [ ! -z ${VIASH_PAR_COPY_TAGS_LIST+x} ]; then echo "${VIASH_PAR_COPY_TAGS_LIST}" | sed "s#'#'\\"'\\"'#g;s#.*#par_copy_tags_list='&'#" ; else echo "# par_copy_tags_list="; fi ) +$( if [ ! -z ${VIASH_PAR_READ1+x} ]; then echo "${VIASH_PAR_READ1}" | sed "s#'#'\\"'\\"'#g;s#.*#par_read1='&'#" ; else echo "# par_read1="; fi ) +$( if [ ! -z ${VIASH_PAR_READ2+x} ]; then echo "${VIASH_PAR_READ2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_read2='&'#" ; else echo "# par_read2="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_READS+x} ]; then echo "${VIASH_PAR_OUTPUT_READS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_reads='&'#" ; else echo "# par_output_reads="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_READS_BOTH+x} ]; then echo "${VIASH_PAR_OUTPUT_READS_BOTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_reads_both='&'#" ; else echo "# par_output_reads_both="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTER_FLAGS+x} ]; then echo "${VIASH_PAR_FILTER_FLAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_filter_flags='&'#" ; else echo "# par_filter_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCL_FLAGS+x} ]; then echo "${VIASH_PAR_EXCL_FLAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_excl_flags='&'#" ; else echo "# par_excl_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_INCL_FLAGS+x} ]; then echo "${VIASH_PAR_INCL_FLAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_incl_flags='&'#" ; else echo "# par_incl_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCL_FLAGS_ALL+x} ]; then echo "${VIASH_PAR_EXCL_FLAGS_ALL}" | sed "s#'#'\\"'\\"'#g;s#.*#par_excl_flags_all='&'#" ; else echo "# par_excl_flags_all="; fi ) +$( if [ ! -z ${VIASH_PAR_AUX_TAG+x} ]; then echo "${VIASH_PAR_AUX_TAG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_aux_tag='&'#" ; else echo "# par_aux_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_AUX_TAG_FILE+x} ]; then echo "${VIASH_PAR_AUX_TAG_FILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_aux_tag_file='&'#" ; else echo "# par_aux_tag_file="; fi ) +$( if [ ! -z ${VIASH_PAR_CASAVA+x} ]; then echo "${VIASH_PAR_CASAVA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_casava='&'#" ; else echo "# par_casava="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPRESSION+x} ]; then echo "${VIASH_PAR_COMPRESSION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_compression='&'#" ; else echo "# par_compression="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX1+x} ]; then echo "${VIASH_PAR_INDEX1}" | sed "s#'#'\\"'\\"'#g;s#.*#par_index1='&'#" ; else echo "# par_index1="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX2+x} ]; then echo "${VIASH_PAR_INDEX2}" | sed "s#'#'\\"'\\"'#g;s#.*#par_index2='&'#" ; else echo "# par_index2="; fi ) +$( if [ ! -z ${VIASH_PAR_BARCODE_TAG+x} ]; then echo "${VIASH_PAR_BARCODE_TAG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_barcode_tag='&'#" ; else echo "# par_barcode_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_QUALITY_TAG+x} ]; then echo "${VIASH_PAR_QUALITY_TAG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_quality_tag='&'#" ; else echo "# par_quality_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX_FORMAT+x} ]; then echo "${VIASH_PAR_INDEX_FORMAT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_index_format='&'#" ; else echo "# par_index_format="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\\$par_no_suffix" == "false" ]] && unset par_no_suffix +[[ "\\$par_suffix" == "false" ]] && unset par_suffix +[[ "\\$par_use_oq" == "false" ]] && unset par_use_oq +[[ "\\$par_copy_tags" == "false" ]] && unset par_copy_tags +[[ "\\$par_casava" == "false" ]] && unset par_casava + +samtools fastq \\\\ + \\${par_no_suffix:+-n} \\\\ + \\${par_suffix:+-N} \\\\ + \\${par_use_oq:+-O} \\\\ + \\${par_singleton:+-s "\\$par_singleton"} \\\\ + \\${par_copy_tags:+-t} \\\\ + \\${par_copy_tags_list:+-T "\\$par_copy_tags_list"} \\\\ + \\${par_read1:+-1 "\\$par_read1"} \\\\ + \\${par_read2:+-2 "\\$par_read2"} \\\\ + \\${par_output_reads:+-o "\\$par_output_reads"} \\\\ + \\${par_output_reads_both:+-0 "\\$par_output_reads_both"} \\\\ + \\${par_filter_flags:+-f "\\$par_filter_flags"} \\\\ + \\${par_excl_flags:+-F "\\$par_excl_flags"} \\\\ + \\${par_incl_flags:+--rf "\\$par_incl_flags"} \\\\ + \\${par_excl_flags_all:+-G "\\$par_excl_flags_all"} \\\\ + \\${par_aux_tag:+-d "\\$par_aux_tag"} \\\\ + \\${par_aux_tag_file:+-D "\\$par_aux_tag_file"} \\\\ + \\${par_casava:+-i} \\\\ + \\${par_compression:+-c "\\$par_compression"} \\\\ + \\${par_index1:+--i1 "\\$par_index1"} \\\\ + \\${par_index2:+--i2 "\\$par_index2"} \\\\ + \\${par_barcode_tag:+--barcode-tag "\\$par_barcode_tag"} \\\\ + \\${par_quality_tag:+--quality-tag "\\$par_quality_tag"} \\\\ + \\${par_index_format:+--index-format "\\$par_index_format"} \\\\ + "\\$par_input" \\\\ + > "\\$par_output" +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/samtools/samtools_fastq", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/samtools/samtools_fastq/nextflow.config b/target/nextflow/samtools/samtools_fastq/nextflow.config new file mode 100644 index 00000000..2fe59400 --- /dev/null +++ b/target/nextflow/samtools/samtools_fastq/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'samtools/samtools_fastq' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Converts a SAM, BAM or CRAM to FASTQ format.' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/samtools/samtools_fastq/nextflow_schema.json b/target/nextflow/samtools/samtools_fastq/nextflow_schema.json new file mode 100644 index 00000000..078c9148 --- /dev/null +++ b/target/nextflow/samtools/samtools_fastq/nextflow_schema.json @@ -0,0 +1,355 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "samtools_fastq", +"description": "Converts a SAM, BAM or CRAM to FASTQ format.", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`, required. input SAM/BAM/CRAM file", + "help_text": "Type: `file`, required. input SAM/BAM/CRAM file" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output.output`. output FASTQ file", + "help_text": "Type: `file`, required, default: `$id.$key.output.output`. output FASTQ file" + , + "default": "$id.$key.output.output" + } + + +} +}, + + + "options" : { + "title": "Options", + "type": "object", + "description": "No description", + "properties": { + + + "no_suffix": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. By default, either \u0027/1\u0027 or \u0027/2\u0027 is added to the end of read names where the corresponding \nREAD1 or READ2 FLAG bit is set", + "help_text": "Type: `boolean_true`, default: `false`. By default, either \u0027/1\u0027 or \u0027/2\u0027 is added to the end of read names where the corresponding \nREAD1 or READ2 FLAG bit is set. Using -n causes read names to be left as they are.\n" + , + "default": "False" + } + + + , + "suffix": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Always add either \u0027/1\u0027 or \u0027/2\u0027 to the end of read names even when put into different files", + "help_text": "Type: `boolean_true`, default: `false`. Always add either \u0027/1\u0027 or \u0027/2\u0027 to the end of read names even when put into different files.\n" + , + "default": "False" + } + + + , + "use_oq": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use quality values from OQ tags in preference to standard quality string if available", + "help_text": "Type: `boolean_true`, default: `false`. Use quality values from OQ tags in preference to standard quality string if available.\n" + , + "default": "False" + } + + + , + "singleton": { + "type": + "string", + "description": "Type: `file`. write singleton reads to FILE", + "help_text": "Type: `file`. write singleton reads to FILE." + + } + + + , + "copy_tags": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Copy RG, BC and QT tags to the FASTQ header line, if they exist", + "help_text": "Type: `boolean_true`, default: `false`. Copy RG, BC and QT tags to the FASTQ header line, if they exist.\n" + , + "default": "False" + } + + + , + "copy_tags_list": { + "type": + "string", + "description": "Type: `string`. Specify a comma-separated list of tags to copy to the FASTQ header line, if they exist", + "help_text": "Type: `string`. Specify a comma-separated list of tags to copy to the FASTQ header line, if they exist. \nTAGLIST can be blank or * to indicate all tags should be copied to the output. If using *, \nbe careful to quote it to avoid unwanted shell expansion.\n" + + } + + + , + "read1": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.read1.read1`. Write reads with the READ1 FLAG set (and READ2 not set) to FILE instead of outputting them", + "help_text": "Type: `file`, default: `$id.$key.read1.read1`. Write reads with the READ1 FLAG set (and READ2 not set) to FILE instead of outputting them. \nIf the -s option is used, only paired reads will be written to this file.\n" + , + "default": "$id.$key.read1.read1" + } + + + , + "read2": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.read2.read2`. Write reads with the READ2 FLAG set (and READ1 not set) to FILE instead of outputting them", + "help_text": "Type: `file`, default: `$id.$key.read2.read2`. Write reads with the READ2 FLAG set (and READ1 not set) to FILE instead of outputting them. \nIf the -s option is used, only paired reads will be written to this file.\n" + , + "default": "$id.$key.read2.read2" + } + + + , + "output_reads": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.output_reads.output_reads`. Write reads with either READ1 FLAG or READ2 flag set to FILE instead of outputting them to stdout", + "help_text": "Type: `file`, default: `$id.$key.output_reads.output_reads`. Write reads with either READ1 FLAG or READ2 flag set to FILE instead of outputting them to stdout. \nThis is equivalent to -1 FILE -2 FILE.\n" + , + "default": "$id.$key.output_reads.output_reads" + } + + + , + "output_reads_both": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.output_reads_both.output_reads_both`. Write reads where the READ1 and READ2 FLAG bits set are either both set or both unset to FILE \ninstead of outputting them", + "help_text": "Type: `file`, default: `$id.$key.output_reads_both.output_reads_both`. Write reads where the READ1 and READ2 FLAG bits set are either both set or both unset to FILE \ninstead of outputting them.\n" + , + "default": "$id.$key.output_reads_both.output_reads_both" + } + + + , + "filter_flags": { + "type": + "integer", + "description": "Type: `integer`, default: `0`. Only output alignments with all bits set in INT present in the FLAG field", + "help_text": "Type: `integer`, default: `0`. Only output alignments with all bits set in INT present in the FLAG field. INT can be specified \nin hex by beginning with `0x\u0027 (i.e. /^0x[0-9A-F]+/) or in octal by beginning with `0\u0027 \n(i.e. /^0[0-7]+/).\n" + , + "default": "0" + } + + + , + "excl_flags": { + "type": + "string", + "description": "Type: `string`, default: `2304`. Do not output alignments with any bits set in INT present in the FLAG field", + "help_text": "Type: `string`, default: `2304`. Do not output alignments with any bits set in INT present in the FLAG field. INT can be specified \nin hex by beginning with `0x\u0027 (i.e. /^0x[0-9A-F]+/) or in octal by beginning with `0\u0027 \n(i.e. /^0[0-7]+/). This defaults to 0x900 representing filtering of secondary and \nsupplementary alignments.\n" + , + "default": "2304" + } + + + , + "incl_flags": { + "type": + "string", + "description": "Type: `string`, default: `0`. Only output alignments with any bits set in INT present in the FLAG field", + "help_text": "Type: `string`, default: `0`. Only output alignments with any bits set in INT present in the FLAG field. INT can be specified \nin hex by beginning with `0x\u0027 (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0\u0027 \n(i.e. /^0[0-7]+/), as a decimal number not beginning with \u00270\u0027 or as a comma-separated list of \nflag names.\n" + , + "default": "0" + } + + + , + "excl_flags_all": { + "type": + "integer", + "description": "Type: `integer`, default: `0`. Only EXCLUDE reads with all of the bits set in INT present in the FLAG field", + "help_text": "Type: `integer`, default: `0`. Only EXCLUDE reads with all of the bits set in INT present in the FLAG field. INT can be specified \nin hex by beginning with `0x\u0027 (i.e. /^0x[0-9A-F]+/) or in octal by beginning with `0\u0027 \n(i.e. /^0[0-7]+/).\n" + , + "default": "0" + } + + + , + "aux_tag": { + "type": + "string", + "description": "Type: `string`. Only output alignments containing an auxiliary tag matching both TAG and VAL", + "help_text": "Type: `string`. Only output alignments containing an auxiliary tag matching both TAG and VAL. If VAL is omitted \nthen any value is accepted. The tag types supported are i, f, Z, A and H. \"B\" arrays are not \nsupported. This is comparable to the method used in samtools view --tag. The option may be specified \nmultiple times and is equivalent to using the --aux_tag_file option.\n" + + } + + + , + "aux_tag_file": { + "type": + "string", + "description": "Type: `string`. Only output alignments containing an auxiliary tag matching TAG and having a value listed in FILE", + "help_text": "Type: `string`. Only output alignments containing an auxiliary tag matching TAG and having a value listed in FILE. \nThe format of the file is one line per value. This is equivalent to specifying --aux_tag multiple times.\n" + + } + + + , + "casava": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. add Illumina Casava 1", + "help_text": "Type: `boolean_true`, default: `false`. add Illumina Casava 1.8 format entry to header (eg 1:N:0:ATCACG)" + , + "default": "False" + } + + + , + "compression": { + "type": + "integer", + "description": "Type: `integer`, default: `0`. set compression level when writing gz or bgzf fastq files", + "help_text": "Type: `integer`, default: `0`. set compression level when writing gz or bgzf fastq files." + , + "default": "0" + } + + + , + "index1": { + "type": + "string", + "description": "Type: `file`. write first index reads to FILE", + "help_text": "Type: `file`. write first index reads to FILE." + + } + + + , + "index2": { + "type": + "string", + "description": "Type: `file`. write second index reads to FILE", + "help_text": "Type: `file`. write second index reads to FILE." + + } + + + , + "barcode_tag": { + "type": + "string", + "description": "Type: `string`, default: `BC`. Auxiliary tag to find index reads in", + "help_text": "Type: `string`, default: `BC`. Auxiliary tag to find index reads in." + , + "default": "BC" + } + + + , + "quality_tag": { + "type": + "string", + "description": "Type: `string`, default: `QT`. Auxiliary tag to find index quality in", + "help_text": "Type: `string`, default: `QT`. Auxiliary tag to find index quality in." + , + "default": "QT" + } + + + , + "index_format": { + "type": + "string", + "description": "Type: `string`. string to describe how to parse the barcode and quality tags", + "help_text": "Type: `string`. string to describe how to parse the barcode and quality tags. For example:\n[i14i8]: the first 14 characters are index 1, the next 8 characters are index 2.\n[n8i14]: ignore the first 8 characters, and use the next 14 characters for index 1.\nIf the tag contains a separator, then the numeric part can be replaced with \u0027*\u0027 to mean \n\u0027read until the separator or end of tag\u0027, for example: [n*i*].\n" + + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/options" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/samtools/samtools_flagstat/.config.vsh.yaml b/target/nextflow/samtools/samtools_flagstat/.config.vsh.yaml new file mode 100644 index 00000000..971641d8 --- /dev/null +++ b/target/nextflow/samtools/samtools_flagstat/.config.vsh.yaml @@ -0,0 +1,185 @@ +name: "samtools_flagstat" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--bam" + description: "BAM input files.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--bai" + description: "BAM index file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + description: "File containing samtools stats output.\n" + info: null + example: + - "output.flagstat" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Counts the number of alignments in SAM/BAM/CRAM files for each FLAG\ + \ type." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "stats" +- "mapping" +- "counts" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-flagstat.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_flagstat/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/samtools/samtools_flagstat" + executable: "target/nextflow/samtools/samtools_flagstat/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/samtools/samtools_flagstat/main.nf b/target/nextflow/samtools/samtools_flagstat/main.nf new file mode 100644 index 00000000..7eb25fa5 --- /dev/null +++ b/target/nextflow/samtools/samtools_flagstat/main.nf @@ -0,0 +1,3528 @@ +// samtools_flagstat main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "samtools_flagstat", + "namespace" : "samtools", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--bam", + "description" : "BAM input files.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--bai", + "description" : "BAM index file.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "description" : "File containing samtools stats output.\n", + "example" : [ + "output.flagstat" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Counts the number of alignments in SAM/BAM/CRAM files for each FLAG type.", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "stats", + "mapping", + "counts", + "bam", + "sam", + "cram" + ], + "license" : "MIT/Expat", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btp352", + "10.1093/gigascience/giab008" + ] + }, + "links" : { + "repository" : "https://github.com/samtools/samtools", + "homepage" : "https://www.htslib.org/", + "documentation" : "https://www.htslib.org/doc/samtools-flagstat.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\\\nsed 's#Using ##;s# \\\\([0-9\\\\.]*\\\\)$#: \\\\1#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/samtools/samtools_flagstat/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/samtools/samtools_flagstat", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_BAM+x} ]; then echo "${VIASH_PAR_BAM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bam='&'#" ; else echo "# par_bam="; fi ) +$( if [ ! -z ${VIASH_PAR_BAI+x} ]; then echo "${VIASH_PAR_BAI}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bai='&'#" ; else echo "# par_bai="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +samtools flagstat \\\\ + "\\$par_bam" \\\\ + > "\\$par_output" + +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/samtools/samtools_flagstat", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/samtools/samtools_flagstat/nextflow.config b/target/nextflow/samtools/samtools_flagstat/nextflow.config new file mode 100644 index 00000000..db252ff7 --- /dev/null +++ b/target/nextflow/samtools/samtools_flagstat/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'samtools/samtools_flagstat' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Counts the number of alignments in SAM/BAM/CRAM files for each FLAG type.' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/samtools/samtools_flagstat/nextflow_schema.json b/target/nextflow/samtools/samtools_flagstat/nextflow_schema.json new file mode 100644 index 00000000..6c3cfd06 --- /dev/null +++ b/target/nextflow/samtools/samtools_flagstat/nextflow_schema.json @@ -0,0 +1,105 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "samtools_flagstat", +"description": "Counts the number of alignments in SAM/BAM/CRAM files for each FLAG type.", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "bam": { + "type": + "string", + "description": "Type: `file`. BAM input files", + "help_text": "Type: `file`. BAM input files.\n" + + } + + + , + "bai": { + "type": + "string", + "description": "Type: `file`. BAM index file", + "help_text": "Type: `file`. BAM index file.\n" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output.flagstat`, example: `output.flagstat`. File containing samtools stats output", + "help_text": "Type: `file`, required, default: `$id.$key.output.flagstat`, example: `output.flagstat`. File containing samtools stats output.\n" + , + "default": "$id.$key.output.flagstat" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/samtools/samtools_idxstats/.config.vsh.yaml b/target/nextflow/samtools/samtools_idxstats/.config.vsh.yaml new file mode 100644 index 00000000..2b778a7c --- /dev/null +++ b/target/nextflow/samtools/samtools_idxstats/.config.vsh.yaml @@ -0,0 +1,195 @@ +name: "samtools_idxstats" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--bam" + description: "BAM input file." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--bai" + description: "BAM index file." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--fasta" + description: "Reference file the CRAM was created with (optional)." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + description: "File containing samtools stats output in tab-delimited format.\n" + info: null + example: + - "output.idxstats" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Reports alignment summary statistics for a BAM file." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "stats" +- "mapping" +- "counts" +- "chromosome" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-idxstats.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_idxstats/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/samtools/samtools_idxstats" + executable: "target/nextflow/samtools/samtools_idxstats/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/samtools/samtools_idxstats/main.nf b/target/nextflow/samtools/samtools_idxstats/main.nf new file mode 100644 index 00000000..6256d859 --- /dev/null +++ b/target/nextflow/samtools/samtools_idxstats/main.nf @@ -0,0 +1,3538 @@ +// samtools_idxstats main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "samtools_idxstats", + "namespace" : "samtools", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--bam", + "description" : "BAM input file.", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--bai", + "description" : "BAM index file.", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--fasta", + "description" : "Reference file the CRAM was created with (optional).", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "description" : "File containing samtools stats output in tab-delimited format.\n", + "example" : [ + "output.idxstats" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Reports alignment summary statistics for a BAM file.", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "stats", + "mapping", + "counts", + "chromosome", + "bam", + "sam", + "cram" + ], + "license" : "MIT/Expat", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btp352", + "10.1093/gigascience/giab008" + ] + }, + "links" : { + "repository" : "https://github.com/samtools/samtools", + "homepage" : "https://www.htslib.org/", + "documentation" : "https://www.htslib.org/doc/samtools-idxstats.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\\\nsed 's#Using ##;s# \\\\([0-9\\\\.]*\\\\)$#: \\\\1#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/samtools/samtools_idxstats/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/samtools/samtools_idxstats", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_BAM+x} ]; then echo "${VIASH_PAR_BAM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bam='&'#" ; else echo "# par_bam="; fi ) +$( if [ ! -z ${VIASH_PAR_BAI+x} ]; then echo "${VIASH_PAR_BAI}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bai='&'#" ; else echo "# par_bai="; fi ) +$( if [ ! -z ${VIASH_PAR_FASTA+x} ]; then echo "${VIASH_PAR_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fasta='&'#" ; else echo "# par_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +samtools idxstats "\\$par_bam" > "\\$par_output" +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/samtools/samtools_idxstats", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/samtools/samtools_idxstats/nextflow.config b/target/nextflow/samtools/samtools_idxstats/nextflow.config new file mode 100644 index 00000000..e997e992 --- /dev/null +++ b/target/nextflow/samtools/samtools_idxstats/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'samtools/samtools_idxstats' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Reports alignment summary statistics for a BAM file.' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/samtools/samtools_idxstats/nextflow_schema.json b/target/nextflow/samtools/samtools_idxstats/nextflow_schema.json new file mode 100644 index 00000000..8426544a --- /dev/null +++ b/target/nextflow/samtools/samtools_idxstats/nextflow_schema.json @@ -0,0 +1,115 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "samtools_idxstats", +"description": "Reports alignment summary statistics for a BAM file.", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "bam": { + "type": + "string", + "description": "Type: `file`. BAM input file", + "help_text": "Type: `file`. BAM input file." + + } + + + , + "bai": { + "type": + "string", + "description": "Type: `file`. BAM index file", + "help_text": "Type: `file`. BAM index file." + + } + + + , + "fasta": { + "type": + "string", + "description": "Type: `file`. Reference file the CRAM was created with (optional)", + "help_text": "Type: `file`. Reference file the CRAM was created with (optional)." + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output.idxstats`, example: `output.idxstats`. File containing samtools stats output in tab-delimited format", + "help_text": "Type: `file`, required, default: `$id.$key.output.idxstats`, example: `output.idxstats`. File containing samtools stats output in tab-delimited format.\n" + , + "default": "$id.$key.output.idxstats" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/samtools/samtools_index/.config.vsh.yaml b/target/nextflow/samtools/samtools_index/.config.vsh.yaml new file mode 100644 index 00000000..1eabb436 --- /dev/null +++ b/target/nextflow/samtools/samtools_index/.config.vsh.yaml @@ -0,0 +1,201 @@ +name: "samtools_index" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "Input file name" + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output file name" + info: null + example: + - "out.bam.bai" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +- name: "Options" + arguments: + - type: "boolean_true" + name: "--bai" + alternatives: + - "-b" + description: "Generate BAM index" + info: null + direction: "input" + - type: "boolean_true" + name: "--csi" + alternatives: + - "-c" + description: "Create a CSI index for BAM files instead of the traditional BAI\ + \ \nindex. This will be required for genomes with larger chromosome \nsizes.\n" + info: null + direction: "input" + - type: "integer" + name: "--min_shift" + alternatives: + - "-m" + description: "Create a CSI index, with a minimum interval size of 2^INT.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Index SAM/BAM/CRAM files." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "index" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-index.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_index/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/samtools/samtools_index" + executable: "target/nextflow/samtools/samtools_index/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/samtools/samtools_index/main.nf b/target/nextflow/samtools/samtools_index/main.nf new file mode 100644 index 00000000..5917fdee --- /dev/null +++ b/target/nextflow/samtools/samtools_index/main.nf @@ -0,0 +1,3562 @@ +// samtools_index main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "samtools_index", + "namespace" : "samtools", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "Input file name", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "alternatives" : [ + "-o" + ], + "description" : "Output file name", + "example" : [ + "out.bam.bai" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Options", + "arguments" : [ + { + "type" : "boolean_true", + "name" : "--bai", + "alternatives" : [ + "-b" + ], + "description" : "Generate BAM index", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--csi", + "alternatives" : [ + "-c" + ], + "description" : "Create a CSI index for BAM files instead of the traditional BAI \nindex. This will be required for genomes with larger chromosome \nsizes.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--min_shift", + "alternatives" : [ + "-m" + ], + "description" : "Create a CSI index, with a minimum interval size of 2^INT.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Index SAM/BAM/CRAM files.", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "index", + "bam", + "sam", + "cram" + ], + "license" : "MIT/Expat", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btp352", + "10.1093/gigascience/giab008" + ] + }, + "links" : { + "repository" : "https://github.com/samtools/samtools", + "homepage" : "https://www.htslib.org/", + "documentation" : "https://www.htslib.org/doc/samtools-index.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\\\nsed 's#Using ##;s# \\\\([0-9\\\\.]*\\\\)$#: \\\\1#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/samtools/samtools_index/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/samtools/samtools_index", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_BAI+x} ]; then echo "${VIASH_PAR_BAI}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bai='&'#" ; else echo "# par_bai="; fi ) +$( if [ ! -z ${VIASH_PAR_CSI+x} ]; then echo "${VIASH_PAR_CSI}" | sed "s#'#'\\"'\\"'#g;s#.*#par_csi='&'#" ; else echo "# par_csi="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_SHIFT+x} ]; then echo "${VIASH_PAR_MIN_SHIFT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_shift='&'#" ; else echo "# par_min_shift="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e +[[ "\\$par_multiple" == "false" ]] && unset par_multiple +[[ "\\$par_bai" == "false" ]] && unset par_bai +[[ "\\$par_csi" == "false" ]] && unset par_csi +[[ "\\$par_multiple" == "false" ]] && unset par_multiple + +samtools index \\\\ + "\\$par_input" \\\\ + \\${par_csi:+-c} \\\\ + \\${par_bai:+-b} \\\\ + \\${par_min_shift:+-m "par_min_shift"} \\\\ + \\${par_multiple:+-M} \\\\ + -o "\\$par_output" +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/samtools/samtools_index", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/samtools/samtools_index/nextflow.config b/target/nextflow/samtools/samtools_index/nextflow.config new file mode 100644 index 00000000..9e4063a6 --- /dev/null +++ b/target/nextflow/samtools/samtools_index/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'samtools/samtools_index' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Index SAM/BAM/CRAM files.' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/samtools/samtools_index/nextflow_schema.json b/target/nextflow/samtools/samtools_index/nextflow_schema.json new file mode 100644 index 00000000..38b24926 --- /dev/null +++ b/target/nextflow/samtools/samtools_index/nextflow_schema.json @@ -0,0 +1,141 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "samtools_index", +"description": "Index SAM/BAM/CRAM files.", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`, required. Input file name", + "help_text": "Type: `file`, required. Input file name" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output.bai`, example: `out.bam.bai`. Output file name", + "help_text": "Type: `file`, required, default: `$id.$key.output.bai`, example: `out.bam.bai`. Output file name" + , + "default": "$id.$key.output.bai" + } + + +} +}, + + + "options" : { + "title": "Options", + "type": "object", + "description": "No description", + "properties": { + + + "bai": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Generate BAM index", + "help_text": "Type: `boolean_true`, default: `false`. Generate BAM index" + , + "default": "False" + } + + + , + "csi": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Create a CSI index for BAM files instead of the traditional BAI \nindex", + "help_text": "Type: `boolean_true`, default: `false`. Create a CSI index for BAM files instead of the traditional BAI \nindex. This will be required for genomes with larger chromosome \nsizes.\n" + , + "default": "False" + } + + + , + "min_shift": { + "type": + "integer", + "description": "Type: `integer`. Create a CSI index, with a minimum interval size of 2^INT", + "help_text": "Type: `integer`. Create a CSI index, with a minimum interval size of 2^INT.\n" + + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/options" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/samtools/samtools_sort/.config.vsh.yaml b/target/nextflow/samtools/samtools_sort/.config.vsh.yaml new file mode 100644 index 00000000..39d5c2c0 --- /dev/null +++ b/target/nextflow/samtools/samtools_sort/.config.vsh.yaml @@ -0,0 +1,344 @@ +name: "samtools_sort" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "SAM/BAM/CRAM input file." + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + description: "Write final output to file.\n" + info: null + example: + - "out.bam" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt" + alternatives: + - "-O" + description: "Specify output format (SAM, BAM, CRAM).\n" + info: null + example: + - "BAM" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt_option" + description: "Specify a single output file format option in the form\nof OPTION\ + \ or OPTION=VALUE.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reference" + description: "Reference sequence FASTA FILE.\n" + info: null + example: + - "ref.fa" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--write_index" + description: "Automatically index the output files.\n" + info: null + direction: "input" + - type: "string" + name: "--prefix" + alternatives: + - "-T" + description: "Write temporary files to PREFIX.nnnn.bam.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_PG" + description: "Do not add a PG line.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--template_coordinate" + description: "Sort by template-coordinate.\n" + info: null + direction: "input" + - type: "string" + name: "--input_fmt_option" + description: "Specify a single input file format option in the form\nof OPTION\ + \ or OPTION=VALUE.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Options" + arguments: + - type: "integer" + name: "--compression" + alternatives: + - "-l" + description: "Set compression level, from 0 (uncompressed) to 9 (best).\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--uncompressed" + alternatives: + - "-u" + description: "Output uncompressed data (equivalent to --compression 0).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--minimiser" + alternatives: + - "-M" + description: "Use minimiser for clustering unaligned/unplaced reads.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--not_reverse" + alternatives: + - "-R" + description: "Do not use reverse strand (only compatible with --minimiser)\n" + info: null + direction: "input" + - type: "integer" + name: "--kmer_size" + alternatives: + - "-K" + description: "Kmer size to use for minimiser.\n" + info: null + example: + - 20 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--order" + alternatives: + - "-I" + description: "Order minimisers by their position in FILE FASTA.\n" + info: null + example: + - "ref.fa" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--window" + alternatives: + - "-w" + description: "Window size for minimiser INDEXING VIA --order REF.FA.\n" + info: null + example: + - 100 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--homopolymers" + alternatives: + - "-H" + description: "Squash homopolymers when computing minimiser.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--natural_sort" + alternatives: + - "-n" + description: "Sort by read name (natural): cannot be used with samtools index.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--ascii_sort" + alternatives: + - "-N" + description: "Sort by read name (ASCII): cannot be used with samtools index.\n" + info: null + direction: "input" + - type: "string" + name: "--tag" + alternatives: + - "-t" + description: "Sort by value of TAG. Uses position as secondary index \n(or read\ + \ name if --natural_sort is set).\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Sort SAM/BAM/CRAM file." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "sort" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-sort.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_sort/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/samtools/samtools_sort" + executable: "target/nextflow/samtools/samtools_sort/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/samtools/samtools_sort/main.nf b/target/nextflow/samtools/samtools_sort/main.nf new file mode 100644 index 00000000..20753ee4 --- /dev/null +++ b/target/nextflow/samtools/samtools_sort/main.nf @@ -0,0 +1,3775 @@ +// samtools_sort main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "samtools_sort", + "namespace" : "samtools", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "SAM/BAM/CRAM input file.", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "description" : "Write final output to file.\n", + "example" : [ + "out.bam" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--output_fmt", + "alternatives" : [ + "-O" + ], + "description" : "Specify output format (SAM, BAM, CRAM).\n", + "example" : [ + "BAM" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--output_fmt_option", + "description" : "Specify a single output file format option in the form\nof OPTION or OPTION=VALUE.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--reference", + "description" : "Reference sequence FASTA FILE.\n", + "example" : [ + "ref.fa" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--write_index", + "description" : "Automatically index the output files.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--prefix", + "alternatives" : [ + "-T" + ], + "description" : "Write temporary files to PREFIX.nnnn.bam.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--no_PG", + "description" : "Do not add a PG line.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--template_coordinate", + "description" : "Sort by template-coordinate.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--input_fmt_option", + "description" : "Specify a single input file format option in the form\nof OPTION or OPTION=VALUE.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Options", + "arguments" : [ + { + "type" : "integer", + "name" : "--compression", + "alternatives" : [ + "-l" + ], + "description" : "Set compression level, from 0 (uncompressed) to 9 (best).\n", + "default" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--uncompressed", + "alternatives" : [ + "-u" + ], + "description" : "Output uncompressed data (equivalent to --compression 0).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--minimiser", + "alternatives" : [ + "-M" + ], + "description" : "Use minimiser for clustering unaligned/unplaced reads.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--not_reverse", + "alternatives" : [ + "-R" + ], + "description" : "Do not use reverse strand (only compatible with --minimiser)\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--kmer_size", + "alternatives" : [ + "-K" + ], + "description" : "Kmer size to use for minimiser.\n", + "example" : [ + 20 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--order", + "alternatives" : [ + "-I" + ], + "description" : "Order minimisers by their position in FILE FASTA.\n", + "example" : [ + "ref.fa" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--window", + "alternatives" : [ + "-w" + ], + "description" : "Window size for minimiser INDEXING VIA --order REF.FA.\n", + "example" : [ + 100 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--homopolymers", + "alternatives" : [ + "-H" + ], + "description" : "Squash homopolymers when computing minimiser.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--natural_sort", + "alternatives" : [ + "-n" + ], + "description" : "Sort by read name (natural): cannot be used with samtools index.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--ascii_sort", + "alternatives" : [ + "-N" + ], + "description" : "Sort by read name (ASCII): cannot be used with samtools index.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--tag", + "alternatives" : [ + "-t" + ], + "description" : "Sort by value of TAG. Uses position as secondary index \n(or read name if --natural_sort is set).\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Sort SAM/BAM/CRAM file.", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "sort", + "bam", + "sam", + "cram" + ], + "license" : "MIT/Expat", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btp352", + "10.1093/gigascience/giab008" + ] + }, + "links" : { + "repository" : "https://github.com/samtools/samtools", + "homepage" : "https://www.htslib.org/", + "documentation" : "https://www.htslib.org/doc/samtools-sort.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\\\nsed 's#Using ##;s# \\\\([0-9\\\\.]*\\\\)$#: \\\\1#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/samtools/samtools_sort/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/samtools/samtools_sort", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_fmt='&'#" ; else echo "# par_output_fmt="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT_OPTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_fmt_option='&'#" ; else echo "# par_output_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_REFERENCE+x} ]; then echo "${VIASH_PAR_REFERENCE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_reference='&'#" ; else echo "# par_reference="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_INDEX+x} ]; then echo "${VIASH_PAR_WRITE_INDEX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_write_index='&'#" ; else echo "# par_write_index="; fi ) +$( if [ ! -z ${VIASH_PAR_PREFIX+x} ]; then echo "${VIASH_PAR_PREFIX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_prefix='&'#" ; else echo "# par_prefix="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_PG+x} ]; then echo "${VIASH_PAR_NO_PG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_PG='&'#" ; else echo "# par_no_PG="; fi ) +$( if [ ! -z ${VIASH_PAR_TEMPLATE_COORDINATE+x} ]; then echo "${VIASH_PAR_TEMPLATE_COORDINATE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_template_coordinate='&'#" ; else echo "# par_template_coordinate="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_INPUT_FMT_OPTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input_fmt_option='&'#" ; else echo "# par_input_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_COMPRESSION+x} ]; then echo "${VIASH_PAR_COMPRESSION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_compression='&'#" ; else echo "# par_compression="; fi ) +$( if [ ! -z ${VIASH_PAR_UNCOMPRESSED+x} ]; then echo "${VIASH_PAR_UNCOMPRESSED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_uncompressed='&'#" ; else echo "# par_uncompressed="; fi ) +$( if [ ! -z ${VIASH_PAR_MINIMISER+x} ]; then echo "${VIASH_PAR_MINIMISER}" | sed "s#'#'\\"'\\"'#g;s#.*#par_minimiser='&'#" ; else echo "# par_minimiser="; fi ) +$( if [ ! -z ${VIASH_PAR_NOT_REVERSE+x} ]; then echo "${VIASH_PAR_NOT_REVERSE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_not_reverse='&'#" ; else echo "# par_not_reverse="; fi ) +$( if [ ! -z ${VIASH_PAR_KMER_SIZE+x} ]; then echo "${VIASH_PAR_KMER_SIZE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_kmer_size='&'#" ; else echo "# par_kmer_size="; fi ) +$( if [ ! -z ${VIASH_PAR_ORDER+x} ]; then echo "${VIASH_PAR_ORDER}" | sed "s#'#'\\"'\\"'#g;s#.*#par_order='&'#" ; else echo "# par_order="; fi ) +$( if [ ! -z ${VIASH_PAR_WINDOW+x} ]; then echo "${VIASH_PAR_WINDOW}" | sed "s#'#'\\"'\\"'#g;s#.*#par_window='&'#" ; else echo "# par_window="; fi ) +$( if [ ! -z ${VIASH_PAR_HOMOPOLYMERS+x} ]; then echo "${VIASH_PAR_HOMOPOLYMERS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_homopolymers='&'#" ; else echo "# par_homopolymers="; fi ) +$( if [ ! -z ${VIASH_PAR_NATURAL_SORT+x} ]; then echo "${VIASH_PAR_NATURAL_SORT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_natural_sort='&'#" ; else echo "# par_natural_sort="; fi ) +$( if [ ! -z ${VIASH_PAR_ASCII_SORT+x} ]; then echo "${VIASH_PAR_ASCII_SORT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ascii_sort='&'#" ; else echo "# par_ascii_sort="; fi ) +$( if [ ! -z ${VIASH_PAR_TAG+x} ]; then echo "${VIASH_PAR_TAG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_tag='&'#" ; else echo "# par_tag="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\\$par_uncompressed" == "false" ]] && unset par_uncompressed +[[ "\\$par_minimiser" == "false" ]] && unset par_minimiser +[[ "\\$par_not_reverse" == "false" ]] && unset par_not_reverse +[[ "\\$par_homopolymers" == "false" ]] && unset par_homopolymers +[[ "\\$par_natural_sort" == "false" ]] && unset par_natural_sort +[[ "\\$par_ascii_sort" == "false" ]] && unset par_ascii_sort +[[ "\\$par_template_coordinate" == "false" ]] && unset par_template_coordinate +[[ "\\$par_write_index" == "false" ]] && unset par_write_index +[[ "\\$par_no_PG" == "false" ]] && unset par_no_PG + + +samtools sort \\\\ + \\${par_compression:+-l "\\$par_compression"} \\\\ + \\${par_uncompressed:+-u} \\\\ + \\${par_minimiser:+-M} \\\\ + \\${par_not_reverse:+-R} \\\\ + \\${par_kmer_size:+-K "\\$par_kmer_size"} \\\\ + \\${par_order:+-I "\\$par_order"} \\\\ + \\${par_window:+-w "\\$par_window"} \\\\ + \\${par_homopolymers:+-H} \\\\ + \\${par_natural_sort:+-n} \\\\ + \\${par_ascii_sort:+-N} \\\\ + \\${par_tag:+-t "\\$par_tag"} \\\\ + \\${par_input_fmt_option:+--input-fmt-option "\\$par_input_fmt_option"} \\\\ + \\${par_template_coordinate:+--template-coordinate} \\\\ + \\${par_write_index:+--write-index} \\\\ + \\${par_prefix:+-T "\\$par_prefix"} \\\\ + \\${par_no_PG:+--no-PG} \\\\ + \\${par_output_fmt:+-O "\\$par_output_fmt"} \\\\ + \\${par_output_fmt_option:+--output-fmt-option "\\$par_output_fmt_option"} \\\\ + \\${par_reference:+--reference "\\$par_reference"} \\\\ + -o "\\$par_output" \\\\ + "\\$par_input" + +# save text files containing the output of samtools view for later comparison +samtools view "\\$par_output" -o "\\$par_output".txt +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/samtools/samtools_sort", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/samtools/samtools_sort/nextflow.config b/target/nextflow/samtools/samtools_sort/nextflow.config new file mode 100644 index 00000000..7c73ec79 --- /dev/null +++ b/target/nextflow/samtools/samtools_sort/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'samtools/samtools_sort' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Sort SAM/BAM/CRAM file.' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/samtools/samtools_sort/nextflow_schema.json b/target/nextflow/samtools/samtools_sort/nextflow_schema.json new file mode 100644 index 00000000..74147b33 --- /dev/null +++ b/target/nextflow/samtools/samtools_sort/nextflow_schema.json @@ -0,0 +1,309 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "samtools_sort", +"description": "Sort SAM/BAM/CRAM file.", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`, required. SAM/BAM/CRAM input file", + "help_text": "Type: `file`, required. SAM/BAM/CRAM input file." + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output.bam`, example: `out.bam`. Write final output to file", + "help_text": "Type: `file`, required, default: `$id.$key.output.bam`, example: `out.bam`. Write final output to file.\n" + , + "default": "$id.$key.output.bam" + } + + + , + "output_fmt": { + "type": + "string", + "description": "Type: `string`, example: `BAM`. Specify output format (SAM, BAM, CRAM)", + "help_text": "Type: `string`, example: `BAM`. Specify output format (SAM, BAM, CRAM).\n" + + } + + + , + "output_fmt_option": { + "type": + "string", + "description": "Type: `string`. Specify a single output file format option in the form\nof OPTION or OPTION=VALUE", + "help_text": "Type: `string`. Specify a single output file format option in the form\nof OPTION or OPTION=VALUE.\n" + + } + + + , + "reference": { + "type": + "string", + "description": "Type: `file`, example: `ref.fa`. Reference sequence FASTA FILE", + "help_text": "Type: `file`, example: `ref.fa`. Reference sequence FASTA FILE.\n" + + } + + + , + "write_index": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Automatically index the output files", + "help_text": "Type: `boolean_true`, default: `false`. Automatically index the output files.\n" + , + "default": "False" + } + + + , + "prefix": { + "type": + "string", + "description": "Type: `string`. Write temporary files to PREFIX", + "help_text": "Type: `string`. Write temporary files to PREFIX.nnnn.bam.\n" + + } + + + , + "no_PG": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Do not add a PG line", + "help_text": "Type: `boolean_true`, default: `false`. Do not add a PG line.\n" + , + "default": "False" + } + + + , + "template_coordinate": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Sort by template-coordinate", + "help_text": "Type: `boolean_true`, default: `false`. Sort by template-coordinate.\n" + , + "default": "False" + } + + + , + "input_fmt_option": { + "type": + "string", + "description": "Type: `string`. Specify a single input file format option in the form\nof OPTION or OPTION=VALUE", + "help_text": "Type: `string`. Specify a single input file format option in the form\nof OPTION or OPTION=VALUE.\n" + + } + + +} +}, + + + "options" : { + "title": "Options", + "type": "object", + "description": "No description", + "properties": { + + + "compression": { + "type": + "integer", + "description": "Type: `integer`, default: `0`. Set compression level, from 0 (uncompressed) to 9 (best)", + "help_text": "Type: `integer`, default: `0`. Set compression level, from 0 (uncompressed) to 9 (best).\n" + , + "default": "0" + } + + + , + "uncompressed": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output uncompressed data (equivalent to --compression 0)", + "help_text": "Type: `boolean_true`, default: `false`. Output uncompressed data (equivalent to --compression 0).\n" + , + "default": "False" + } + + + , + "minimiser": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use minimiser for clustering unaligned/unplaced reads", + "help_text": "Type: `boolean_true`, default: `false`. Use minimiser for clustering unaligned/unplaced reads.\n" + , + "default": "False" + } + + + , + "not_reverse": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Do not use reverse strand (only compatible with --minimiser)\n", + "help_text": "Type: `boolean_true`, default: `false`. Do not use reverse strand (only compatible with --minimiser)\n" + , + "default": "False" + } + + + , + "kmer_size": { + "type": + "integer", + "description": "Type: `integer`, example: `20`. Kmer size to use for minimiser", + "help_text": "Type: `integer`, example: `20`. Kmer size to use for minimiser.\n" + + } + + + , + "order": { + "type": + "string", + "description": "Type: `file`, example: `ref.fa`. Order minimisers by their position in FILE FASTA", + "help_text": "Type: `file`, example: `ref.fa`. Order minimisers by their position in FILE FASTA.\n" + + } + + + , + "window": { + "type": + "integer", + "description": "Type: `integer`, example: `100`. Window size for minimiser INDEXING VIA --order REF", + "help_text": "Type: `integer`, example: `100`. Window size for minimiser INDEXING VIA --order REF.FA.\n" + + } + + + , + "homopolymers": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Squash homopolymers when computing minimiser", + "help_text": "Type: `boolean_true`, default: `false`. Squash homopolymers when computing minimiser.\n" + , + "default": "False" + } + + + , + "natural_sort": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Sort by read name (natural): cannot be used with samtools index", + "help_text": "Type: `boolean_true`, default: `false`. Sort by read name (natural): cannot be used with samtools index.\n" + , + "default": "False" + } + + + , + "ascii_sort": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Sort by read name (ASCII): cannot be used with samtools index", + "help_text": "Type: `boolean_true`, default: `false`. Sort by read name (ASCII): cannot be used with samtools index.\n" + , + "default": "False" + } + + + , + "tag": { + "type": + "string", + "description": "Type: `string`. Sort by value of TAG", + "help_text": "Type: `string`. Sort by value of TAG. Uses position as secondary index \n(or read name if --natural_sort is set).\n" + + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/options" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/samtools/samtools_stats/.config.vsh.yaml b/target/nextflow/samtools/samtools_stats/.config.vsh.yaml new file mode 100644 index 00000000..7aefbe46 --- /dev/null +++ b/target/nextflow/samtools/samtools_stats/.config.vsh.yaml @@ -0,0 +1,406 @@ +name: "samtools_stats" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "Input file.\n" + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--bai" + description: "Index file.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--fasta" + description: "Reference file the CRAM was created with.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--coverage" + alternatives: + - "-c" + description: "Coverage distribution min,max,step [1,1000,1].\n" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: "," + - type: "boolean_true" + name: "--remove_dups" + alternatives: + - "-d" + description: "Exclude from statistics reads marked as duplicates.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--customized_index_file" + alternatives: + - "-X" + description: "Use a customized index file.\n" + info: null + direction: "input" + - type: "string" + name: "--required_flag" + alternatives: + - "-f" + description: "Required flag, 0 for unset. See also `samtools flags`.\n" + info: null + default: + - "0" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--filtering_flag" + alternatives: + - "-F" + description: "Filtering flag, 0 for unset. See also `samtools flags`.\n" + info: null + default: + - "0" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--GC_depth" + description: "The size of GC-depth bins (decreasing bin size increases memory\ + \ requirement).\n" + info: null + default: + - 20000.0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--insert_size" + alternatives: + - "-i" + description: "Maximum insert size.\n" + info: null + default: + - 8000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--id" + alternatives: + - "-I" + description: "Include only listed read group or sample name.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--read_length" + alternatives: + - "-l" + description: "Include in the statistics only reads with the given read length.\n" + info: null + default: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--most_inserts" + alternatives: + - "-m" + description: "Report only the main part of inserts.\n" + info: null + default: + - 0.99 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--split_prefix" + alternatives: + - "-P" + description: "Path or string prefix for filepaths output by --split (default is\ + \ input filename).\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--trim_quality" + alternatives: + - "-q" + description: "The BWA trimming parameter.\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--ref_seq" + alternatives: + - "-r" + description: "Reference sequence (required for GC-depth and mismatches-per-cycle\ + \ calculation).\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--split" + alternatives: + - "-S" + description: "Also write statistics to separate files split by tagged field.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--target_regions" + alternatives: + - "-t" + description: "Do stats in these regions only. Tab-delimited file chr,from,to,\ + \ 1-based, inclusive.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--sparse" + alternatives: + - "-x" + description: "Suppress outputting IS rows where there are no insertions.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--remove_overlaps" + alternatives: + - "-p" + description: "Remove overlaps of paired-end reads from coverage and base count\ + \ computations.\n" + info: null + direction: "input" + - type: "integer" + name: "--cov_threshold" + alternatives: + - "-g" + description: "Only bases with coverage above this value will be included in the\ + \ target percentage computation.\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--input_fmt_option" + description: "Specify a single input file format option in the form of OPTION\ + \ or OPTION=VALUE.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reference" + description: "Reference sequence FASTA FILE.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output file.\n" + info: null + default: + - "out.txt" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Reports alignment summary statistics for a BAM file." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "statistics" +- "counts" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-stats.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_stats/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/samtools/samtools_stats" + executable: "target/nextflow/samtools/samtools_stats/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/samtools/samtools_stats/main.nf b/target/nextflow/samtools/samtools_stats/main.nf new file mode 100644 index 00000000..1099dec9 --- /dev/null +++ b/target/nextflow/samtools/samtools_stats/main.nf @@ -0,0 +1,3836 @@ +// samtools_stats main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "samtools_stats", + "namespace" : "samtools", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "Input file.\n", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--bai", + "description" : "Index file.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--fasta", + "description" : "Reference file the CRAM was created with.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--coverage", + "alternatives" : [ + "-c" + ], + "description" : "Coverage distribution min,max,step [1,1000,1].\n", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : "," + }, + { + "type" : "boolean_true", + "name" : "--remove_dups", + "alternatives" : [ + "-d" + ], + "description" : "Exclude from statistics reads marked as duplicates.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--customized_index_file", + "alternatives" : [ + "-X" + ], + "description" : "Use a customized index file.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--required_flag", + "alternatives" : [ + "-f" + ], + "description" : "Required flag, 0 for unset. See also `samtools flags`.\n", + "default" : [ + "0" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--filtering_flag", + "alternatives" : [ + "-F" + ], + "description" : "Filtering flag, 0 for unset. See also `samtools flags`.\n", + "default" : [ + "0" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--GC_depth", + "description" : "The size of GC-depth bins (decreasing bin size increases memory requirement).\n", + "default" : [ + 20000.0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--insert_size", + "alternatives" : [ + "-i" + ], + "description" : "Maximum insert size.\n", + "default" : [ + 8000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--id", + "alternatives" : [ + "-I" + ], + "description" : "Include only listed read group or sample name.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--read_length", + "alternatives" : [ + "-l" + ], + "description" : "Include in the statistics only reads with the given read length.\n", + "default" : [ + -1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--most_inserts", + "alternatives" : [ + "-m" + ], + "description" : "Report only the main part of inserts.\n", + "default" : [ + 0.99 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--split_prefix", + "alternatives" : [ + "-P" + ], + "description" : "Path or string prefix for filepaths output by --split (default is input filename).\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--trim_quality", + "alternatives" : [ + "-q" + ], + "description" : "The BWA trimming parameter.\n", + "default" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--ref_seq", + "alternatives" : [ + "-r" + ], + "description" : "Reference sequence (required for GC-depth and mismatches-per-cycle calculation).\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--split", + "alternatives" : [ + "-S" + ], + "description" : "Also write statistics to separate files split by tagged field.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--target_regions", + "alternatives" : [ + "-t" + ], + "description" : "Do stats in these regions only. Tab-delimited file chr,from,to, 1-based, inclusive.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--sparse", + "alternatives" : [ + "-x" + ], + "description" : "Suppress outputting IS rows where there are no insertions.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--remove_overlaps", + "alternatives" : [ + "-p" + ], + "description" : "Remove overlaps of paired-end reads from coverage and base count computations.\n", + "direction" : "input" + }, + { + "type" : "integer", + "name" : "--cov_threshold", + "alternatives" : [ + "-g" + ], + "description" : "Only bases with coverage above this value will be included in the target percentage computation.\n", + "default" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--input_fmt_option", + "description" : "Specify a single input file format option in the form of OPTION or OPTION=VALUE.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--reference", + "description" : "Reference sequence FASTA FILE.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "alternatives" : [ + "-o" + ], + "description" : "Output file.\n", + "default" : [ + "out.txt" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Reports alignment summary statistics for a BAM file.", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "statistics", + "counts", + "bam", + "sam", + "cram" + ], + "license" : "MIT/Expat", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btp352", + "10.1093/gigascience/giab008" + ] + }, + "links" : { + "repository" : "https://github.com/samtools/samtools", + "homepage" : "https://www.htslib.org/", + "documentation" : "https://www.htslib.org/doc/samtools-stats.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\\\nsed 's#Using ##;s# \\\\([0-9\\\\.]*\\\\)$#: \\\\1#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/samtools/samtools_stats/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/samtools/samtools_stats", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_BAI+x} ]; then echo "${VIASH_PAR_BAI}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bai='&'#" ; else echo "# par_bai="; fi ) +$( if [ ! -z ${VIASH_PAR_FASTA+x} ]; then echo "${VIASH_PAR_FASTA}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fasta='&'#" ; else echo "# par_fasta="; fi ) +$( if [ ! -z ${VIASH_PAR_COVERAGE+x} ]; then echo "${VIASH_PAR_COVERAGE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_coverage='&'#" ; else echo "# par_coverage="; fi ) +$( if [ ! -z ${VIASH_PAR_REMOVE_DUPS+x} ]; then echo "${VIASH_PAR_REMOVE_DUPS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_remove_dups='&'#" ; else echo "# par_remove_dups="; fi ) +$( if [ ! -z ${VIASH_PAR_CUSTOMIZED_INDEX_FILE+x} ]; then echo "${VIASH_PAR_CUSTOMIZED_INDEX_FILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_customized_index_file='&'#" ; else echo "# par_customized_index_file="; fi ) +$( if [ ! -z ${VIASH_PAR_REQUIRED_FLAG+x} ]; then echo "${VIASH_PAR_REQUIRED_FLAG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_required_flag='&'#" ; else echo "# par_required_flag="; fi ) +$( if [ ! -z ${VIASH_PAR_FILTERING_FLAG+x} ]; then echo "${VIASH_PAR_FILTERING_FLAG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_filtering_flag='&'#" ; else echo "# par_filtering_flag="; fi ) +$( if [ ! -z ${VIASH_PAR_GC_DEPTH+x} ]; then echo "${VIASH_PAR_GC_DEPTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_GC_depth='&'#" ; else echo "# par_GC_depth="; fi ) +$( if [ ! -z ${VIASH_PAR_INSERT_SIZE+x} ]; then echo "${VIASH_PAR_INSERT_SIZE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_insert_size='&'#" ; else echo "# par_insert_size="; fi ) +$( if [ ! -z ${VIASH_PAR_ID+x} ]; then echo "${VIASH_PAR_ID}" | sed "s#'#'\\"'\\"'#g;s#.*#par_id='&'#" ; else echo "# par_id="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_LENGTH+x} ]; then echo "${VIASH_PAR_READ_LENGTH}" | sed "s#'#'\\"'\\"'#g;s#.*#par_read_length='&'#" ; else echo "# par_read_length="; fi ) +$( if [ ! -z ${VIASH_PAR_MOST_INSERTS+x} ]; then echo "${VIASH_PAR_MOST_INSERTS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_most_inserts='&'#" ; else echo "# par_most_inserts="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLIT_PREFIX+x} ]; then echo "${VIASH_PAR_SPLIT_PREFIX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_split_prefix='&'#" ; else echo "# par_split_prefix="; fi ) +$( if [ ! -z ${VIASH_PAR_TRIM_QUALITY+x} ]; then echo "${VIASH_PAR_TRIM_QUALITY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_trim_quality='&'#" ; else echo "# par_trim_quality="; fi ) +$( if [ ! -z ${VIASH_PAR_REF_SEQ+x} ]; then echo "${VIASH_PAR_REF_SEQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_ref_seq='&'#" ; else echo "# par_ref_seq="; fi ) +$( if [ ! -z ${VIASH_PAR_SPLIT+x} ]; then echo "${VIASH_PAR_SPLIT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_split='&'#" ; else echo "# par_split="; fi ) +$( if [ ! -z ${VIASH_PAR_TARGET_REGIONS+x} ]; then echo "${VIASH_PAR_TARGET_REGIONS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_target_regions='&'#" ; else echo "# par_target_regions="; fi ) +$( if [ ! -z ${VIASH_PAR_SPARSE+x} ]; then echo "${VIASH_PAR_SPARSE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sparse='&'#" ; else echo "# par_sparse="; fi ) +$( if [ ! -z ${VIASH_PAR_REMOVE_OVERLAPS+x} ]; then echo "${VIASH_PAR_REMOVE_OVERLAPS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_remove_overlaps='&'#" ; else echo "# par_remove_overlaps="; fi ) +$( if [ ! -z ${VIASH_PAR_COV_THRESHOLD+x} ]; then echo "${VIASH_PAR_COV_THRESHOLD}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cov_threshold='&'#" ; else echo "# par_cov_threshold="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_INPUT_FMT_OPTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input_fmt_option='&'#" ; else echo "# par_input_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_REFERENCE+x} ]; then echo "${VIASH_PAR_REFERENCE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_reference='&'#" ; else echo "# par_reference="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\\$par_remove_dups" == "false" ]] && unset par_remove_dups +[[ "\\$par_customized_index_file" == "false" ]] && unset par_customized_index_file +[[ "\\$par_sparse" == "false" ]] && unset par_sparse +[[ "\\$par_remove_overlaps" == "false" ]] && unset par_remove_overlaps + +samtools stats \\\\ + \\${par_coverage:+-c "\\$par_coverage"} \\\\ + \\${par_remove_dups:+-d} \\\\ + \\${par_required_flag:+-f "\\$par_required_flag"} \\\\ + \\${par_filtering_flag:+-F "\\$par_filtering_flag"} \\\\ + \\${par_GC_depth:+--GC-depth "\\$par_GC_depth"} \\\\ + \\${par_insert_size:+-i "\\$par_insert_size"} \\\\ + \\${par_id:+-I "\\$par_id"} \\\\ + \\${par_read_length:+-l "\\$par_read_length"} \\\\ + \\${par_most_inserts:+-m "\\$par_most_inserts"} \\\\ + \\${par_split_prefix:+-P "\\$par_split_prefix"} \\\\ + \\${par_trim_quality:+-q "\\$par_trim_quality"} \\\\ + \\${par_ref_seq:+-r "\\$par_ref_seq"} \\\\ + \\${par_split:+-S "\\$par_split"} \\\\ + \\${par_target_regions:+-t "\\$par_target_regions"} \\\\ + \\${par_sparse:+-x} \\\\ + \\${par_remove_overlaps:+-p} \\\\ + \\${par_cov_threshold:+-g "\\$par_cov_threshold"} \\\\ + \\${par_input_fmt_option:+-O "\\$par_input_fmt_option"} \\\\ + \\${par_reference:+-R "\\$par_reference"} \\\\ + "\\$par_input" \\\\ + > "\\$par_output" + +exit 0 +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/samtools/samtools_stats", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/samtools/samtools_stats/nextflow.config b/target/nextflow/samtools/samtools_stats/nextflow.config new file mode 100644 index 00000000..e26eaf30 --- /dev/null +++ b/target/nextflow/samtools/samtools_stats/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'samtools/samtools_stats' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Reports alignment summary statistics for a BAM file.' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/samtools/samtools_stats/nextflow_schema.json b/target/nextflow/samtools/samtools_stats/nextflow_schema.json new file mode 100644 index 00000000..ceb3b49c --- /dev/null +++ b/target/nextflow/samtools/samtools_stats/nextflow_schema.json @@ -0,0 +1,327 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "samtools_stats", +"description": "Reports alignment summary statistics for a BAM file.", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`, required. Input file", + "help_text": "Type: `file`, required. Input file.\n" + + } + + + , + "bai": { + "type": + "string", + "description": "Type: `file`. Index file", + "help_text": "Type: `file`. Index file.\n" + + } + + + , + "fasta": { + "type": + "string", + "description": "Type: `file`. Reference file the CRAM was created with", + "help_text": "Type: `file`. Reference file the CRAM was created with.\n" + + } + + + , + "coverage": { + "type": + "string", + "description": "Type: List of `integer`, multiple_sep: `\",\"`. Coverage distribution min,max,step [1,1000,1]", + "help_text": "Type: List of `integer`, multiple_sep: `\",\"`. Coverage distribution min,max,step [1,1000,1].\n" + + } + + + , + "remove_dups": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Exclude from statistics reads marked as duplicates", + "help_text": "Type: `boolean_true`, default: `false`. Exclude from statistics reads marked as duplicates.\n" + , + "default": "False" + } + + + , + "customized_index_file": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use a customized index file", + "help_text": "Type: `boolean_true`, default: `false`. Use a customized index file.\n" + , + "default": "False" + } + + + , + "required_flag": { + "type": + "string", + "description": "Type: `string`, default: `0`. Required flag, 0 for unset", + "help_text": "Type: `string`, default: `0`. Required flag, 0 for unset. See also `samtools flags`.\n" + , + "default": "0" + } + + + , + "filtering_flag": { + "type": + "string", + "description": "Type: `string`, default: `0`. Filtering flag, 0 for unset", + "help_text": "Type: `string`, default: `0`. Filtering flag, 0 for unset. See also `samtools flags`.\n" + , + "default": "0" + } + + + , + "GC_depth": { + "type": + "number", + "description": "Type: `double`, default: `20000.0`. The size of GC-depth bins (decreasing bin size increases memory requirement)", + "help_text": "Type: `double`, default: `20000.0`. The size of GC-depth bins (decreasing bin size increases memory requirement).\n" + , + "default": "20000.0" + } + + + , + "insert_size": { + "type": + "integer", + "description": "Type: `integer`, default: `8000`. Maximum insert size", + "help_text": "Type: `integer`, default: `8000`. Maximum insert size.\n" + , + "default": "8000" + } + + + , + "id": { + "type": + "string", + "description": "Type: `string`. Include only listed read group or sample name", + "help_text": "Type: `string`. Include only listed read group or sample name.\n" + + } + + + , + "read_length": { + "type": + "integer", + "description": "Type: `integer`, default: `-1`. Include in the statistics only reads with the given read length", + "help_text": "Type: `integer`, default: `-1`. Include in the statistics only reads with the given read length.\n" + , + "default": "-1" + } + + + , + "most_inserts": { + "type": + "number", + "description": "Type: `double`, default: `0.99`. Report only the main part of inserts", + "help_text": "Type: `double`, default: `0.99`. Report only the main part of inserts.\n" + , + "default": "0.99" + } + + + , + "split_prefix": { + "type": + "string", + "description": "Type: `string`. Path or string prefix for filepaths output by --split (default is input filename)", + "help_text": "Type: `string`. Path or string prefix for filepaths output by --split (default is input filename).\n" + + } + + + , + "trim_quality": { + "type": + "integer", + "description": "Type: `integer`, default: `0`. The BWA trimming parameter", + "help_text": "Type: `integer`, default: `0`. The BWA trimming parameter.\n" + , + "default": "0" + } + + + , + "ref_seq": { + "type": + "string", + "description": "Type: `file`. Reference sequence (required for GC-depth and mismatches-per-cycle calculation)", + "help_text": "Type: `file`. Reference sequence (required for GC-depth and mismatches-per-cycle calculation).\n" + + } + + + , + "split": { + "type": + "string", + "description": "Type: `string`. Also write statistics to separate files split by tagged field", + "help_text": "Type: `string`. Also write statistics to separate files split by tagged field.\n" + + } + + + , + "target_regions": { + "type": + "string", + "description": "Type: `file`. Do stats in these regions only", + "help_text": "Type: `file`. Do stats in these regions only. Tab-delimited file chr,from,to, 1-based, inclusive.\n" + + } + + + , + "sparse": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Suppress outputting IS rows where there are no insertions", + "help_text": "Type: `boolean_true`, default: `false`. Suppress outputting IS rows where there are no insertions.\n" + , + "default": "False" + } + + + , + "remove_overlaps": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Remove overlaps of paired-end reads from coverage and base count computations", + "help_text": "Type: `boolean_true`, default: `false`. Remove overlaps of paired-end reads from coverage and base count computations.\n" + , + "default": "False" + } + + + , + "cov_threshold": { + "type": + "integer", + "description": "Type: `integer`, default: `0`. Only bases with coverage above this value will be included in the target percentage computation", + "help_text": "Type: `integer`, default: `0`. Only bases with coverage above this value will be included in the target percentage computation.\n" + , + "default": "0" + } + + + , + "input_fmt_option": { + "type": + "string", + "description": "Type: `string`. Specify a single input file format option in the form of OPTION or OPTION=VALUE", + "help_text": "Type: `string`. Specify a single input file format option in the form of OPTION or OPTION=VALUE.\n" + + } + + + , + "reference": { + "type": + "string", + "description": "Type: `file`. Reference sequence FASTA FILE", + "help_text": "Type: `file`. Reference sequence FASTA FILE.\n" + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output.txt`. Output file", + "help_text": "Type: `file`, required, default: `$id.$key.output.txt`. Output file.\n" + , + "default": "$id.$key.output.txt" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/samtools/samtools_view/.config.vsh.yaml b/target/nextflow/samtools/samtools_view/.config.vsh.yaml new file mode 100644 index 00000000..cfb1632b --- /dev/null +++ b/target/nextflow/samtools/samtools_view/.config.vsh.yaml @@ -0,0 +1,677 @@ +name: "samtools_view" +namespace: "samtools" +version: "main" +argument_groups: +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + description: "Input SAM, BAM, or CRAM file." + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--fai_reference" + alternatives: + - "-t" + description: "A tab-delimited FILE. Each line must contain the reference name\ + \ in the first column\nand the length of the reference in the second column,\ + \ with one line for each distinct\nreference. Any additional fields beyond the\ + \ second column are ignored. This file also\ndefines the order of the reference\ + \ sequences in sorting. If you run: `samtools faidx ',\nthe resulting\ + \ index file .fai can be used as this FILE.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reference" + alternatives: + - "-T" + description: "A FASTA format reference FILE, optionally compressed by bgzip and\ + \ ideally indexed by samtools faidx.\nIf an index is not present one will be\ + \ generated for you, if the reference file is local.\nIf the reference file\ + \ is not local, but is accessed instead via an https://, s3:// or other URL,\n\ + the index file will need to be supplied by the server alongside the reference.\ + \ It is possible to\nhave the reference and index files in different locations\ + \ by supplying both to this option separated\nby the string \"##idx##\", for\ + \ example:\n--reference ftp://x.com/ref.fa##idx##ftp://y.com/index.fa.fai\n\ + However, note that only the location of the reference will be stored in the\ + \ output file header.\nIf this method is used to make CRAM files, the cram reader\ + \ may not be able to find the index,\nand may not be able to decode the file\ + \ unless it can get the references it needs using a different\nmethod.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--target_file" + alternatives: + - "-L" + description: "Only output alignments overlapping the input BED FILE [null].\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--region_file" + description: "Use an index and multi-region iterator to only output alignments\ + \ overlapping the input BED FILE.\nEquivalent to --use_index --target_file FILE.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--qname_file" + alternatives: + - "-N" + description: "Output only alignments with read names listed in FILE. If FILE starts\ + \ with ^ then the operation is\nnegated and only outputs alignment with read\ + \ groups not listed in FILE. It is not permissible to mix\nboth the filter-in\ + \ and filter-out style syntax in the same command.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--read_group_file" + alternatives: + - "-R" + description: "Output alignments in read groups listed in FILE [null]. If FILE\ + \ starts with ^ then the operation is\nnegated and only outputs alignment with\ + \ read names not listed in FILE. It is not permissible to mix\nboth the filter-in\ + \ and filter-out style syntax in the same command. Note that records with no\ + \ RG tag\nwill also be output when using this option. This behaviour may change\ + \ in a future release.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--use_index" + alternatives: + - "-M" + description: "Use the multi-region iterator on the union of a BED file and command-line\ + \ region arguments.\nThis avoids re-reading the same regions of files so can\ + \ sometimes be much faster. Note this also\nremoves duplicate sequences. Without\ + \ this a sequence that overlaps multiple regions specified on\nthe command line\ + \ will be reported multiple times. The usage of a BED file is optional and its\ + \ path\nhas to be preceded by --target_file option.\n" + info: null + direction: "input" +- name: "Outputs" + arguments: + - type: "file" + name: "--output" + alternatives: + - "-o" + description: "Output to FILE instead of [stdout]." + info: null + example: + - "output.bam" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--bam" + alternatives: + - "-b" + description: "Output in the BAM format." + info: null + direction: "input" + - type: "boolean_true" + name: "--cram" + alternatives: + - "-C" + description: "Output in the CRAM format (requires --reference).\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--fast" + description: "Enable fast compression. This also changes the default output format\ + \ to BAM,\nbut this can be overridden by the explicit format options or using\ + \ a filename\nwith a known suffix.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--uncompressed" + alternatives: + - "-u" + description: "Output uncompressed data. This also changes the default output format\ + \ to BAM,\nbut this can be overridden by the explicit format options or using\ + \ a filename\nwith a known suffix.\nThis option saves time spent on compression/decompression\ + \ and is thus preferred\nwhen the output is piped to another samtools command.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--with_header" + description: "Include the header in the output.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--header_only" + alternatives: + - "-H" + description: "Output the header only.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--no_header" + description: "When producing SAM format, output alignment records but not headers.\n\ + This is the default; the option can be used to reset the effect of \n--with_header/--header_only.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--count" + alternatives: + - "-c" + description: "Instead of printing the alignments, only count them and print the\ + \ total number.\nAll filter options, such as --require_flags, --excl_flags,\ + \ and --min_MQ, are taken\ninto account. The --unmap option is ignored in this\ + \ mode.\n" + info: null + direction: "input" + - type: "file" + name: "--output_unselected" + alternatives: + - "-U" + description: "Write alignments that are not selected by the various filter options\ + \ to FILE.\nWhen this option is used, all alignments (or all alignments intersecting\ + \ the regions\nspecified) are written to either the output file or this file,\ + \ but never both.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--unmap" + alternatives: + - "-p" + description: "Set the UNMAP flag on alignments that are not selected by the filter\ + \ options.\nThese alignments are then written to the normal output. This is\ + \ not compatible\nwith --output_unselected.\n" + info: null + direction: "input" + - type: "string" + name: "--read_group" + alternatives: + - "-r" + description: "Output alignments in read group STR [null]. Note that records with\ + \ no RG tag will also be output\nwhen using this option. This behaviour may\ + \ change in a future release.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--tag" + alternatives: + - "-d" + description: "Only output alignments with tag STR1 and associated value STR2,\ + \ which can be a string or an integer\n[null].\nThe value can be omitted, in\ + \ which case only the tag is considered.\nNote that this option does not specify\ + \ a tag type. For example, use --tag XX:42 to select alignments\nwith an XX:i:42\ + \ field, not --tag XX:i:42.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--tag_file" + alternatives: + - "-D" + description: "Only output alignments with tag STR and associated values listed\ + \ in FILE.\n" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_MQ" + alternatives: + - "-q" + description: "Skip alignments with MAPQ smaller than INT.\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--library" + alternatives: + - "-l" + description: "Only output alignments in library STR.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--min_qlen" + alternatives: + - "-m" + description: "Only output alignments with number of CIGAR bases consuming query\ + \ sequence >= INT.\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--expr" + alternatives: + - "-e" + description: "Only include alignments that match the filter expression STR. The\ + \ syntax for these expressions is\ndescribed in the main samtools.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--require_flags" + alternatives: + - "-f" + description: "Only output alignments with all bits set in FLAG present in the\ + \ FLAG field. FLAG can be specified\nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/),\ + \ in octal by beginning with `0' (i.e. /^0[0-7]+/),\nas a decimal number not\ + \ beginning with '0' or as a comma-separated list of flag names.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--excl_flags" + alternatives: + - "-F" + description: "Do not output alignments with any bits set in FLAG present in the\ + \ FLAG field. FLAG can be specified\nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/),\ + \ in octal by beginning with `0' (i.e. /^0[0-7]+/),\nas a decimal number not\ + \ beginning with '0' or as a comma-separated list of flag names.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--excl_all_flags" + alternatives: + - "-G" + description: "Do not output alignments with all bits set in INT present in the\ + \ FLAG field. This is the opposite of\n--require_flags such that --require_flags\ + \ 12 --exclude_all_flags 12 is the same as no filtering at all.\nFLAG can be\ + \ specified in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by\ + \ beginning with `0'\n(i.e. /^0[0-7]+/), as a decimal number not beginning with\ + \ '0' or as a comma-separated list of flag names.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--incl_flags" + alternatives: + - "--rf" + description: "Only output alignments with any bit set in FLAG present in the FLAG\ + \ field. FLAG can be specified in hex\nby beginning with `0x' (i.e. /^0x[0-9A-F]+/),\ + \ in octal by beginning with `0' (i.e. /^0[0-7]+/), as a decimal\nnumber not\ + \ beginning with '0' or as a comma-separated list of flag names.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--remove_tag" + alternatives: + - "-x" + description: "Read tag(s) to exclude from output (repeatable) [null]. This can\ + \ be a single tag or a comma separated list.\nAlternatively the option itself\ + \ can be repeated multiple times.\nIf the list starts with a `^' then it is\ + \ negated and treated as a request to remove all tags except those in STR.\n\ + The list may be empty, so --remove_tag ^ will remove all tags.\nNote that tags\ + \ will only be removed from reads that pass filtering.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--keep_tag" + description: "This keeps only tags listed in STR and is directly equivalent to\ + \ --remove_tag ^STR. Specifying an empty list\nwill remove all tags. If both\ + \ --keep_tag and --remove_tag are specified then --keep_tag has precedence.\n\ + Note that tags will only be removed from reads that pass filtering.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--remove_B" + alternatives: + - "-B" + description: "Collapse the backward CIGAR operation.\n" + info: null + direction: "input" + - type: "string" + name: "--add_flags" + description: "Adds flag(s) to read. FLAG can be specified in hex by beginning\ + \ with `0x' (i.e. /^0x[0-9A-F]+/), in octal\nby beginning with `0' (i.e. /^0[0-7]+/),\ + \ as a decimal number not beginning with '0' or as a comma-separated\nlist of\ + \ flag names.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--remove_flags" + description: "Remove flag(s) from read. FLAG is specified in the same way as with\ + \ the --add_flags option.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--subsample" + description: "Output only a proportion of the input alignments, as specified by\ + \ 0.0 <= FLOAT <= 1.0, which gives the fraction\nof templates/pairs to be kept.\ + \ This subsampling acts in the same way on all of the alignment records in the\ + \ same\ntemplate or read pair, so it never keeps a read but not its mate.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--subsample_seed" + description: "Subsampling seed used to influence which subset of reads is kept.\ + \ When subsampling data that has previously\nbeen subsampled, be sure to use\ + \ a different seed value from those used previously; otherwise more reads will\n\ + be retained than expected.\n" + info: null + default: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--fetch_pairs" + alternatives: + - "-P" + description: "Retrieve pairs even when the mate is outside of the requested region.\ + \ Enabling this option also turns on the\nmulti-region iterator (-M). A region\ + \ to search must be specified, either on the command-line, or using the\n--target_file\ + \ option. The input file must be an indexed regular file.\nThis option first\ + \ scans the requested region, using the RNEXT and PNEXT fields of the records\ + \ that have the\nPAIRED flag set and pass other filtering options to find where\ + \ paired reads are located. These locations are\nused to build an expanded region\ + \ list, and a set of QNAMEs to allow from the new regions. It will then make\n\ + a second pass, collecting all reads from the originally-specified region list\ + \ together with reads from additional\nlocations that match the allowed set\ + \ of QNAMEs. Any other filtering options used will be applied to all reads\n\ + found during this second pass.\nAs this option links reads using RNEXT and PNEXT,\ + \ it is important that these fields are set accurately. Use\n'samtools fixmate'\ + \ to correct them if necessary.\nNote that this option does not work with the\ + \ --count, --output-unselected or --unmap options.\n" + info: null + direction: "input" + - type: "boolean_true" + name: "--customized_index" + alternatives: + - "-X" + description: "Include customized index file as a part of arguments. See EXAMPLES\ + \ section for sample of usage.\n" + info: null + direction: "input" + - type: "string" + name: "--sanitize" + alternatives: + - "-z" + description: "Perform some sanity checks on the state of SAM record fields, fixing\ + \ up common mistakes made by aligners.\nThese include soft-clipping alignments\ + \ when they extend beyond the end of the reference, marking records as\nunmapped\ + \ when they have reference * or position 0, and ensuring unmapped alignments\ + \ have no CIGAR or mapping\nquality for unmapped alignments and no MD, NM, CG\ + \ or SM tags.\nFLAGs is a comma-separated list of keywords chosen from the following\ + \ list.\n\nunmap: The UNMAPPED BAM flag. This is set for reads with position\ + \ <= 0, reference name \"*\" or reads starting\nbeyond the end of the reference.\ + \ Note CIGAR \"*\" is permitted for mapped data so does not trigger this.\n\n\ + pos: Position and reference name fields. These may be cleared when a sequence\ + \ is unmapped due to the\ncoordinates being beyond the end of the reference.\ + \ Selecting this may change the sort order of the file,\nso it is not a part\ + \ of the on compound argument.\nmqual: Mapping quality. This is set to zero\ + \ for unmapped reads.\ncigar: Modifies CIGAR fields, either by adding soft-clips\ + \ for reads that overlap the end of the reference or\n by clearing it\ + \ for unmapped reads.\naux: For unmapped data, some auxiliary fields are meaningless\ + \ and will be removed. These include NM, MD, CG and SM.\noff: Perform no sanity\ + \ fixing. This is the default\non: Sanitize data in a way that guarantees the\ + \ same sort order. This is everything except for pos.\nall: All sanitizing options,\ + \ including pos.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--no_PG" + description: "Do not add a @PG line to the header of the output file.\n" + info: null + direction: "input" + - type: "string" + name: "--input_fmt_option" + description: "Specify a single input file format option in the form of OPTION\ + \ or OPTION=VALUE.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt" + alternatives: + - "-O" + description: "Specify output format (SAM, BAM, CRAM).\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--output_fmt_option" + description: "Specify a single output file format option in the form of OPTION\ + \ or OPTION=VALUE.\n" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "boolean_true" + name: "--write_index" + description: "Automatically index the output files.\n" + info: null + direction: "input" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Views and converts SAM/BAM/CRAM files." +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +- type: "file" + path: "test_data" +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "view" +- "convert" +- "bam" +- "sam" +- "cram" +license: "MIT/Expat" +references: + doi: + - "10.1093/bioinformatics/btp352" + - "10.1093/gigascience/giab008" +links: + repository: "https://github.com/samtools/samtools" + homepage: "https://www.htslib.org/" + documentation: "https://www.htslib.org/doc/samtools-view.html" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\nsed 's#Using\ + \ ##;s# \\([0-9\\.]*\\)$#: \\1#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/samtools/samtools_view/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/samtools/samtools_view" + executable: "target/nextflow/samtools/samtools_view/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/samtools/samtools_view/main.nf b/target/nextflow/samtools/samtools_view/main.nf new file mode 100644 index 00000000..d00e913b --- /dev/null +++ b/target/nextflow/samtools/samtools_view/main.nf @@ -0,0 +1,4078 @@ +// samtools_view main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "samtools_view", + "namespace" : "samtools", + "version" : "main", + "argument_groups" : [ + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "description" : "Input SAM, BAM, or CRAM file.", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--fai_reference", + "alternatives" : [ + "-t" + ], + "description" : "A tab-delimited FILE. Each line must contain the reference name in the first column\nand the length of the reference in the second column, with one line for each distinct\nreference. Any additional fields beyond the second column are ignored. This file also\ndefines the order of the reference sequences in sorting. If you run: `samtools faidx ',\nthe resulting index file .fai can be used as this FILE.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--reference", + "alternatives" : [ + "-T" + ], + "description" : "A FASTA format reference FILE, optionally compressed by bgzip and ideally indexed by samtools faidx.\nIf an index is not present one will be generated for you, if the reference file is local.\nIf the reference file is not local, but is accessed instead via an https://, s3:// or other URL,\nthe index file will need to be supplied by the server alongside the reference. It is possible to\nhave the reference and index files in different locations by supplying both to this option separated\nby the string \\"##idx##\\", for example:\n--reference ftp://x.com/ref.fa##idx##ftp://y.com/index.fa.fai\nHowever, note that only the location of the reference will be stored in the output file header.\nIf this method is used to make CRAM files, the cram reader may not be able to find the index,\nand may not be able to decode the file unless it can get the references it needs using a different\nmethod.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--target_file", + "alternatives" : [ + "-L" + ], + "description" : "Only output alignments overlapping the input BED FILE [null].\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--region_file", + "description" : "Use an index and multi-region iterator to only output alignments overlapping the input BED FILE.\nEquivalent to --use_index --target_file FILE.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--qname_file", + "alternatives" : [ + "-N" + ], + "description" : "Output only alignments with read names listed in FILE. If FILE starts with ^ then the operation is\nnegated and only outputs alignment with read groups not listed in FILE. It is not permissible to mix\nboth the filter-in and filter-out style syntax in the same command.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--read_group_file", + "alternatives" : [ + "-R" + ], + "description" : "Output alignments in read groups listed in FILE [null]. If FILE starts with ^ then the operation is\nnegated and only outputs alignment with read names not listed in FILE. It is not permissible to mix\nboth the filter-in and filter-out style syntax in the same command. Note that records with no RG tag\nwill also be output when using this option. This behaviour may change in a future release.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--use_index", + "alternatives" : [ + "-M" + ], + "description" : "Use the multi-region iterator on the union of a BED file and command-line region arguments.\nThis avoids re-reading the same regions of files so can sometimes be much faster. Note this also\nremoves duplicate sequences. Without this a sequence that overlaps multiple regions specified on\nthe command line will be reported multiple times. The usage of a BED file is optional and its path\nhas to be preceded by --target_file option.\n", + "direction" : "input" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--output", + "alternatives" : [ + "-o" + ], + "description" : "Output to FILE instead of [stdout].", + "example" : [ + "output.bam" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--bam", + "alternatives" : [ + "-b" + ], + "description" : "Output in the BAM format.", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--cram", + "alternatives" : [ + "-C" + ], + "description" : "Output in the CRAM format (requires --reference).\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--fast", + "description" : "Enable fast compression. This also changes the default output format to BAM,\nbut this can be overridden by the explicit format options or using a filename\nwith a known suffix.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--uncompressed", + "alternatives" : [ + "-u" + ], + "description" : "Output uncompressed data. This also changes the default output format to BAM,\nbut this can be overridden by the explicit format options or using a filename\nwith a known suffix.\nThis option saves time spent on compression/decompression and is thus preferred\nwhen the output is piped to another samtools command.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--with_header", + "description" : "Include the header in the output.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--header_only", + "alternatives" : [ + "-H" + ], + "description" : "Output the header only.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--no_header", + "description" : "When producing SAM format, output alignment records but not headers.\nThis is the default; the option can be used to reset the effect of \n--with_header/--header_only.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--count", + "alternatives" : [ + "-c" + ], + "description" : "Instead of printing the alignments, only count them and print the total number.\nAll filter options, such as --require_flags, --excl_flags, and --min_MQ, are taken\ninto account. The --unmap option is ignored in this mode.\n", + "direction" : "input" + }, + { + "type" : "file", + "name" : "--output_unselected", + "alternatives" : [ + "-U" + ], + "description" : "Write alignments that are not selected by the various filter options to FILE.\nWhen this option is used, all alignments (or all alignments intersecting the regions\nspecified) are written to either the output file or this file, but never both.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--unmap", + "alternatives" : [ + "-p" + ], + "description" : "Set the UNMAP flag on alignments that are not selected by the filter options.\nThese alignments are then written to the normal output. This is not compatible\nwith --output_unselected.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--read_group", + "alternatives" : [ + "-r" + ], + "description" : "Output alignments in read group STR [null]. Note that records with no RG tag will also be output\nwhen using this option. This behaviour may change in a future release.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--tag", + "alternatives" : [ + "-d" + ], + "description" : "Only output alignments with tag STR1 and associated value STR2, which can be a string or an integer\n[null].\nThe value can be omitted, in which case only the tag is considered.\nNote that this option does not specify a tag type. For example, use --tag XX:42 to select alignments\nwith an XX:i:42 field, not --tag XX:i:42.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--tag_file", + "alternatives" : [ + "-D" + ], + "description" : "Only output alignments with tag STR and associated values listed in FILE.\n", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_MQ", + "alternatives" : [ + "-q" + ], + "description" : "Skip alignments with MAPQ smaller than INT.\n", + "default" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--library", + "alternatives" : [ + "-l" + ], + "description" : "Only output alignments in library STR.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--min_qlen", + "alternatives" : [ + "-m" + ], + "description" : "Only output alignments with number of CIGAR bases consuming query sequence >= INT.\n", + "default" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--expr", + "alternatives" : [ + "-e" + ], + "description" : "Only include alignments that match the filter expression STR. The syntax for these expressions is\ndescribed in the main samtools.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--require_flags", + "alternatives" : [ + "-f" + ], + "description" : "Only output alignments with all bits set in FLAG present in the FLAG field. FLAG can be specified\nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0' (i.e. /^0[0-7]+/),\nas a decimal number not beginning with '0' or as a comma-separated list of flag names.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--excl_flags", + "alternatives" : [ + "-F" + ], + "description" : "Do not output alignments with any bits set in FLAG present in the FLAG field. FLAG can be specified\nin hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0' (i.e. /^0[0-7]+/),\nas a decimal number not beginning with '0' or as a comma-separated list of flag names.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--excl_all_flags", + "alternatives" : [ + "-G" + ], + "description" : "Do not output alignments with all bits set in INT present in the FLAG field. This is the opposite of\n--require_flags such that --require_flags 12 --exclude_all_flags 12 is the same as no filtering at all.\nFLAG can be specified in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0'\n(i.e. /^0[0-7]+/), as a decimal number not beginning with '0' or as a comma-separated list of flag names.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--incl_flags", + "alternatives" : [ + "--rf" + ], + "description" : "Only output alignments with any bit set in FLAG present in the FLAG field. FLAG can be specified in hex\nby beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0' (i.e. /^0[0-7]+/), as a decimal\nnumber not beginning with '0' or as a comma-separated list of flag names.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--remove_tag", + "alternatives" : [ + "-x" + ], + "description" : "Read tag(s) to exclude from output (repeatable) [null]. This can be a single tag or a comma separated list.\nAlternatively the option itself can be repeated multiple times.\nIf the list starts with a `^' then it is negated and treated as a request to remove all tags except those in STR.\nThe list may be empty, so --remove_tag ^ will remove all tags.\nNote that tags will only be removed from reads that pass filtering.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--keep_tag", + "description" : "This keeps only tags listed in STR and is directly equivalent to --remove_tag ^STR. Specifying an empty list\nwill remove all tags. If both --keep_tag and --remove_tag are specified then --keep_tag has precedence.\nNote that tags will only be removed from reads that pass filtering.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--remove_B", + "alternatives" : [ + "-B" + ], + "description" : "Collapse the backward CIGAR operation.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--add_flags", + "description" : "Adds flag(s) to read. FLAG can be specified in hex by beginning with `0x' (i.e. /^0x[0-9A-F]+/), in octal\nby beginning with `0' (i.e. /^0[0-7]+/), as a decimal number not beginning with '0' or as a comma-separated\nlist of flag names.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--remove_flags", + "description" : "Remove flag(s) from read. FLAG is specified in the same way as with the --add_flags option.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--subsample", + "description" : "Output only a proportion of the input alignments, as specified by 0.0 <= FLOAT <= 1.0, which gives the fraction\nof templates/pairs to be kept. This subsampling acts in the same way on all of the alignment records in the same\ntemplate or read pair, so it never keeps a read but not its mate.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--subsample_seed", + "description" : "Subsampling seed used to influence which subset of reads is kept. When subsampling data that has previously\nbeen subsampled, be sure to use a different seed value from those used previously; otherwise more reads will\nbe retained than expected.\n", + "default" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--fetch_pairs", + "alternatives" : [ + "-P" + ], + "description" : "Retrieve pairs even when the mate is outside of the requested region. Enabling this option also turns on the\nmulti-region iterator (-M). A region to search must be specified, either on the command-line, or using the\n--target_file option. The input file must be an indexed regular file.\nThis option first scans the requested region, using the RNEXT and PNEXT fields of the records that have the\nPAIRED flag set and pass other filtering options to find where paired reads are located. These locations are\nused to build an expanded region list, and a set of QNAMEs to allow from the new regions. It will then make\na second pass, collecting all reads from the originally-specified region list together with reads from additional\nlocations that match the allowed set of QNAMEs. Any other filtering options used will be applied to all reads\nfound during this second pass.\nAs this option links reads using RNEXT and PNEXT, it is important that these fields are set accurately. Use\n'samtools fixmate' to correct them if necessary.\nNote that this option does not work with the --count, --output-unselected or --unmap options.\n", + "direction" : "input" + }, + { + "type" : "boolean_true", + "name" : "--customized_index", + "alternatives" : [ + "-X" + ], + "description" : "Include customized index file as a part of arguments. See EXAMPLES section for sample of usage.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--sanitize", + "alternatives" : [ + "-z" + ], + "description" : "Perform some sanity checks on the state of SAM record fields, fixing up common mistakes made by aligners.\nThese include soft-clipping alignments when they extend beyond the end of the reference, marking records as\nunmapped when they have reference * or position 0, and ensuring unmapped alignments have no CIGAR or mapping\nquality for unmapped alignments and no MD, NM, CG or SM tags.\nFLAGs is a comma-separated list of keywords chosen from the following list.\n\nunmap: The UNMAPPED BAM flag. This is set for reads with position <= 0, reference name \\"*\\" or reads starting\nbeyond the end of the reference. Note CIGAR \\"*\\" is permitted for mapped data so does not trigger this.\n\npos: Position and reference name fields. These may be cleared when a sequence is unmapped due to the\ncoordinates being beyond the end of the reference. Selecting this may change the sort order of the file,\nso it is not a part of the on compound argument.\nmqual: Mapping quality. This is set to zero for unmapped reads.\ncigar: Modifies CIGAR fields, either by adding soft-clips for reads that overlap the end of the reference or\n by clearing it for unmapped reads.\naux: For unmapped data, some auxiliary fields are meaningless and will be removed. These include NM, MD, CG and SM.\noff: Perform no sanity fixing. This is the default\non: Sanitize data in a way that guarantees the same sort order. This is everything except for pos.\nall: All sanitizing options, including pos.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--no_PG", + "description" : "Do not add a @PG line to the header of the output file.\n", + "direction" : "input" + }, + { + "type" : "string", + "name" : "--input_fmt_option", + "description" : "Specify a single input file format option in the form of OPTION or OPTION=VALUE.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--output_fmt", + "alternatives" : [ + "-O" + ], + "description" : "Specify output format (SAM, BAM, CRAM).\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--output_fmt_option", + "description" : "Specify a single output file format option in the form of OPTION or OPTION=VALUE.\n", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "boolean_true", + "name" : "--write_index", + "description" : "Automatically index the output files.\n", + "direction" : "input" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Views and converts SAM/BAM/CRAM files.", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + }, + { + "type" : "file", + "path" : "test_data" + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "view", + "convert", + "bam", + "sam", + "cram" + ], + "license" : "MIT/Expat", + "references" : { + "doi" : [ + "10.1093/bioinformatics/btp352", + "10.1093/gigascience/giab008" + ] + }, + "links" : { + "repository" : "https://github.com/samtools/samtools", + "homepage" : "https://www.htslib.org/", + "documentation" : "https://www.htslib.org/doc/samtools-view.html" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "quay.io/biocontainers/samtools:1.19.2--h50ea8bc_1", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "samtools --version 2>&1 | grep -E '^(samtools|Using htslib)' | \\\\\nsed 's#Using ##;s# \\\\([0-9\\\\.]*\\\\)$#: \\\\1#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/samtools/samtools_view/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/samtools/samtools_view", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "${VIASH_PAR_INPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input='&'#" ; else echo "# par_input="; fi ) +$( if [ ! -z ${VIASH_PAR_FAI_REFERENCE+x} ]; then echo "${VIASH_PAR_FAI_REFERENCE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fai_reference='&'#" ; else echo "# par_fai_reference="; fi ) +$( if [ ! -z ${VIASH_PAR_REFERENCE+x} ]; then echo "${VIASH_PAR_REFERENCE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_reference='&'#" ; else echo "# par_reference="; fi ) +$( if [ ! -z ${VIASH_PAR_TARGET_FILE+x} ]; then echo "${VIASH_PAR_TARGET_FILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_target_file='&'#" ; else echo "# par_target_file="; fi ) +$( if [ ! -z ${VIASH_PAR_REGION_FILE+x} ]; then echo "${VIASH_PAR_REGION_FILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_region_file='&'#" ; else echo "# par_region_file="; fi ) +$( if [ ! -z ${VIASH_PAR_QNAME_FILE+x} ]; then echo "${VIASH_PAR_QNAME_FILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_qname_file='&'#" ; else echo "# par_qname_file="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_GROUP_FILE+x} ]; then echo "${VIASH_PAR_READ_GROUP_FILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_read_group_file='&'#" ; else echo "# par_read_group_file="; fi ) +$( if [ ! -z ${VIASH_PAR_USE_INDEX+x} ]; then echo "${VIASH_PAR_USE_INDEX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_use_index='&'#" ; else echo "# par_use_index="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT+x} ]; then echo "${VIASH_PAR_OUTPUT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output='&'#" ; else echo "# par_output="; fi ) +$( if [ ! -z ${VIASH_PAR_BAM+x} ]; then echo "${VIASH_PAR_BAM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_bam='&'#" ; else echo "# par_bam="; fi ) +$( if [ ! -z ${VIASH_PAR_CRAM+x} ]; then echo "${VIASH_PAR_CRAM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_cram='&'#" ; else echo "# par_cram="; fi ) +$( if [ ! -z ${VIASH_PAR_FAST+x} ]; then echo "${VIASH_PAR_FAST}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fast='&'#" ; else echo "# par_fast="; fi ) +$( if [ ! -z ${VIASH_PAR_UNCOMPRESSED+x} ]; then echo "${VIASH_PAR_UNCOMPRESSED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_uncompressed='&'#" ; else echo "# par_uncompressed="; fi ) +$( if [ ! -z ${VIASH_PAR_WITH_HEADER+x} ]; then echo "${VIASH_PAR_WITH_HEADER}" | sed "s#'#'\\"'\\"'#g;s#.*#par_with_header='&'#" ; else echo "# par_with_header="; fi ) +$( if [ ! -z ${VIASH_PAR_HEADER_ONLY+x} ]; then echo "${VIASH_PAR_HEADER_ONLY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_header_only='&'#" ; else echo "# par_header_only="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_HEADER+x} ]; then echo "${VIASH_PAR_NO_HEADER}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_header='&'#" ; else echo "# par_no_header="; fi ) +$( if [ ! -z ${VIASH_PAR_COUNT+x} ]; then echo "${VIASH_PAR_COUNT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_count='&'#" ; else echo "# par_count="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_UNSELECTED+x} ]; then echo "${VIASH_PAR_OUTPUT_UNSELECTED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_unselected='&'#" ; else echo "# par_output_unselected="; fi ) +$( if [ ! -z ${VIASH_PAR_UNMAP+x} ]; then echo "${VIASH_PAR_UNMAP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_unmap='&'#" ; else echo "# par_unmap="; fi ) +$( if [ ! -z ${VIASH_PAR_READ_GROUP+x} ]; then echo "${VIASH_PAR_READ_GROUP}" | sed "s#'#'\\"'\\"'#g;s#.*#par_read_group='&'#" ; else echo "# par_read_group="; fi ) +$( if [ ! -z ${VIASH_PAR_TAG+x} ]; then echo "${VIASH_PAR_TAG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_tag='&'#" ; else echo "# par_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_TAG_FILE+x} ]; then echo "${VIASH_PAR_TAG_FILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_tag_file='&'#" ; else echo "# par_tag_file="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_MQ+x} ]; then echo "${VIASH_PAR_MIN_MQ}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_MQ='&'#" ; else echo "# par_min_MQ="; fi ) +$( if [ ! -z ${VIASH_PAR_LIBRARY+x} ]; then echo "${VIASH_PAR_LIBRARY}" | sed "s#'#'\\"'\\"'#g;s#.*#par_library='&'#" ; else echo "# par_library="; fi ) +$( if [ ! -z ${VIASH_PAR_MIN_QLEN+x} ]; then echo "${VIASH_PAR_MIN_QLEN}" | sed "s#'#'\\"'\\"'#g;s#.*#par_min_qlen='&'#" ; else echo "# par_min_qlen="; fi ) +$( if [ ! -z ${VIASH_PAR_EXPR+x} ]; then echo "${VIASH_PAR_EXPR}" | sed "s#'#'\\"'\\"'#g;s#.*#par_expr='&'#" ; else echo "# par_expr="; fi ) +$( if [ ! -z ${VIASH_PAR_REQUIRE_FLAGS+x} ]; then echo "${VIASH_PAR_REQUIRE_FLAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_require_flags='&'#" ; else echo "# par_require_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCL_FLAGS+x} ]; then echo "${VIASH_PAR_EXCL_FLAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_excl_flags='&'#" ; else echo "# par_excl_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_EXCL_ALL_FLAGS+x} ]; then echo "${VIASH_PAR_EXCL_ALL_FLAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_excl_all_flags='&'#" ; else echo "# par_excl_all_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_INCL_FLAGS+x} ]; then echo "${VIASH_PAR_INCL_FLAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_incl_flags='&'#" ; else echo "# par_incl_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_REMOVE_TAG+x} ]; then echo "${VIASH_PAR_REMOVE_TAG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_remove_tag='&'#" ; else echo "# par_remove_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_KEEP_TAG+x} ]; then echo "${VIASH_PAR_KEEP_TAG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_keep_tag='&'#" ; else echo "# par_keep_tag="; fi ) +$( if [ ! -z ${VIASH_PAR_REMOVE_B+x} ]; then echo "${VIASH_PAR_REMOVE_B}" | sed "s#'#'\\"'\\"'#g;s#.*#par_remove_B='&'#" ; else echo "# par_remove_B="; fi ) +$( if [ ! -z ${VIASH_PAR_ADD_FLAGS+x} ]; then echo "${VIASH_PAR_ADD_FLAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_add_flags='&'#" ; else echo "# par_add_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_REMOVE_FLAGS+x} ]; then echo "${VIASH_PAR_REMOVE_FLAGS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_remove_flags='&'#" ; else echo "# par_remove_flags="; fi ) +$( if [ ! -z ${VIASH_PAR_SUBSAMPLE+x} ]; then echo "${VIASH_PAR_SUBSAMPLE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_subsample='&'#" ; else echo "# par_subsample="; fi ) +$( if [ ! -z ${VIASH_PAR_SUBSAMPLE_SEED+x} ]; then echo "${VIASH_PAR_SUBSAMPLE_SEED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_subsample_seed='&'#" ; else echo "# par_subsample_seed="; fi ) +$( if [ ! -z ${VIASH_PAR_FETCH_PAIRS+x} ]; then echo "${VIASH_PAR_FETCH_PAIRS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_fetch_pairs='&'#" ; else echo "# par_fetch_pairs="; fi ) +$( if [ ! -z ${VIASH_PAR_CUSTOMIZED_INDEX+x} ]; then echo "${VIASH_PAR_CUSTOMIZED_INDEX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_customized_index='&'#" ; else echo "# par_customized_index="; fi ) +$( if [ ! -z ${VIASH_PAR_SANITIZE+x} ]; then echo "${VIASH_PAR_SANITIZE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sanitize='&'#" ; else echo "# par_sanitize="; fi ) +$( if [ ! -z ${VIASH_PAR_NO_PG+x} ]; then echo "${VIASH_PAR_NO_PG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_no_PG='&'#" ; else echo "# par_no_PG="; fi ) +$( if [ ! -z ${VIASH_PAR_INPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_INPUT_FMT_OPTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_input_fmt_option='&'#" ; else echo "# par_input_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_fmt='&'#" ; else echo "# par_output_fmt="; fi ) +$( if [ ! -z ${VIASH_PAR_OUTPUT_FMT_OPTION+x} ]; then echo "${VIASH_PAR_OUTPUT_FMT_OPTION}" | sed "s#'#'\\"'\\"'#g;s#.*#par_output_fmt_option='&'#" ; else echo "# par_output_fmt_option="; fi ) +$( if [ ! -z ${VIASH_PAR_WRITE_INDEX+x} ]; then echo "${VIASH_PAR_WRITE_INDEX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_write_index='&'#" ; else echo "# par_write_index="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +set -e + +[[ "\\$par_bam" == "false" ]] && unset par_bam +[[ "\\$par_cram" == "false" ]] && unset par_cram +[[ "\\$par_fast" == "false" ]] && unset par_fast +[[ "\\$par_uncompressed" == "false" ]] && unset par_uncompressed +[[ "\\$par_with_header" == "false" ]] && unset par_with_header +[[ "\\$par_header_only" == "false" ]] && unset par_header_only +[[ "\\$par_no_header" == "false" ]] && unset par_no_header +[[ "\\$par_count" == "false" ]] && unset par_count +[[ "\\$par_unmap" == "false" ]] && unset par_unmap +[[ "\\$par_use_index" == "false" ]] && unset par_use_index +[[ "\\$par_fetch_pairs" == "false" ]] && unset par_fetch_pairs +[[ "\\$par_customized_index" == "false" ]] && unset par_customized_index +[[ "\\$par_no_PG" == "false" ]] && unset par_no_PG +[[ "\\$par_write_index" == "false" ]] && unset par_write_index +[[ "\\$par_remove_B" == "false" ]] && unset par_remove_B + +samtools view \\\\ + \\${par_bam:+-b} \\\\ + \\${par_cram:+-C} \\\\ + \\${par_fast:+--fast} \\\\ + \\${par_uncompressed:+-u} \\\\ + \\${par_with_header:+--with-header} \\\\ + \\${par_header_only:+-H} \\\\ + \\${par_no_header:+--no-header} \\\\ + \\${par_count:+-c} \\\\ + \\${par_output:+-o "\\$par_output"} \\\\ + \\${par_output_unselected:+-U "\\$par_output_unselected"} \\\\ + \\${par_unmap:+-p "\\$par_unmap"} \\\\ + \\${par_fetch_pairs:+-P "\\$par_fetch_pairs"} \\\\ + \\${par_fai_reference:+-t "\\$par_fai_reference"} \\\\ + \\${par_use_index:+-M "\\$par_use_index"} \\\\ + \\${par_region_file:+--region-file "\\$par_region_file"} \\\\ + \\${par_customized_index:+-X} \\\\ + \\${par_target_file:+-L "\\$par_target_file"} \\\\ + \\${par_qname_file:+-N "\\$par_qname_file"} \\\\ + \\${par_read_group:+-r "\\$par_read_group"} \\\\ + \\${par_read_group_file:+-R "\\$par_read_group_file"} \\\\ + \\${par_tag:+-d "\\$par_tag"} \\\\ + \\${par_tag_file:+-D "\\$par_tag_file"} \\\\ + \\${par_min_MQ:+-q "\\$par_min_MQ"} \\\\ + \\${par_library:+-l "\\$par_library"} \\\\ + \\${par_min_qlen:+-m "\\$par_min_qlen"} \\\\ + \\${par_expr:+-e "\\$par_expr"} \\\\ + \\${par_require_flags:+-f "\\$par_require_flags"} \\\\ + \\${par_excl_flags:+-F "\\$par_excl_flags"} \\\\ + \\${par_incl_flags:+--rf "\\$par_incl_flags"} \\\\ + \\${par_excl_all_flags:+-G "\\$par_excl_all_flags"} \\\\ + \\${par_subsample:+--subsample "\\$par_subsample"} \\\\ + \\${par_subsample_seed:+--subsample-seed "\\$par_subsample_seed"} \\\\ + \\${par_add_flags:+--add-flags "\\$par_add_flags"} \\\\ + \\${par_remove_flags:+--remove-flags "\\$par_remove_flags"} \\\\ + \\${par_remove_tag:+-x "\\$par_remove_tag"} \\\\ + \\${par_keep_tag:+--keep-tag "\\$par_keep_tag"} \\\\ + \\${par_remove_B:+-B} \\\\ + \\${par_sanitize:+-z "\\$par_sanitize"} \\\\ + \\${par_input_fmt_option:+--input-fmt-option "\\$par_input_fmt_option"} \\\\ + \\${par_output_fmt:+-O "\\$par_output_fmt"} \\\\ + \\${par_output_fmt_option:+--output-fmt-option "\\$par_output_fmt_option"} \\\\ + \\${par_reference:+-T "\\$par_reference"} \\\\ + \\${par_write_index:+--write-index} \\\\ + \\${par_no_PG:+--no-PG} \\\\ + "\\$par_input" + +exit 0 +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/samtools/samtools_view", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/samtools/samtools_view/nextflow.config b/target/nextflow/samtools/samtools_view/nextflow.config new file mode 100644 index 00000000..f7e8defc --- /dev/null +++ b/target/nextflow/samtools/samtools_view/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'samtools/samtools_view' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Views and converts SAM/BAM/CRAM files.' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/samtools/samtools_view/nextflow_schema.json b/target/nextflow/samtools/samtools_view/nextflow_schema.json new file mode 100644 index 00000000..1de1003b --- /dev/null +++ b/target/nextflow/samtools/samtools_view/nextflow_schema.json @@ -0,0 +1,543 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "samtools_view", +"description": "Views and converts SAM/BAM/CRAM files.", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: `file`, required. Input SAM, BAM, or CRAM file", + "help_text": "Type: `file`, required. Input SAM, BAM, or CRAM file." + + } + + + , + "fai_reference": { + "type": + "string", + "description": "Type: `file`. A tab-delimited FILE", + "help_text": "Type: `file`. A tab-delimited FILE. Each line must contain the reference name in the first column\nand the length of the reference in the second column, with one line for each distinct\nreference. Any additional fields beyond the second column are ignored. This file also\ndefines the order of the reference sequences in sorting. If you run: `samtools faidx \u003cref.fa\u003e\u0027,\nthe resulting index file \u003cref.fa\u003e.fai can be used as this FILE.\n" + + } + + + , + "reference": { + "type": + "string", + "description": "Type: `file`. A FASTA format reference FILE, optionally compressed by bgzip and ideally indexed by samtools faidx", + "help_text": "Type: `file`. A FASTA format reference FILE, optionally compressed by bgzip and ideally indexed by samtools faidx.\nIf an index is not present one will be generated for you, if the reference file is local.\nIf the reference file is not local, but is accessed instead via an https://, s3:// or other URL,\nthe index file will need to be supplied by the server alongside the reference. It is possible to\nhave the reference and index files in different locations by supplying both to this option separated\nby the string \"##idx##\", for example:\n--reference ftp://x.com/ref.fa##idx##ftp://y.com/index.fa.fai\nHowever, note that only the location of the reference will be stored in the output file header.\nIf this method is used to make CRAM files, the cram reader may not be able to find the index,\nand may not be able to decode the file unless it can get the references it needs using a different\nmethod.\n" + + } + + + , + "target_file": { + "type": + "string", + "description": "Type: `file`. Only output alignments overlapping the input BED FILE [null]", + "help_text": "Type: `file`. Only output alignments overlapping the input BED FILE [null].\n" + + } + + + , + "region_file": { + "type": + "string", + "description": "Type: `file`. Use an index and multi-region iterator to only output alignments overlapping the input BED FILE", + "help_text": "Type: `file`. Use an index and multi-region iterator to only output alignments overlapping the input BED FILE.\nEquivalent to --use_index --target_file FILE.\n" + + } + + + , + "qname_file": { + "type": + "string", + "description": "Type: `file`. Output only alignments with read names listed in FILE", + "help_text": "Type: `file`. Output only alignments with read names listed in FILE. If FILE starts with ^ then the operation is\nnegated and only outputs alignment with read groups not listed in FILE. It is not permissible to mix\nboth the filter-in and filter-out style syntax in the same command.\n" + + } + + + , + "read_group_file": { + "type": + "string", + "description": "Type: `file`. Output alignments in read groups listed in FILE [null]", + "help_text": "Type: `file`. Output alignments in read groups listed in FILE [null]. If FILE starts with ^ then the operation is\nnegated and only outputs alignment with read names not listed in FILE. It is not permissible to mix\nboth the filter-in and filter-out style syntax in the same command. Note that records with no RG tag\nwill also be output when using this option. This behaviour may change in a future release.\n" + + } + + + , + "use_index": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Use the multi-region iterator on the union of a BED file and command-line region arguments", + "help_text": "Type: `boolean_true`, default: `false`. Use the multi-region iterator on the union of a BED file and command-line region arguments.\nThis avoids re-reading the same regions of files so can sometimes be much faster. Note this also\nremoves duplicate sequences. Without this a sequence that overlaps multiple regions specified on\nthe command line will be reported multiple times. The usage of a BED file is optional and its path\nhas to be preceded by --target_file option.\n" + , + "default": "False" + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "output": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.output.bam`, example: `output.bam`. Output to FILE instead of [stdout]", + "help_text": "Type: `file`, required, default: `$id.$key.output.bam`, example: `output.bam`. Output to FILE instead of [stdout]." + , + "default": "$id.$key.output.bam" + } + + + , + "bam": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output in the BAM format", + "help_text": "Type: `boolean_true`, default: `false`. Output in the BAM format." + , + "default": "False" + } + + + , + "cram": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output in the CRAM format (requires --reference)", + "help_text": "Type: `boolean_true`, default: `false`. Output in the CRAM format (requires --reference).\n" + , + "default": "False" + } + + + , + "fast": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Enable fast compression", + "help_text": "Type: `boolean_true`, default: `false`. Enable fast compression. This also changes the default output format to BAM,\nbut this can be overridden by the explicit format options or using a filename\nwith a known suffix.\n" + , + "default": "False" + } + + + , + "uncompressed": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output uncompressed data", + "help_text": "Type: `boolean_true`, default: `false`. Output uncompressed data. This also changes the default output format to BAM,\nbut this can be overridden by the explicit format options or using a filename\nwith a known suffix.\nThis option saves time spent on compression/decompression and is thus preferred\nwhen the output is piped to another samtools command.\n" + , + "default": "False" + } + + + , + "with_header": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Include the header in the output", + "help_text": "Type: `boolean_true`, default: `false`. Include the header in the output.\n" + , + "default": "False" + } + + + , + "header_only": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Output the header only", + "help_text": "Type: `boolean_true`, default: `false`. Output the header only.\n" + , + "default": "False" + } + + + , + "no_header": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. When producing SAM format, output alignment records but not headers", + "help_text": "Type: `boolean_true`, default: `false`. When producing SAM format, output alignment records but not headers.\nThis is the default; the option can be used to reset the effect of \n--with_header/--header_only.\n" + , + "default": "False" + } + + + , + "count": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Instead of printing the alignments, only count them and print the total number", + "help_text": "Type: `boolean_true`, default: `false`. Instead of printing the alignments, only count them and print the total number.\nAll filter options, such as --require_flags, --excl_flags, and --min_MQ, are taken\ninto account. The --unmap option is ignored in this mode.\n" + , + "default": "False" + } + + + , + "output_unselected": { + "type": + "string", + "description": "Type: `file`. Write alignments that are not selected by the various filter options to FILE", + "help_text": "Type: `file`. Write alignments that are not selected by the various filter options to FILE.\nWhen this option is used, all alignments (or all alignments intersecting the regions\nspecified) are written to either the output file or this file, but never both.\n" + + } + + + , + "unmap": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Set the UNMAP flag on alignments that are not selected by the filter options", + "help_text": "Type: `boolean_true`, default: `false`. Set the UNMAP flag on alignments that are not selected by the filter options.\nThese alignments are then written to the normal output. This is not compatible\nwith --output_unselected.\n" + , + "default": "False" + } + + + , + "read_group": { + "type": + "string", + "description": "Type: `string`. Output alignments in read group STR [null]", + "help_text": "Type: `string`. Output alignments in read group STR [null]. Note that records with no RG tag will also be output\nwhen using this option. This behaviour may change in a future release.\n" + + } + + + , + "tag": { + "type": + "string", + "description": "Type: `string`. Only output alignments with tag STR1 and associated value STR2, which can be a string or an integer\n[null]", + "help_text": "Type: `string`. Only output alignments with tag STR1 and associated value STR2, which can be a string or an integer\n[null].\nThe value can be omitted, in which case only the tag is considered.\nNote that this option does not specify a tag type. For example, use --tag XX:42 to select alignments\nwith an XX:i:42 field, not --tag XX:i:42.\n" + + } + + + , + "tag_file": { + "type": + "string", + "description": "Type: `file`. Only output alignments with tag STR and associated values listed in FILE", + "help_text": "Type: `file`. Only output alignments with tag STR and associated values listed in FILE.\n" + + } + + + , + "min_MQ": { + "type": + "integer", + "description": "Type: `integer`, default: `0`. Skip alignments with MAPQ smaller than INT", + "help_text": "Type: `integer`, default: `0`. Skip alignments with MAPQ smaller than INT.\n" + , + "default": "0" + } + + + , + "library": { + "type": + "string", + "description": "Type: `string`. Only output alignments in library STR", + "help_text": "Type: `string`. Only output alignments in library STR.\n" + + } + + + , + "min_qlen": { + "type": + "integer", + "description": "Type: `integer`, default: `0`. Only output alignments with number of CIGAR bases consuming query sequence \u003e= INT", + "help_text": "Type: `integer`, default: `0`. Only output alignments with number of CIGAR bases consuming query sequence \u003e= INT.\n" + , + "default": "0" + } + + + , + "expr": { + "type": + "string", + "description": "Type: `string`. Only include alignments that match the filter expression STR", + "help_text": "Type: `string`. Only include alignments that match the filter expression STR. The syntax for these expressions is\ndescribed in the main samtools.\n" + + } + + + , + "require_flags": { + "type": + "string", + "description": "Type: `string`. Only output alignments with all bits set in FLAG present in the FLAG field", + "help_text": "Type: `string`. Only output alignments with all bits set in FLAG present in the FLAG field. FLAG can be specified\nin hex by beginning with `0x\u0027 (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0\u0027 (i.e. /^0[0-7]+/),\nas a decimal number not beginning with \u00270\u0027 or as a comma-separated list of flag names.\n" + + } + + + , + "excl_flags": { + "type": + "string", + "description": "Type: `string`. Do not output alignments with any bits set in FLAG present in the FLAG field", + "help_text": "Type: `string`. Do not output alignments with any bits set in FLAG present in the FLAG field. FLAG can be specified\nin hex by beginning with `0x\u0027 (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0\u0027 (i.e. /^0[0-7]+/),\nas a decimal number not beginning with \u00270\u0027 or as a comma-separated list of flag names.\n" + + } + + + , + "excl_all_flags": { + "type": + "integer", + "description": "Type: `integer`. Do not output alignments with all bits set in INT present in the FLAG field", + "help_text": "Type: `integer`. Do not output alignments with all bits set in INT present in the FLAG field. This is the opposite of\n--require_flags such that --require_flags 12 --exclude_all_flags 12 is the same as no filtering at all.\nFLAG can be specified in hex by beginning with `0x\u0027 (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0\u0027\n(i.e. /^0[0-7]+/), as a decimal number not beginning with \u00270\u0027 or as a comma-separated list of flag names.\n" + + } + + + , + "incl_flags": { + "type": + "string", + "description": "Type: `string`. Only output alignments with any bit set in FLAG present in the FLAG field", + "help_text": "Type: `string`. Only output alignments with any bit set in FLAG present in the FLAG field. FLAG can be specified in hex\nby beginning with `0x\u0027 (i.e. /^0x[0-9A-F]+/), in octal by beginning with `0\u0027 (i.e. /^0[0-7]+/), as a decimal\nnumber not beginning with \u00270\u0027 or as a comma-separated list of flag names.\n" + + } + + + , + "remove_tag": { + "type": + "string", + "description": "Type: `string`. Read tag(s) to exclude from output (repeatable) [null]", + "help_text": "Type: `string`. Read tag(s) to exclude from output (repeatable) [null]. This can be a single tag or a comma separated list.\nAlternatively the option itself can be repeated multiple times.\nIf the list starts with a `^\u0027 then it is negated and treated as a request to remove all tags except those in STR.\nThe list may be empty, so --remove_tag ^ will remove all tags.\nNote that tags will only be removed from reads that pass filtering.\n" + + } + + + , + "keep_tag": { + "type": + "string", + "description": "Type: `string`. This keeps only tags listed in STR and is directly equivalent to --remove_tag ^STR", + "help_text": "Type: `string`. This keeps only tags listed in STR and is directly equivalent to --remove_tag ^STR. Specifying an empty list\nwill remove all tags. If both --keep_tag and --remove_tag are specified then --keep_tag has precedence.\nNote that tags will only be removed from reads that pass filtering.\n" + + } + + + , + "remove_B": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Collapse the backward CIGAR operation", + "help_text": "Type: `boolean_true`, default: `false`. Collapse the backward CIGAR operation.\n" + , + "default": "False" + } + + + , + "add_flags": { + "type": + "string", + "description": "Type: `string`. Adds flag(s) to read", + "help_text": "Type: `string`. Adds flag(s) to read. FLAG can be specified in hex by beginning with `0x\u0027 (i.e. /^0x[0-9A-F]+/), in octal\nby beginning with `0\u0027 (i.e. /^0[0-7]+/), as a decimal number not beginning with \u00270\u0027 or as a comma-separated\nlist of flag names.\n" + + } + + + , + "remove_flags": { + "type": + "string", + "description": "Type: `string`. Remove flag(s) from read", + "help_text": "Type: `string`. Remove flag(s) from read. FLAG is specified in the same way as with the --add_flags option.\n" + + } + + + , + "subsample": { + "type": + "number", + "description": "Type: `double`. Output only a proportion of the input alignments, as specified by 0", + "help_text": "Type: `double`. Output only a proportion of the input alignments, as specified by 0.0 \u003c= FLOAT \u003c= 1.0, which gives the fraction\nof templates/pairs to be kept. This subsampling acts in the same way on all of the alignment records in the same\ntemplate or read pair, so it never keeps a read but not its mate.\n" + + } + + + , + "subsample_seed": { + "type": + "integer", + "description": "Type: `integer`, default: `0`. Subsampling seed used to influence which subset of reads is kept", + "help_text": "Type: `integer`, default: `0`. Subsampling seed used to influence which subset of reads is kept. When subsampling data that has previously\nbeen subsampled, be sure to use a different seed value from those used previously; otherwise more reads will\nbe retained than expected.\n" + , + "default": "0" + } + + + , + "fetch_pairs": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Retrieve pairs even when the mate is outside of the requested region", + "help_text": "Type: `boolean_true`, default: `false`. Retrieve pairs even when the mate is outside of the requested region. Enabling this option also turns on the\nmulti-region iterator (-M). A region to search must be specified, either on the command-line, or using the\n--target_file option. The input file must be an indexed regular file.\nThis option first scans the requested region, using the RNEXT and PNEXT fields of the records that have the\nPAIRED flag set and pass other filtering options to find where paired reads are located. These locations are\nused to build an expanded region list, and a set of QNAMEs to allow from the new regions. It will then make\na second pass, collecting all reads from the originally-specified region list together with reads from additional\nlocations that match the allowed set of QNAMEs. Any other filtering options used will be applied to all reads\nfound during this second pass.\nAs this option links reads using RNEXT and PNEXT, it is important that these fields are set accurately. Use\n\u0027samtools fixmate\u0027 to correct them if necessary.\nNote that this option does not work with the --count, --output-unselected or --unmap options.\n" + , + "default": "False" + } + + + , + "customized_index": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Include customized index file as a part of arguments", + "help_text": "Type: `boolean_true`, default: `false`. Include customized index file as a part of arguments. See EXAMPLES section for sample of usage.\n" + , + "default": "False" + } + + + , + "sanitize": { + "type": + "string", + "description": "Type: `string`. Perform some sanity checks on the state of SAM record fields, fixing up common mistakes made by aligners", + "help_text": "Type: `string`. Perform some sanity checks on the state of SAM record fields, fixing up common mistakes made by aligners.\nThese include soft-clipping alignments when they extend beyond the end of the reference, marking records as\nunmapped when they have reference * or position 0, and ensuring unmapped alignments have no CIGAR or mapping\nquality for unmapped alignments and no MD, NM, CG or SM tags.\nFLAGs is a comma-separated list of keywords chosen from the following list.\n\nunmap: The UNMAPPED BAM flag. This is set for reads with position \u003c= 0, reference name \"*\" or reads starting\nbeyond the end of the reference. Note CIGAR \"*\" is permitted for mapped data so does not trigger this.\n\npos: Position and reference name fields. These may be cleared when a sequence is unmapped due to the\ncoordinates being beyond the end of the reference. Selecting this may change the sort order of the file,\nso it is not a part of the on compound argument.\nmqual: Mapping quality. This is set to zero for unmapped reads.\ncigar: Modifies CIGAR fields, either by adding soft-clips for reads that overlap the end of the reference or\n by clearing it for unmapped reads.\naux: For unmapped data, some auxiliary fields are meaningless and will be removed. These include NM, MD, CG and SM.\noff: Perform no sanity fixing. This is the default\non: Sanitize data in a way that guarantees the same sort order. This is everything except for pos.\nall: All sanitizing options, including pos.\n" + + } + + + , + "no_PG": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Do not add a @PG line to the header of the output file", + "help_text": "Type: `boolean_true`, default: `false`. Do not add a @PG line to the header of the output file.\n" + , + "default": "False" + } + + + , + "input_fmt_option": { + "type": + "string", + "description": "Type: `string`. Specify a single input file format option in the form of OPTION or OPTION=VALUE", + "help_text": "Type: `string`. Specify a single input file format option in the form of OPTION or OPTION=VALUE.\n" + + } + + + , + "output_fmt": { + "type": + "string", + "description": "Type: `string`. Specify output format (SAM, BAM, CRAM)", + "help_text": "Type: `string`. Specify output format (SAM, BAM, CRAM).\n" + + } + + + , + "output_fmt_option": { + "type": + "string", + "description": "Type: `string`. Specify a single output file format option in the form of OPTION or OPTION=VALUE", + "help_text": "Type: `string`. Specify a single output file format option in the form of OPTION or OPTION=VALUE.\n" + + } + + + , + "write_index": { + "type": + "boolean", + "description": "Type: `boolean_true`, default: `false`. Automatically index the output files", + "help_text": "Type: `boolean_true`, default: `false`. Automatically index the output files.\n" + , + "default": "False" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/star/star_align_reads/.config.vsh.yaml b/target/nextflow/star/star_align_reads/.config.vsh.yaml new file mode 100644 index 00000000..2bf8a55e --- /dev/null +++ b/target/nextflow/star/star_align_reads/.config.vsh.yaml @@ -0,0 +1,2121 @@ +name: "star_align_reads" +namespace: "star" +version: "main" +argument_groups: +- name: "Run Parameters" + arguments: + - type: "integer" + name: "--runRNGseed" + description: "random number generator seed." + info: null + example: + - 777 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Genome Parameters" + arguments: + - type: "file" + name: "--genomeDir" + description: "path to the directory where genome files are stored (for --runMode\ + \ alignReads) or will be generated (for --runMode generateGenome)" + info: null + example: + - "./GenomeDir" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--genomeLoad" + description: "mode of shared memory usage for the genome files. Only used with\ + \ --runMode alignReads.\n\n- LoadAndKeep ... load genome into shared and\ + \ keep it in memory after run\n- LoadAndRemove ... load genome into shared\ + \ but remove it after run\n- LoadAndExit ... load genome into shared memory\ + \ and exit, keeping the genome in memory for future runs\n- Remove \ + \ ... do not map anything, just remove loaded genome from memory\n- NoSharedMemory\ + \ ... do not use shared memory, each job will have its own private copy of\ + \ the genome" + info: null + example: + - "NoSharedMemory" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--genomeFastaFiles" + description: "path(s) to the fasta files with the genome sequences, separated\ + \ by spaces. These files should be plain text FASTA files, they *cannot* be\ + \ zipped.\n\nRequired for the genome generation (--runMode genomeGenerate).\ + \ Can also be used in the mapping (--runMode alignReads) to add extra (new)\ + \ sequences to the genome (e.g. spike-ins)." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--genomeFileSizes" + description: "genome files exact sizes in bytes. Typically, this should not be\ + \ defined by the user." + info: null + example: + - 0 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--genomeTransformOutput" + description: "which output to transform back to original genome\n\n- SAM ...\ + \ SAM/BAM alignments\n- SJ ... splice junctions (SJ.out.tab)\n- Quant \ + \ ... quantifications (from --quantMode option)\n- None ... no transformation\ + \ of the output" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--genomeChrSetMitochondrial" + description: "names of the mitochondrial chromosomes. Presently only used for\ + \ STARsolo statistics output/" + info: null + example: + - "chrM" + - "M" + - "MT" + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Splice Junctions Database" + arguments: + - type: "string" + name: "--sjdbFileChrStartEnd" + description: "path to the files with genomic coordinates (chr start \ + \ end strand) for the splice junction introns. Multiple files can be supplied\ + \ and will be concatenated." + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--sjdbGTFfile" + description: "path to the GTF file with annotations" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFchrPrefix" + description: "prefix for chromosome names in a GTF file (e.g. 'chr' for using\ + \ ENSMEBL annotations with UCSC genomes)" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFfeatureExon" + description: "feature type in GTF file to be used as exons for building transcripts" + info: null + example: + - "exon" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentTranscript" + description: "GTF attribute name for parent transcript ID (default \"transcript_id\"\ + \ works for GTF files)" + info: null + example: + - "transcript_id" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGene" + description: "GTF attribute name for parent gene ID (default \"gene_id\" works\ + \ for GTF files)" + info: null + example: + - "gene_id" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGeneName" + description: "GTF attribute name for parent gene name" + info: null + example: + - "gene_name" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGeneType" + description: "GTF attribute name for parent gene type" + info: null + example: + - "gene_type" + - "gene_biotype" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--sjdbOverhang" + description: "length of the donor/acceptor sequence on each side of the junctions,\ + \ ideally = (mate_length - 1)" + info: null + example: + - 100 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--sjdbScore" + description: "extra alignment score for alignments that cross database junctions" + info: null + example: + - 2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbInsertSave" + description: "which files to save when sjdb junctions are inserted on the fly\ + \ at the mapping step\n\n- Basic ... only small junction / transcript files\n\ + - All ... all files including big Genome, SA and SAindex - this will create\ + \ a complete genome directory" + info: null + example: + - "Basic" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Variation parameters" + arguments: + - type: "string" + name: "--varVCFfile" + description: "path to the VCF file that contains variation data. The 10th column\ + \ should contain the genotype information, e.g. 0/1" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Read Parameters" + arguments: + - type: "string" + name: "--readFilesType" + description: "format of input read files\n\n- Fastx ... FASTA or FASTQ\n\ + - SAM SE ... SAM or BAM single-end reads; for BAM use --readFilesCommand\ + \ samtools view\n- SAM PE ... SAM or BAM paired-end reads; for BAM use\ + \ --readFilesCommand samtools view" + info: null + example: + - "Fastx" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--readFilesSAMattrKeep" + description: "for --readFilesType SAM SE/PE, which SAM tags to keep in the output\ + \ BAM, e.g.: --readFilesSAMtagsKeep RG PL\n\n- All ... keep all tags\n-\ + \ None ... do not keep any tags" + info: null + example: + - "All" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--readFilesManifest" + description: "path to the \"manifest\" file with the names of read files. The\ + \ manifest file should contain 3 tab-separated columns:\n\npaired-end reads:\ + \ read1_file_name $tab$ read2_file_name $tab$ read_group_line.\nsingle-end reads:\ + \ read1_file_name $tab$ - $tab$ read_group_line.\nSpaces, but\ + \ not tabs are allowed in file names.\nIf read_group_line does not start with\ + \ ID:, it can only contain one ID field, and ID: will be added to it.\nIf read_group_line\ + \ starts with ID:, it can contain several fields separated by $tab$, and all\ + \ fields will be be copied verbatim into SAM @RG header line." + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--readFilesPrefix" + description: "prefix for the read files names, i.e. it will be added in front\ + \ of the strings in --readFilesIn" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--readFilesCommand" + description: "command line to execute for each of the input file. This command\ + \ should generate FASTA or FASTQ text and send it to stdout\n\nFor example:\ + \ zcat - to uncompress .gz files, bzcat - to uncompress .bz2 files, etc." + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--readMapNumber" + description: "number of reads to map from the beginning of the file\n\n-1: map\ + \ all reads" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--readMatesLengthsIn" + description: "Equal/NotEqual - lengths of names,sequences,qualities for both mates\ + \ are the same / not the same. NotEqual is safe in all situations." + info: null + example: + - "NotEqual" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--readNameSeparator" + description: "character(s) separating the part of the read names that will be\ + \ trimmed in output (read name after space is always trimmed)" + info: null + example: + - "/" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--readQualityScoreBase" + description: "number to be subtracted from the ASCII code to get Phred quality\ + \ score" + info: null + example: + - 33 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Read Clipping" + arguments: + - type: "string" + name: "--clipAdapterType" + description: "adapter clipping type\n\n- Hamming ... adapter clipping based on\ + \ Hamming distance, with the number of mismatches controlled by --clip5pAdapterMMp\n\ + - CellRanger4 ... 5p and 3p adapter clipping similar to CellRanger4. Utilizes\ + \ Opal package by Martin Sosic: https://github.com/Martinsos/opal\n- None ...\ + \ no adapter clipping, all other clip* parameters are disregarded" + info: null + example: + - "Hamming" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--clip3pNbases" + description: "number(s) of bases to clip from 3p of each mate. If one value is\ + \ given, it will be assumed the same for both mates." + info: null + example: + - 0 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--clip3pAdapterSeq" + description: "adapter sequences to clip from 3p of each mate. If one value is\ + \ given, it will be assumed the same for both mates.\n\n- polyA ... polyA sequence\ + \ with the length equal to read length" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "double" + name: "--clip3pAdapterMMp" + description: "max proportion of mismatches for 3p adapter clipping for each mate.\ + \ If one value is given, it will be assumed the same for both mates." + info: null + example: + - 0.1 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--clip3pAfterAdapterNbases" + description: "number of bases to clip from 3p of each mate after the adapter clipping.\ + \ If one value is given, it will be assumed the same for both mates." + info: null + example: + - 0 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--clip5pNbases" + description: "number(s) of bases to clip from 5p of each mate. If one value is\ + \ given, it will be assumed the same for both mates." + info: null + example: + - 0 + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Limits" + arguments: + - type: "long" + name: "--limitGenomeGenerateRAM" + description: "maximum available RAM (bytes) for genome generation" + info: null + example: + - 31000000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "long" + name: "--limitIObufferSize" + description: "max available buffers size (bytes) for input/output, per thread" + info: null + example: + - 30000000 + - 50000000 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "long" + name: "--limitOutSAMoneReadBytes" + description: "max size of the SAM record (bytes) for one read. Recommended value:\ + \ >(2*(LengthMate1+LengthMate2+100)*outFilterMultimapNmax" + info: null + example: + - 100000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--limitOutSJoneRead" + description: "max number of junctions for one read (including all multi-mappers)" + info: null + example: + - 1000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--limitOutSJcollapsed" + description: "max number of collapsed junctions" + info: null + example: + - 1000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "long" + name: "--limitBAMsortRAM" + description: "maximum available RAM (bytes) for sorting BAM. If =0, it will be\ + \ set to the genome index size. 0 value can only be used with --genomeLoad NoSharedMemory\ + \ option." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--limitSjdbInsertNsj" + description: "maximum number of junctions to be inserted to the genome on the\ + \ fly at the mapping stage, including those from annotations and those detected\ + \ in the 1st step of the 2-pass run" + info: null + example: + - 1000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--limitNreadsSoft" + description: "soft limit on the number of reads" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output: general" + arguments: + - type: "string" + name: "--outTmpKeep" + description: "whether to keep the temporary files after STAR runs is finished\n\ + \n- None ... remove all temporary files\n- All ... keep all files" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outStd" + description: "which output will be directed to stdout (standard out)\n\n- Log\ + \ ... log messages\n- SAM ... alignments\ + \ in SAM format (which normally are output to Aligned.out.sam file), normal\ + \ standard output will go into Log.std.out\n- BAM_Unsorted ... alignments\ + \ in BAM format, unsorted. Requires --outSAMtype BAM Unsorted\n- BAM_SortedByCoordinate\ + \ ... alignments in BAM format, sorted by coordinate. Requires --outSAMtype\ + \ BAM SortedByCoordinate\n- BAM_Quant ... alignments to transcriptome\ + \ in BAM format, unsorted. Requires --quantMode TranscriptomeSAM" + info: null + example: + - "Log" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outReadsUnmapped" + description: "output of unmapped and partially mapped (i.e. mapped only one mate\ + \ of a paired end read) reads in separate file(s).\n\n- None ... no output\n\ + - Fastx ... output in separate fasta/fastq files, Unmapped.out.mate1/2" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outQSconversionAdd" + description: "add this number to the quality score (e.g. to convert from Illumina\ + \ to Sanger, use -31)" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outMultimapperOrder" + description: "order of multimapping alignments in the output files\n\n- Old_2.4\ + \ ... quasi-random order used before 2.5.0\n- Random \ + \ ... random order of alignments for each multi-mapper. Read mates (pairs)\ + \ are always adjacent, all alignment for each read stay together. This option\ + \ will become default in the future releases." + info: null + example: + - "Old_2.4" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output: SAM and BAM" + arguments: + - type: "string" + name: "--outSAMtype" + description: "type of SAM/BAM output\n\n1st word:\n- BAM ... output BAM without\ + \ sorting\n- SAM ... output SAM without sorting\n- None ... no SAM/BAM output\n\ + 2nd, 3rd:\n- Unsorted ... standard unsorted\n- SortedByCoordinate\ + \ ... sorted by coordinate. This option will allocate extra memory for sorting\ + \ which can be specified by --limitBAMsortRAM." + info: null + example: + - "SAM" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outSAMmode" + description: "mode of SAM output\n\n- None ... no SAM output\n- Full ... full\ + \ SAM output\n- NoQS ... full SAM but without quality scores" + info: null + example: + - "Full" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMstrandField" + description: "Cufflinks-like strand field flag\n\n- None ... not used\n\ + - intronMotif ... strand derived from the intron motif. This option changes\ + \ the output alignments: reads with inconsistent and/or non-canonical introns\ + \ are filtered out." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMattributes" + description: "a string of desired SAM attributes, in the order desired for the\ + \ output SAM. Tags can be listed in any combination/order.\n\n***Presets:\n\ + - None ... no attributes\n- Standard ... NH HI AS nM\n- All \ + \ ... NH HI AS nM NM MD jM jI MC ch\n***Alignment:\n- NH ... number\ + \ of loci the reads maps to: =1 for unique mappers, >1 for multimappers. Standard\ + \ SAM tag.\n- HI ... multiple alignment index, starts with --outSAMattrIHstart\ + \ (=1 by default). Standard SAM tag.\n- AS ... local alignment score,\ + \ +1/-1 for matches/mismateches, score* penalties for indels and gaps. For PE\ + \ reads, total score for two mates. Stadnard SAM tag.\n- nM ... number\ + \ of mismatches. For PE reads, sum over two mates.\n- NM ... edit distance\ + \ to the reference (number of mismatched + inserted + deleted bases) for each\ + \ mate. Standard SAM tag.\n- MD ... string encoding mismatched and\ + \ deleted reference bases (see standard SAM specifications). Standard SAM tag.\n\ + - jM ... intron motifs for all junctions (i.e. N in CIGAR): 0: non-canonical;\ + \ 1: GT/AG, 2: CT/AC, 3: GC/AG, 4: CT/GC, 5: AT/AC, 6: GT/AT. If splice junctions\ + \ database is used, and a junction is annotated, 20 is added to its motif value.\n\ + - jI ... start and end of introns for all junctions (1-based).\n- XS\ + \ ... alignment strand according to --outSAMstrandField.\n- MC \ + \ ... mate's CIGAR string. Standard SAM tag.\n- ch ... marks all\ + \ segment of all chimeric alingments for --chimOutType WithinBAM output.\n-\ + \ cN ... number of bases clipped from the read ends: 5' and 3'\n***Variation:\n\ + - vA ... variant allele\n- vG ... genomic coordinate of the\ + \ variant overlapped by the read.\n- vW ... 1 - alignment passes WASP\ + \ filtering; 2,3,4,5,6,7 - alignment does not pass WASP filtering. Requires\ + \ --waspOutputMode SAMtag.\n- ha ... haplotype (1/2) when mapping to\ + \ the diploid genome. Requires genome generated with --genomeTransformType Diploid\ + \ .\n***STARsolo:\n- CR CY UR UY ... sequences and quality scores of cell barcodes\ + \ and UMIs for the solo* demultiplexing.\n- GX GN ... gene ID and gene\ + \ name for unique-gene reads.\n- gx gn ... gene IDs and gene names for\ + \ unique- and multi-gene reads.\n- CB UB ... error-corrected cell barcodes\ + \ and UMIs for solo* demultiplexing. Requires --outSAMtype BAM SortedByCoordinate.\n\ + - sM ... assessment of CB and UMI.\n- sS ... sequence of the\ + \ entire barcode (CB,UMI,adapter).\n- sQ ... quality of the entire\ + \ barcode.\n- sF ... type of feature overlap and number of features\ + \ for each alignment\n***Unsupported/undocumented:\n- rB ... alignment\ + \ block read/genomic coordinates.\n- vR ... read coordinate of the\ + \ variant." + info: null + example: + - "Standard" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSAMattrIHstart" + description: "start value for the IH attribute. 0 may be required by some downstream\ + \ software, such as Cufflinks or StringTie." + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMunmapped" + description: "output of unmapped reads in the SAM format\n\n1st word:\n- None\ + \ ... no output\n- Within ... output unmapped reads within the main SAM file\ + \ (i.e. Aligned.out.sam)\n2nd word:\n- KeepPairs ... record unmapped mate for\ + \ each alignment, and, in case of unsorted output, keep it adjacent to its mapped\ + \ mate. Only affects multi-mapping reads." + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outSAMorder" + description: "type of sorting for the SAM output\n\nPaired: one mate after the\ + \ other for all paired alignments\nPairedKeepInputOrder: one mate after the\ + \ other for all paired alignments, the order is kept the same as in the input\ + \ FASTQ files" + info: null + example: + - "Paired" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMprimaryFlag" + description: "which alignments are considered primary - all others will be marked\ + \ with 0x100 bit in the FLAG\n\n- OneBestScore ... only one alignment with the\ + \ best score is primary\n- AllBestScore ... all alignments with the best score\ + \ are primary" + info: null + example: + - "OneBestScore" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMreadID" + description: "read ID record type\n\n- Standard ... first word (until space) from\ + \ the FASTx read ID line, removing /1,/2 from the end\n- Number ... read number\ + \ (index) in the FASTx file" + info: null + example: + - "Standard" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outSAMmapqUnique" + description: "0 to 255: the MAPQ value for unique mappers" + info: null + example: + - 255 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outSAMflagOR" + description: "0 to 65535: sam FLAG will be bitwise OR'd with this value, i.e.\ + \ FLAG=FLAG | outSAMflagOR. This is applied after all flags have been set by\ + \ STAR, and after outSAMflagAND. Can be used to set specific bits that are not\ + \ set otherwise." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outSAMflagAND" + description: "0 to 65535: sam FLAG will be bitwise AND'd with this value, i.e.\ + \ FLAG=FLAG & outSAMflagOR. This is applied after all flags have been set by\ + \ STAR, but before outSAMflagOR. Can be used to unset specific bits that are\ + \ not set otherwise." + info: null + example: + - 65535 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMattrRGline" + description: "SAM/BAM read group line. The first word contains the read group\ + \ identifier and must start with \"ID:\", e.g. --outSAMattrRGline ID:xxx CN:yy\ + \ \"DS:z z z\".\n\nxxx will be added as RG tag to each output alignment. Any\ + \ spaces in the tag values have to be double quoted.\nComma separated RG lines\ + \ correspons to different (comma separated) input files in --readFilesIn. Commas\ + \ have to be surrounded by spaces, e.g.\n--outSAMattrRGline ID:xxx , ID:zzz\ + \ \"DS:z z\" , ID:yyy DS:yyyy" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outSAMheaderHD" + description: "@HD (header) line of the SAM header" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outSAMheaderPG" + description: "extra @PG (software) line of the SAM header (in addition to STAR)" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outSAMheaderCommentFile" + description: "path to the file with @CO (comment) lines of the SAM header" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outSAMfilter" + description: "filter the output into main SAM/BAM files\n\n- KeepOnlyAddedReferences\ + \ ... only keep the reads for which all alignments are to the extra reference\ + \ sequences added with --genomeFastaFiles at the mapping stage.\n- KeepAllAddedReferences\ + \ ... keep all alignments to the extra reference sequences added with --genomeFastaFiles\ + \ at the mapping stage." + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSAMmultNmax" + description: "max number of multiple alignments for a read that will be output\ + \ to the SAM/BAM files. Note that if this value is not equal to -1, the top\ + \ scoring alignment will be output first\n\n- -1 ... all alignments (up to --outFilterMultimapNmax)\ + \ will be output" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outSAMtlen" + description: "calculation method for the TLEN field in the SAM/BAM files\n\n-\ + \ 1 ... leftmost base of the (+)strand mate to rightmost base of the (-)mate.\ + \ (+)sign for the (+)strand mate\n- 2 ... leftmost base of any mate to rightmost\ + \ base of any mate. (+)sign for the mate with the leftmost base. This is different\ + \ from 1 for overlapping mates with protruding ends" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outBAMcompression" + description: "-1 to 10 BAM compression level, -1=default compression (6?), 0=no\ + \ compression, 10=maximum compression" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outBAMsortingThreadN" + description: ">=0: number of threads for BAM sorting. 0 will default to min(6,--runThreadN)." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outBAMsortingBinsN" + description: ">0: number of genome bins for coordinate-sorting" + info: null + example: + - 50 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "BAM processing" + arguments: + - type: "string" + name: "--bamRemoveDuplicatesType" + description: "mark duplicates in the BAM file, for now only works with (i) sorted\ + \ BAM fed with inputBAMfile, and (ii) for paired-end alignments only\n\n- -\ + \ ... no duplicate removal/marking\n- UniqueIdentical\ + \ ... mark all multimappers, and duplicate unique mappers. The coordinates,\ + \ FLAG, CIGAR must be identical\n- UniqueIdenticalNotMulti ... mark duplicate\ + \ unique mappers but not multimappers." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--bamRemoveDuplicatesMate2basesN" + description: "number of bases from the 5' of mate 2 to use in collapsing (e.g.\ + \ for RAMPAGE)" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output Wiggle" + arguments: + - type: "string" + name: "--outWigType" + description: "type of signal output, e.g. \"bedGraph\" OR \"bedGraph read1_5p\"\ + . Requires sorted BAM: --outSAMtype BAM SortedByCoordinate .\n\n1st word:\n\ + - None ... no signal output\n- bedGraph ... bedGraph format\n- wiggle\ + \ ... wiggle format\n2nd word:\n- read1_5p ... signal from only 5' of\ + \ the 1st read, useful for CAGE/RAMPAGE etc\n- read2 ... signal from only\ + \ 2nd read" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--outWigStrand" + description: "strandedness of wiggle/bedGraph output\n\n- Stranded ... separate\ + \ strands, str1 and str2\n- Unstranded ... collapsed strands" + info: null + example: + - "Stranded" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outWigReferencesPrefix" + description: "prefix matching reference names to include in the output wiggle\ + \ file, e.g. \"chr\", default \"-\" - include all references" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outWigNorm" + description: "type of normalization for the signal\n\n- RPM ... reads per million\ + \ of mapped reads\n- None ... no normalization, \"raw\" counts" + info: null + example: + - "RPM" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output Filtering" + arguments: + - type: "string" + name: "--outFilterType" + description: "type of filtering\n\n- Normal ... standard filtering using only\ + \ current alignment\n- BySJout ... keep only those reads that contain junctions\ + \ that passed filtering into SJ.out.tab" + info: null + example: + - "Normal" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outFilterMultimapScoreRange" + description: "the score range below the maximum score for multimapping alignments" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outFilterMultimapNmax" + description: "maximum number of loci the read is allowed to map to. Alignments\ + \ (all of them) will be output only if the read maps to no more loci than this\ + \ value.\n\nOtherwise no alignments will be output, and the read will be counted\ + \ as \"mapped to too many loci\" in the Log.final.out ." + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outFilterMismatchNmax" + description: "alignment will be output only if it has no more mismatches than\ + \ this value." + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--outFilterMismatchNoverLmax" + description: "alignment will be output only if its ratio of mismatches to *mapped*\ + \ length is less than or equal to this value." + info: null + example: + - 0.3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--outFilterMismatchNoverReadLmax" + description: "alignment will be output only if its ratio of mismatches to *read*\ + \ length is less than or equal to this value." + info: null + example: + - 1.0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outFilterScoreMin" + description: "alignment will be output only if its score is higher than or equal\ + \ to this value." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--outFilterScoreMinOverLread" + description: "same as outFilterScoreMin, but normalized to read length (sum of\ + \ mates' lengths for paired-end reads)" + info: null + example: + - 0.66 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outFilterMatchNmin" + description: "alignment will be output only if the number of matched bases is\ + \ higher than or equal to this value." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--outFilterMatchNminOverLread" + description: "sam as outFilterMatchNmin, but normalized to the read length (sum\ + \ of mates' lengths for paired-end reads)." + info: null + example: + - 0.66 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outFilterIntronMotifs" + description: "filter alignment using their motifs\n\n- None \ + \ ... no filtering\n- RemoveNoncanonical ... filter out\ + \ alignments that contain non-canonical junctions\n- RemoveNoncanonicalUnannotated\ + \ ... filter out alignments that contain non-canonical unannotated junctions\ + \ when using annotated splice junctions database. The annotated non-canonical\ + \ junctions will be kept." + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--outFilterIntronStrands" + description: "filter alignments\n\n- RemoveInconsistentStrands ... remove\ + \ alignments that have junctions with inconsistent strands\n- None \ + \ ... no filtering" + info: null + example: + - "RemoveInconsistentStrands" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output splice junctions (SJ.out.tab)" + arguments: + - type: "string" + name: "--outSJtype" + description: "type of splice junction output\n\n- Standard ... standard SJ.out.tab\ + \ output\n- None ... no splice junction output" + info: null + example: + - "Standard" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output Filtering: Splice Junctions" + arguments: + - type: "string" + name: "--outSJfilterReads" + description: "which reads to consider for collapsed splice junctions output\n\n\ + - All ... all reads, unique- and multi-mappers\n- Unique ... uniquely mapping\ + \ reads only" + info: null + example: + - "All" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--outSJfilterOverhangMin" + description: "minimum overhang length for splice junctions on both sides for:\ + \ (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif,\ + \ (4) AT/AC and GT/AT motif. -1 means no output for that motif\n\ndoes not apply\ + \ to annotated junctions" + info: null + example: + - 30 + - 12 + - 12 + - 12 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSJfilterCountUniqueMin" + description: "minimum uniquely mapping read count per junction for: (1) non-canonical\ + \ motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and\ + \ GT/AT motif. -1 means no output for that motif\n\nJunctions are output if\ + \ one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin conditions are\ + \ satisfied\ndoes not apply to annotated junctions" + info: null + example: + - 3 + - 1 + - 1 + - 1 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSJfilterCountTotalMin" + description: "minimum total (multi-mapping+unique) read count per junction for:\ + \ (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif,\ + \ (4) AT/AC and GT/AT motif. -1 means no output for that motif\n\nJunctions\ + \ are output if one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin\ + \ conditions are satisfied\ndoes not apply to annotated junctions" + info: null + example: + - 3 + - 1 + - 1 + - 1 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSJfilterDistToOtherSJmin" + description: "minimum allowed distance to other junctions' donor/acceptor\n\n\ + does not apply to annotated junctions" + info: null + example: + - 10 + - 0 + - 5 + - 10 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--outSJfilterIntronMaxVsReadN" + description: "maximum gap allowed for junctions supported by 1,2,3,,,N reads\n\ + \ni.e. by default junctions supported by 1 read can have gaps <=50000b, by 2\ + \ reads: <=100000b, by 3 reads: <=200000. by >=4 reads any gap <=alignIntronMax\n\ + does not apply to annotated junctions" + info: null + example: + - 50000 + - 100000 + - 200000 + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Scoring" + arguments: + - type: "integer" + name: "--scoreGap" + description: "splice junction penalty (independent on intron motif)" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreGapNoncan" + description: "non-canonical junction penalty (in addition to scoreGap)" + info: null + example: + - -8 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreGapGCAG" + description: "GC/AG and CT/GC junction penalty (in addition to scoreGap)" + info: null + example: + - -4 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreGapATAC" + description: "AT/AC and GT/AT junction penalty (in addition to scoreGap)" + info: null + example: + - -8 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreGenomicLengthLog2scale" + description: "extra score logarithmically scaled with genomic length of the alignment:\ + \ scoreGenomicLengthLog2scale*log2(genomicLength)" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreDelOpen" + description: "deletion open penalty" + info: null + example: + - -2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreDelBase" + description: "deletion extension penalty per base (in addition to scoreDelOpen)" + info: null + example: + - -2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreInsOpen" + description: "insertion open penalty" + info: null + example: + - -2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreInsBase" + description: "insertion extension penalty per base (in addition to scoreInsOpen)" + info: null + example: + - -2 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--scoreStitchSJshift" + description: "maximum score reduction while searching for SJ boundaries in the\ + \ stitching step" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Alignments and Seeding" + arguments: + - type: "integer" + name: "--seedSearchStartLmax" + description: "defines the search start point through the read - the read is split\ + \ into pieces no longer than this value" + info: null + example: + - 50 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--seedSearchStartLmaxOverLread" + description: "seedSearchStartLmax normalized to read length (sum of mates' lengths\ + \ for paired-end reads)" + info: null + example: + - 1.0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedSearchLmax" + description: "defines the maximum length of the seeds, if =0 seed length is not\ + \ limited" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedMultimapNmax" + description: "only pieces that map fewer than this value are utilized in the stitching\ + \ procedure" + info: null + example: + - 10000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedPerReadNmax" + description: "max number of seeds per read" + info: null + example: + - 1000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedPerWindowNmax" + description: "max number of seeds per window" + info: null + example: + - 50 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedNoneLociPerWindow" + description: "max number of one seed loci per window" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedSplitMin" + description: "min length of the seed sequences split by Ns or mate gap" + info: null + example: + - 12 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--seedMapMin" + description: "min length of seeds to be mapped" + info: null + example: + - 5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignIntronMin" + description: "minimum intron size, genomic gap is considered intron if its length>=alignIntronMin,\ + \ otherwise it is considered Deletion" + info: null + example: + - 21 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignIntronMax" + description: "maximum intron size, if 0, max intron size will be determined by\ + \ (2^winBinNbits)*winAnchorDistNbins" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignMatesGapMax" + description: "maximum gap between two mates, if 0, max intron gap will be determined\ + \ by (2^winBinNbits)*winAnchorDistNbins" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignSJoverhangMin" + description: "minimum overhang (i.e. block size) for spliced alignments" + info: null + example: + - 5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignSJstitchMismatchNmax" + description: "maximum number of mismatches for stitching of the splice junctions\ + \ (-1: no limit).\n\n(1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3)\ + \ GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif." + info: null + example: + - 0 + - -1 + - 0 + - 0 + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--alignSJDBoverhangMin" + description: "minimum overhang (i.e. block size) for annotated (sjdb) spliced\ + \ alignments" + info: null + example: + - 3 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignSplicedMateMapLmin" + description: "minimum mapped length for a read mate that is spliced" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--alignSplicedMateMapLminOverLmate" + description: "alignSplicedMateMapLmin normalized to mate length" + info: null + example: + - 0.66 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignWindowsPerReadNmax" + description: "max number of windows per read" + info: null + example: + - 10000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignTranscriptsPerWindowNmax" + description: "max number of transcripts per window" + info: null + example: + - 100 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--alignTranscriptsPerReadNmax" + description: "max number of different alignments per read to consider" + info: null + example: + - 10000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--alignEndsType" + description: "type of read ends alignment\n\n- Local ... standard\ + \ local alignment with soft-clipping allowed\n- EndToEnd ... force\ + \ end-to-end read alignment, do not soft-clip\n- Extend5pOfRead1 ... fully\ + \ extend only the 5p of the read1, all other ends: local alignment\n- Extend5pOfReads12\ + \ ... fully extend only the 5p of the both read1 and read2, all other ends:\ + \ local alignment" + info: null + example: + - "Local" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--alignEndsProtrude" + description: "allow protrusion of alignment ends, i.e. start (end) of the +strand\ + \ mate downstream of the start (end) of the -strand mate\n\n1st word: int: maximum\ + \ number of protrusion bases allowed\n2nd word: string:\n- \ + \ ConcordantPair ... report alignments with non-zero protrusion as concordant\ + \ pairs\n- DiscordantPair ... report alignments with non-zero\ + \ protrusion as discordant pairs" + info: null + example: + - "0 ConcordantPair" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--alignSoftClipAtReferenceEnds" + description: "allow the soft-clipping of the alignments past the end of the chromosomes\n\ + \n- Yes ... allow\n- No ... prohibit, useful for compatibility with Cufflinks" + info: null + example: + - "Yes" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--alignInsertionFlush" + description: "how to flush ambiguous insertion positions\n\n- None ... insertions\ + \ are not flushed\n- Right ... insertions are flushed to the right" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Paired-End reads" + arguments: + - type: "integer" + name: "--peOverlapNbasesMin" + description: "minimum number of overlapping bases to trigger mates merging and\ + \ realignment. Specify >0 value to switch on the \"merginf of overlapping mates\"\ + \ algorithm." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--peOverlapMMp" + description: "maximum proportion of mismatched bases in the overlap area" + info: null + example: + - 0.01 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Windows, Anchors, Binning" + arguments: + - type: "integer" + name: "--winAnchorMultimapNmax" + description: "max number of loci anchors are allowed to map to" + info: null + example: + - 50 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--winBinNbits" + description: "=log2(winBin), where winBin is the size of the bin for the windows/clustering,\ + \ each window will occupy an integer number of bins." + info: null + example: + - 16 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--winAnchorDistNbins" + description: "max number of bins between two anchors that allows aggregation of\ + \ anchors into one window" + info: null + example: + - 9 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--winFlankNbins" + description: "log2(winFlank), where win Flank is the size of the left and right\ + \ flanking regions for each window" + info: null + example: + - 4 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "double" + name: "--winReadCoverageRelativeMin" + description: "minimum relative coverage of the read sequence by the seeds in a\ + \ window, for STARlong algorithm only." + info: null + example: + - 0.5 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--winReadCoverageBasesMin" + description: "minimum number of bases covered by the seeds in a window , for STARlong\ + \ algorithm only." + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Chimeric Alignments" + arguments: + - type: "string" + name: "--chimOutType" + description: "type of chimeric output\n\n- Junctions ... Chimeric.out.junction\n\ + - SeparateSAMold ... output old SAM into separate Chimeric.out.sam file\n-\ + \ WithinBAM ... output into main aligned BAM files (Aligned.*.bam)\n-\ + \ WithinBAM HardClip ... (default) hard-clipping in the CIGAR for supplemental\ + \ chimeric alignments (default if no 2nd word is present)\n- WithinBAM SoftClip\ + \ ... soft-clipping in the CIGAR for supplemental chimeric alignments" + info: null + example: + - "Junctions" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--chimSegmentMin" + description: "minimum length of chimeric segment length, if ==0, no chimeric output" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimScoreMin" + description: "minimum total (summed) score of the chimeric segments" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimScoreDropMax" + description: "max drop (difference) of chimeric score (the sum of scores of all\ + \ chimeric segments) from the read length" + info: null + example: + - 20 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimScoreSeparation" + description: "minimum difference (separation) between the best chimeric score\ + \ and the next one" + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimScoreJunctionNonGTAG" + description: "penalty for a non-GT/AG chimeric junction" + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimJunctionOverhangMin" + description: "minimum overhang for a chimeric junction" + info: null + example: + - 20 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimSegmentReadGapMax" + description: "maximum gap in the read sequence between chimeric segments" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--chimFilter" + description: "different filters for chimeric alignments\n\n- None ... no filtering\n\ + - banGenomicN ... Ns are not allowed in the genome sequence around the chimeric\ + \ junction" + info: null + example: + - "banGenomicN" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--chimMainSegmentMultNmax" + description: "maximum number of multi-alignments for the main chimeric segment.\ + \ =1 will prohibit multimapping main segments." + info: null + example: + - 10 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimMultimapNmax" + description: "maximum number of chimeric multi-alignments\n\n- 0 ... use the old\ + \ scheme for chimeric detection which only considered unique alignments" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimMultimapScoreRange" + description: "the score range for multi-mapping chimeras below the best chimeric\ + \ score. Only works with --chimMultimapNmax > 1" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimNonchimScoreDropMin" + description: "to trigger chimeric detection, the drop in the best non-chimeric\ + \ alignment score with respect to the read length has to be greater than this\ + \ value" + info: null + example: + - 20 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--chimOutJunctionFormat" + description: "formatting type for the Chimeric.out.junction file\n\n- 0 ... no\ + \ comment lines/headers\n- 1 ... comment lines at the end of the file: command\ + \ line and Nreads: total, unique/multi-mapping" + info: null + example: + - 0 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Quantification of Annotations" + arguments: + - type: "string" + name: "--quantMode" + description: "types of quantification requested\n\n- - ... none\n\ + - TranscriptomeSAM ... output SAM/BAM alignments to transcriptome into a separate\ + \ file\n- GeneCounts ... count reads per gene" + info: null + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "integer" + name: "--quantTranscriptomeBAMcompression" + description: "-2 to 10 transcriptome BAM compression level\n\n- -2 ... no BAM\ + \ output\n- -1 ... default compression (6?)\n- 0 ... no compression\n- 10\ + \ ... maximum compression" + info: null + example: + - 1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--quantTranscriptomeSAMoutput" + description: "alignment filtering for TranscriptomeSAM output\n\n- BanSingleEnd_BanIndels_ExtendSoftclip\ + \ ... prohibit indels and single-end alignments, extend softclips - compatible\ + \ with RSEM\n- BanSingleEnd ... prohibit single-end alignments,\ + \ allow indels and softclips\n- BanSingleEnd_ExtendSoftclip ... prohibit single-end\ + \ alignments, extend softclips, allow indels" + info: null + example: + - "BanSingleEnd_BanIndels_ExtendSoftclip" + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "2-pass Mapping" + arguments: + - type: "string" + name: "--twopassMode" + description: "2-pass mapping mode.\n\n- None ... 1-pass mapping\n- Basic\ + \ ... basic 2-pass mapping, with all 1st pass junctions inserted into\ + \ the genome indices on the fly" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--twopass1readsN" + description: "number of reads to process for the 1st step. Use very large number\ + \ (or default -1) to map all reads in the first step." + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "WASP parameters" + arguments: + - type: "string" + name: "--waspOutputMode" + description: "WASP allele-specific output type. This is re-implementation of the\ + \ original WASP mappability filtering by Bryce van de Geijn, Graham McVicker,\ + \ Yoav Gilad & Jonathan K Pritchard. Please cite the original WASP paper: Nature\ + \ Methods 12, 1061-1063 (2015), https://www.nature.com/articles/nmeth.3582 .\n\ + \n- SAMtag ... add WASP tags to the alignments that pass WASP filtering" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Inputs" + arguments: + - type: "file" + name: "--input" + alternatives: + - "--readFilesIn" + description: "The single-end or paired-end R1 FastQ files to be processed." + info: null + example: + - "mysample_S1_L001_R1_001.fastq.gz" + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--input_r2" + description: "The paired-end R2 FastQ files to be processed. Only required if\ + \ --input is a paired-end R1 file." + info: null + example: + - "mysample_S1_L001_R2_001.fastq.gz" + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: true + multiple_sep: ";" +- name: "Outputs" + arguments: + - type: "file" + name: "--aligned_reads" + description: "The output file containing the aligned reads." + info: null + example: + - "aligned_reads.bam" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--reads_per_gene" + description: "The output file containing the number of reads per gene." + info: null + example: + - "reads_per_gene.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unmapped" + description: "The output file containing the unmapped reads." + info: null + example: + - "unmapped.fastq" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--unmapped_r2" + description: "The output file containing the unmapped R2 reads." + info: null + example: + - "unmapped_r2.fastq" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--chimeric_junctions" + description: "The output file containing the chimeric junctions." + info: null + example: + - "chimeric_junctions.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--log" + description: "The output file containing the log of the alignment process." + info: null + example: + - "log.txt" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--splice_junctions" + description: "The output file containing the splice junctions." + info: null + example: + - "splice_junctions.tsv" + must_exist: true + create_parent: true + required: false + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "python_script" + path: "script.py" + is_executable: true +description: "Aligns reads to a reference genome using STAR.\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "align" +- "fasta" +- "genome" +license: "MIT" +references: + doi: + - "10.1093/bioinformatics/bts635" +links: + repository: "https://github.com/alexdobin/STAR" + documentation: "https://github.com/alexdobin/STAR/blob/master/doc/STARmanual.pdf" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "python:3.12-slim" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "apt" + packages: + - "procps" + - "gzip" + - "bzip2" + interactive: false + - type: "docker" + run: + - "apt-get update && \\\n apt-get install -y --no-install-recommends ${PACKAGES}\ + \ && \\\n cd /tmp && \\\n wget --no-check-certificate https://github.com/alexdobin/STAR/archive/refs/tags/${STAR_VERSION}.zip\ + \ && \\\n unzip ${STAR_VERSION}.zip && \\\n cd STAR-${STAR_VERSION}/source\ + \ && \\\n make STARstatic CXXFLAGS_SIMD=-std=c++11 && \\\n cp STAR /usr/local/bin\ + \ && \\\n cd / && \\\n rm -rf /tmp/STAR-${STAR_VERSION} /tmp/${STAR_VERSION}.zip\ + \ && \\\n apt-get --purge autoremove -y ${PACKAGES} && \\\n apt-get clean\n" + env: + - "STAR_VERSION 2.7.11b" + - "PACKAGES gcc g++ make wget zlib1g-dev unzip xxd" + - type: "docker" + run: + - "STAR --version | sed 's#\\(.*\\)#star: \"\\1\"#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/star/star_align_reads/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/star/star_align_reads" + executable: "target/nextflow/star/star_align_reads/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/star/star_align_reads/main.nf b/target/nextflow/star/star_align_reads/main.nf new file mode 100644 index 00000000..e2d1828c --- /dev/null +++ b/target/nextflow/star/star_align_reads/main.nf @@ -0,0 +1,5812 @@ +// star_align_reads main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "star_align_reads", + "namespace" : "star", + "version" : "main", + "argument_groups" : [ + { + "name" : "Run Parameters", + "arguments" : [ + { + "type" : "integer", + "name" : "--runRNGseed", + "description" : "random number generator seed.", + "example" : [ + 777 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Genome Parameters", + "arguments" : [ + { + "type" : "file", + "name" : "--genomeDir", + "description" : "path to the directory where genome files are stored (for --runMode alignReads) or will be generated (for --runMode generateGenome)", + "example" : [ + "./GenomeDir" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--genomeLoad", + "description" : "mode of shared memory usage for the genome files. Only used with --runMode alignReads.\n\n- LoadAndKeep ... load genome into shared and keep it in memory after run\n- LoadAndRemove ... load genome into shared but remove it after run\n- LoadAndExit ... load genome into shared memory and exit, keeping the genome in memory for future runs\n- Remove ... do not map anything, just remove loaded genome from memory\n- NoSharedMemory ... do not use shared memory, each job will have its own private copy of the genome", + "example" : [ + "NoSharedMemory" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--genomeFastaFiles", + "description" : "path(s) to the fasta files with the genome sequences, separated by spaces. These files should be plain text FASTA files, they *cannot* be zipped.\n\nRequired for the genome generation (--runMode genomeGenerate). Can also be used in the mapping (--runMode alignReads) to add extra (new) sequences to the genome (e.g. spike-ins).", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--genomeFileSizes", + "description" : "genome files exact sizes in bytes. Typically, this should not be defined by the user.", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--genomeTransformOutput", + "description" : "which output to transform back to original genome\n\n- SAM ... SAM/BAM alignments\n- SJ ... splice junctions (SJ.out.tab)\n- Quant ... quantifications (from --quantMode option)\n- None ... no transformation of the output", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--genomeChrSetMitochondrial", + "description" : "names of the mitochondrial chromosomes. Presently only used for STARsolo statistics output/", + "example" : [ + "chrM", + "M", + "MT" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Splice Junctions Database", + "arguments" : [ + { + "type" : "string", + "name" : "--sjdbFileChrStartEnd", + "description" : "path to the files with genomic coordinates (chr start end strand) for the splice junction introns. Multiple files can be supplied and will be concatenated.", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--sjdbGTFfile", + "description" : "path to the GTF file with annotations", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFchrPrefix", + "description" : "prefix for chromosome names in a GTF file (e.g. 'chr' for using ENSMEBL annotations with UCSC genomes)", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFfeatureExon", + "description" : "feature type in GTF file to be used as exons for building transcripts", + "example" : [ + "exon" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFtagExonParentTranscript", + "description" : "GTF attribute name for parent transcript ID (default \\"transcript_id\\" works for GTF files)", + "example" : [ + "transcript_id" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFtagExonParentGene", + "description" : "GTF attribute name for parent gene ID (default \\"gene_id\\" works for GTF files)", + "example" : [ + "gene_id" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFtagExonParentGeneName", + "description" : "GTF attribute name for parent gene name", + "example" : [ + "gene_name" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFtagExonParentGeneType", + "description" : "GTF attribute name for parent gene type", + "example" : [ + "gene_type", + "gene_biotype" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--sjdbOverhang", + "description" : "length of the donor/acceptor sequence on each side of the junctions, ideally = (mate_length - 1)", + "example" : [ + 100 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--sjdbScore", + "description" : "extra alignment score for alignments that cross database junctions", + "example" : [ + 2 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbInsertSave", + "description" : "which files to save when sjdb junctions are inserted on the fly at the mapping step\n\n- Basic ... only small junction / transcript files\n- All ... all files including big Genome, SA and SAindex - this will create a complete genome directory", + "example" : [ + "Basic" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Variation parameters", + "arguments" : [ + { + "type" : "string", + "name" : "--varVCFfile", + "description" : "path to the VCF file that contains variation data. The 10th column should contain the genotype information, e.g. 0/1", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Read Parameters", + "arguments" : [ + { + "type" : "string", + "name" : "--readFilesType", + "description" : "format of input read files\n\n- Fastx ... FASTA or FASTQ\n- SAM SE ... SAM or BAM single-end reads; for BAM use --readFilesCommand samtools view\n- SAM PE ... SAM or BAM paired-end reads; for BAM use --readFilesCommand samtools view", + "example" : [ + "Fastx" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--readFilesSAMattrKeep", + "description" : "for --readFilesType SAM SE/PE, which SAM tags to keep in the output BAM, e.g.: --readFilesSAMtagsKeep RG PL\n\n- All ... keep all tags\n- None ... do not keep any tags", + "example" : [ + "All" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--readFilesManifest", + "description" : "path to the \\"manifest\\" file with the names of read files. The manifest file should contain 3 tab-separated columns:\n\npaired-end reads: read1_file_name $tab$ read2_file_name $tab$ read_group_line.\nsingle-end reads: read1_file_name $tab$ - $tab$ read_group_line.\nSpaces, but not tabs are allowed in file names.\nIf read_group_line does not start with ID:, it can only contain one ID field, and ID: will be added to it.\nIf read_group_line starts with ID:, it can contain several fields separated by $tab$, and all fields will be be copied verbatim into SAM @RG header line.", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--readFilesPrefix", + "description" : "prefix for the read files names, i.e. it will be added in front of the strings in --readFilesIn", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--readFilesCommand", + "description" : "command line to execute for each of the input file. This command should generate FASTA or FASTQ text and send it to stdout\n\nFor example: zcat - to uncompress .gz files, bzcat - to uncompress .bz2 files, etc.", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--readMapNumber", + "description" : "number of reads to map from the beginning of the file\n\n-1: map all reads", + "example" : [ + -1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--readMatesLengthsIn", + "description" : "Equal/NotEqual - lengths of names,sequences,qualities for both mates are the same / not the same. NotEqual is safe in all situations.", + "example" : [ + "NotEqual" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--readNameSeparator", + "description" : "character(s) separating the part of the read names that will be trimmed in output (read name after space is always trimmed)", + "example" : [ + "/" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--readQualityScoreBase", + "description" : "number to be subtracted from the ASCII code to get Phred quality score", + "example" : [ + 33 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Read Clipping", + "arguments" : [ + { + "type" : "string", + "name" : "--clipAdapterType", + "description" : "adapter clipping type\n\n- Hamming ... adapter clipping based on Hamming distance, with the number of mismatches controlled by --clip5pAdapterMMp\n- CellRanger4 ... 5p and 3p adapter clipping similar to CellRanger4. Utilizes Opal package by Martin Sosic: https://github.com/Martinsos/opal\n- None ... no adapter clipping, all other clip* parameters are disregarded", + "example" : [ + "Hamming" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--clip3pNbases", + "description" : "number(s) of bases to clip from 3p of each mate. If one value is given, it will be assumed the same for both mates.", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--clip3pAdapterSeq", + "description" : "adapter sequences to clip from 3p of each mate. If one value is given, it will be assumed the same for both mates.\n\n- polyA ... polyA sequence with the length equal to read length", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--clip3pAdapterMMp", + "description" : "max proportion of mismatches for 3p adapter clipping for each mate. If one value is given, it will be assumed the same for both mates.", + "example" : [ + 0.1 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--clip3pAfterAdapterNbases", + "description" : "number of bases to clip from 3p of each mate after the adapter clipping. If one value is given, it will be assumed the same for both mates.", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--clip5pNbases", + "description" : "number(s) of bases to clip from 5p of each mate. If one value is given, it will be assumed the same for both mates.", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Limits", + "arguments" : [ + { + "type" : "long", + "name" : "--limitGenomeGenerateRAM", + "description" : "maximum available RAM (bytes) for genome generation", + "example" : [ + 31000000000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "long", + "name" : "--limitIObufferSize", + "description" : "max available buffers size (bytes) for input/output, per thread", + "example" : [ + 30000000, + 50000000 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "long", + "name" : "--limitOutSAMoneReadBytes", + "description" : "max size of the SAM record (bytes) for one read. Recommended value: >(2*(LengthMate1+LengthMate2+100)*outFilterMultimapNmax", + "example" : [ + 100000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--limitOutSJoneRead", + "description" : "max number of junctions for one read (including all multi-mappers)", + "example" : [ + 1000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--limitOutSJcollapsed", + "description" : "max number of collapsed junctions", + "example" : [ + 1000000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "long", + "name" : "--limitBAMsortRAM", + "description" : "maximum available RAM (bytes) for sorting BAM. If =0, it will be set to the genome index size. 0 value can only be used with --genomeLoad NoSharedMemory option.", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--limitSjdbInsertNsj", + "description" : "maximum number of junctions to be inserted to the genome on the fly at the mapping stage, including those from annotations and those detected in the 1st step of the 2-pass run", + "example" : [ + 1000000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--limitNreadsSoft", + "description" : "soft limit on the number of reads", + "example" : [ + -1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Output: general", + "arguments" : [ + { + "type" : "string", + "name" : "--outTmpKeep", + "description" : "whether to keep the temporary files after STAR runs is finished\n\n- None ... remove all temporary files\n- All ... keep all files", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outStd", + "description" : "which output will be directed to stdout (standard out)\n\n- Log ... log messages\n- SAM ... alignments in SAM format (which normally are output to Aligned.out.sam file), normal standard output will go into Log.std.out\n- BAM_Unsorted ... alignments in BAM format, unsorted. Requires --outSAMtype BAM Unsorted\n- BAM_SortedByCoordinate ... alignments in BAM format, sorted by coordinate. Requires --outSAMtype BAM SortedByCoordinate\n- BAM_Quant ... alignments to transcriptome in BAM format, unsorted. Requires --quantMode TranscriptomeSAM", + "example" : [ + "Log" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outReadsUnmapped", + "description" : "output of unmapped and partially mapped (i.e. mapped only one mate of a paired end read) reads in separate file(s).\n\n- None ... no output\n- Fastx ... output in separate fasta/fastq files, Unmapped.out.mate1/2", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outQSconversionAdd", + "description" : "add this number to the quality score (e.g. to convert from Illumina to Sanger, use -31)", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outMultimapperOrder", + "description" : "order of multimapping alignments in the output files\n\n- Old_2.4 ... quasi-random order used before 2.5.0\n- Random ... random order of alignments for each multi-mapper. Read mates (pairs) are always adjacent, all alignment for each read stay together. This option will become default in the future releases.", + "example" : [ + "Old_2.4" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Output: SAM and BAM", + "arguments" : [ + { + "type" : "string", + "name" : "--outSAMtype", + "description" : "type of SAM/BAM output\n\n1st word:\n- BAM ... output BAM without sorting\n- SAM ... output SAM without sorting\n- None ... no SAM/BAM output\n2nd, 3rd:\n- Unsorted ... standard unsorted\n- SortedByCoordinate ... sorted by coordinate. This option will allocate extra memory for sorting which can be specified by --limitBAMsortRAM.", + "example" : [ + "SAM" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMmode", + "description" : "mode of SAM output\n\n- None ... no SAM output\n- Full ... full SAM output\n- NoQS ... full SAM but without quality scores", + "example" : [ + "Full" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMstrandField", + "description" : "Cufflinks-like strand field flag\n\n- None ... not used\n- intronMotif ... strand derived from the intron motif. This option changes the output alignments: reads with inconsistent and/or non-canonical introns are filtered out.", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMattributes", + "description" : "a string of desired SAM attributes, in the order desired for the output SAM. Tags can be listed in any combination/order.\n\n***Presets:\n- None ... no attributes\n- Standard ... NH HI AS nM\n- All ... NH HI AS nM NM MD jM jI MC ch\n***Alignment:\n- NH ... number of loci the reads maps to: =1 for unique mappers, >1 for multimappers. Standard SAM tag.\n- HI ... multiple alignment index, starts with --outSAMattrIHstart (=1 by default). Standard SAM tag.\n- AS ... local alignment score, +1/-1 for matches/mismateches, score* penalties for indels and gaps. For PE reads, total score for two mates. Stadnard SAM tag.\n- nM ... number of mismatches. For PE reads, sum over two mates.\n- NM ... edit distance to the reference (number of mismatched + inserted + deleted bases) for each mate. Standard SAM tag.\n- MD ... string encoding mismatched and deleted reference bases (see standard SAM specifications). Standard SAM tag.\n- jM ... intron motifs for all junctions (i.e. N in CIGAR): 0: non-canonical; 1: GT/AG, 2: CT/AC, 3: GC/AG, 4: CT/GC, 5: AT/AC, 6: GT/AT. If splice junctions database is used, and a junction is annotated, 20 is added to its motif value.\n- jI ... start and end of introns for all junctions (1-based).\n- XS ... alignment strand according to --outSAMstrandField.\n- MC ... mate's CIGAR string. Standard SAM tag.\n- ch ... marks all segment of all chimeric alingments for --chimOutType WithinBAM output.\n- cN ... number of bases clipped from the read ends: 5' and 3'\n***Variation:\n- vA ... variant allele\n- vG ... genomic coordinate of the variant overlapped by the read.\n- vW ... 1 - alignment passes WASP filtering; 2,3,4,5,6,7 - alignment does not pass WASP filtering. Requires --waspOutputMode SAMtag.\n- ha ... haplotype (1/2) when mapping to the diploid genome. Requires genome generated with --genomeTransformType Diploid .\n***STARsolo:\n- CR CY UR UY ... sequences and quality scores of cell barcodes and UMIs for the solo* demultiplexing.\n- GX GN ... gene ID and gene name for unique-gene reads.\n- gx gn ... gene IDs and gene names for unique- and multi-gene reads.\n- CB UB ... error-corrected cell barcodes and UMIs for solo* demultiplexing. Requires --outSAMtype BAM SortedByCoordinate.\n- sM ... assessment of CB and UMI.\n- sS ... sequence of the entire barcode (CB,UMI,adapter).\n- sQ ... quality of the entire barcode.\n- sF ... type of feature overlap and number of features for each alignment\n***Unsupported/undocumented:\n- rB ... alignment block read/genomic coordinates.\n- vR ... read coordinate of the variant.", + "example" : [ + "Standard" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outSAMattrIHstart", + "description" : "start value for the IH attribute. 0 may be required by some downstream software, such as Cufflinks or StringTie.", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMunmapped", + "description" : "output of unmapped reads in the SAM format\n\n1st word:\n- None ... no output\n- Within ... output unmapped reads within the main SAM file (i.e. Aligned.out.sam)\n2nd word:\n- KeepPairs ... record unmapped mate for each alignment, and, in case of unsorted output, keep it adjacent to its mapped mate. Only affects multi-mapping reads.", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMorder", + "description" : "type of sorting for the SAM output\n\nPaired: one mate after the other for all paired alignments\nPairedKeepInputOrder: one mate after the other for all paired alignments, the order is kept the same as in the input FASTQ files", + "example" : [ + "Paired" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMprimaryFlag", + "description" : "which alignments are considered primary - all others will be marked with 0x100 bit in the FLAG\n\n- OneBestScore ... only one alignment with the best score is primary\n- AllBestScore ... all alignments with the best score are primary", + "example" : [ + "OneBestScore" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMreadID", + "description" : "read ID record type\n\n- Standard ... first word (until space) from the FASTx read ID line, removing /1,/2 from the end\n- Number ... read number (index) in the FASTx file", + "example" : [ + "Standard" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outSAMmapqUnique", + "description" : "0 to 255: the MAPQ value for unique mappers", + "example" : [ + 255 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outSAMflagOR", + "description" : "0 to 65535: sam FLAG will be bitwise OR'd with this value, i.e. FLAG=FLAG | outSAMflagOR. This is applied after all flags have been set by STAR, and after outSAMflagAND. Can be used to set specific bits that are not set otherwise.", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outSAMflagAND", + "description" : "0 to 65535: sam FLAG will be bitwise AND'd with this value, i.e. FLAG=FLAG & outSAMflagOR. This is applied after all flags have been set by STAR, but before outSAMflagOR. Can be used to unset specific bits that are not set otherwise.", + "example" : [ + 65535 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMattrRGline", + "description" : "SAM/BAM read group line. The first word contains the read group identifier and must start with \\"ID:\\", e.g. --outSAMattrRGline ID:xxx CN:yy \\"DS:z z z\\".\n\nxxx will be added as RG tag to each output alignment. Any spaces in the tag values have to be double quoted.\nComma separated RG lines correspons to different (comma separated) input files in --readFilesIn. Commas have to be surrounded by spaces, e.g.\n--outSAMattrRGline ID:xxx , ID:zzz \\"DS:z z\\" , ID:yyy DS:yyyy", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMheaderHD", + "description" : "@HD (header) line of the SAM header", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMheaderPG", + "description" : "extra @PG (software) line of the SAM header (in addition to STAR)", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMheaderCommentFile", + "description" : "path to the file with @CO (comment) lines of the SAM header", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outSAMfilter", + "description" : "filter the output into main SAM/BAM files\n\n- KeepOnlyAddedReferences ... only keep the reads for which all alignments are to the extra reference sequences added with --genomeFastaFiles at the mapping stage.\n- KeepAllAddedReferences ... keep all alignments to the extra reference sequences added with --genomeFastaFiles at the mapping stage.", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outSAMmultNmax", + "description" : "max number of multiple alignments for a read that will be output to the SAM/BAM files. Note that if this value is not equal to -1, the top scoring alignment will be output first\n\n- -1 ... all alignments (up to --outFilterMultimapNmax) will be output", + "example" : [ + -1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outSAMtlen", + "description" : "calculation method for the TLEN field in the SAM/BAM files\n\n- 1 ... leftmost base of the (+)strand mate to rightmost base of the (-)mate. (+)sign for the (+)strand mate\n- 2 ... leftmost base of any mate to rightmost base of any mate. (+)sign for the mate with the leftmost base. This is different from 1 for overlapping mates with protruding ends", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outBAMcompression", + "description" : "-1 to 10 BAM compression level, -1=default compression (6?), 0=no compression, 10=maximum compression", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outBAMsortingThreadN", + "description" : ">=0: number of threads for BAM sorting. 0 will default to min(6,--runThreadN).", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outBAMsortingBinsN", + "description" : ">0: number of genome bins for coordinate-sorting", + "example" : [ + 50 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "BAM processing", + "arguments" : [ + { + "type" : "string", + "name" : "--bamRemoveDuplicatesType", + "description" : "mark duplicates in the BAM file, for now only works with (i) sorted BAM fed with inputBAMfile, and (ii) for paired-end alignments only\n\n- - ... no duplicate removal/marking\n- UniqueIdentical ... mark all multimappers, and duplicate unique mappers. The coordinates, FLAG, CIGAR must be identical\n- UniqueIdenticalNotMulti ... mark duplicate unique mappers but not multimappers.", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--bamRemoveDuplicatesMate2basesN", + "description" : "number of bases from the 5' of mate 2 to use in collapsing (e.g. for RAMPAGE)", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Output Wiggle", + "arguments" : [ + { + "type" : "string", + "name" : "--outWigType", + "description" : "type of signal output, e.g. \\"bedGraph\\" OR \\"bedGraph read1_5p\\". Requires sorted BAM: --outSAMtype BAM SortedByCoordinate .\n\n1st word:\n- None ... no signal output\n- bedGraph ... bedGraph format\n- wiggle ... wiggle format\n2nd word:\n- read1_5p ... signal from only 5' of the 1st read, useful for CAGE/RAMPAGE etc\n- read2 ... signal from only 2nd read", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outWigStrand", + "description" : "strandedness of wiggle/bedGraph output\n\n- Stranded ... separate strands, str1 and str2\n- Unstranded ... collapsed strands", + "example" : [ + "Stranded" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outWigReferencesPrefix", + "description" : "prefix matching reference names to include in the output wiggle file, e.g. \\"chr\\", default \\"-\\" - include all references", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outWigNorm", + "description" : "type of normalization for the signal\n\n- RPM ... reads per million of mapped reads\n- None ... no normalization, \\"raw\\" counts", + "example" : [ + "RPM" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Output Filtering", + "arguments" : [ + { + "type" : "string", + "name" : "--outFilterType", + "description" : "type of filtering\n\n- Normal ... standard filtering using only current alignment\n- BySJout ... keep only those reads that contain junctions that passed filtering into SJ.out.tab", + "example" : [ + "Normal" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outFilterMultimapScoreRange", + "description" : "the score range below the maximum score for multimapping alignments", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outFilterMultimapNmax", + "description" : "maximum number of loci the read is allowed to map to. Alignments (all of them) will be output only if the read maps to no more loci than this value.\n\nOtherwise no alignments will be output, and the read will be counted as \\"mapped to too many loci\\" in the Log.final.out .", + "example" : [ + 10 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outFilterMismatchNmax", + "description" : "alignment will be output only if it has no more mismatches than this value.", + "example" : [ + 10 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--outFilterMismatchNoverLmax", + "description" : "alignment will be output only if its ratio of mismatches to *mapped* length is less than or equal to this value.", + "example" : [ + 0.3 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--outFilterMismatchNoverReadLmax", + "description" : "alignment will be output only if its ratio of mismatches to *read* length is less than or equal to this value.", + "example" : [ + 1.0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outFilterScoreMin", + "description" : "alignment will be output only if its score is higher than or equal to this value.", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--outFilterScoreMinOverLread", + "description" : "same as outFilterScoreMin, but normalized to read length (sum of mates' lengths for paired-end reads)", + "example" : [ + 0.66 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outFilterMatchNmin", + "description" : "alignment will be output only if the number of matched bases is higher than or equal to this value.", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--outFilterMatchNminOverLread", + "description" : "sam as outFilterMatchNmin, but normalized to the read length (sum of mates' lengths for paired-end reads).", + "example" : [ + 0.66 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outFilterIntronMotifs", + "description" : "filter alignment using their motifs\n\n- None ... no filtering\n- RemoveNoncanonical ... filter out alignments that contain non-canonical junctions\n- RemoveNoncanonicalUnannotated ... filter out alignments that contain non-canonical unannotated junctions when using annotated splice junctions database. The annotated non-canonical junctions will be kept.", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--outFilterIntronStrands", + "description" : "filter alignments\n\n- RemoveInconsistentStrands ... remove alignments that have junctions with inconsistent strands\n- None ... no filtering", + "example" : [ + "RemoveInconsistentStrands" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Output splice junctions (SJ.out.tab)", + "arguments" : [ + { + "type" : "string", + "name" : "--outSJtype", + "description" : "type of splice junction output\n\n- Standard ... standard SJ.out.tab output\n- None ... no splice junction output", + "example" : [ + "Standard" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Output Filtering: Splice Junctions", + "arguments" : [ + { + "type" : "string", + "name" : "--outSJfilterReads", + "description" : "which reads to consider for collapsed splice junctions output\n\n- All ... all reads, unique- and multi-mappers\n- Unique ... uniquely mapping reads only", + "example" : [ + "All" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outSJfilterOverhangMin", + "description" : "minimum overhang length for splice junctions on both sides for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif\n\ndoes not apply to annotated junctions", + "example" : [ + 30, + 12, + 12, + 12 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outSJfilterCountUniqueMin", + "description" : "minimum uniquely mapping read count per junction for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif\n\nJunctions are output if one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin conditions are satisfied\ndoes not apply to annotated junctions", + "example" : [ + 3, + 1, + 1, + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outSJfilterCountTotalMin", + "description" : "minimum total (multi-mapping+unique) read count per junction for: (1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif. -1 means no output for that motif\n\nJunctions are output if one of outSJfilterCountUniqueMin OR outSJfilterCountTotalMin conditions are satisfied\ndoes not apply to annotated junctions", + "example" : [ + 3, + 1, + 1, + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outSJfilterDistToOtherSJmin", + "description" : "minimum allowed distance to other junctions' donor/acceptor\n\ndoes not apply to annotated junctions", + "example" : [ + 10, + 0, + 5, + 10 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--outSJfilterIntronMaxVsReadN", + "description" : "maximum gap allowed for junctions supported by 1,2,3,,,N reads\n\ni.e. by default junctions supported by 1 read can have gaps <=50000b, by 2 reads: <=100000b, by 3 reads: <=200000. by >=4 reads any gap <=alignIntronMax\ndoes not apply to annotated junctions", + "example" : [ + 50000, + 100000, + 200000 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Scoring", + "arguments" : [ + { + "type" : "integer", + "name" : "--scoreGap", + "description" : "splice junction penalty (independent on intron motif)", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--scoreGapNoncan", + "description" : "non-canonical junction penalty (in addition to scoreGap)", + "example" : [ + -8 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--scoreGapGCAG", + "description" : "GC/AG and CT/GC junction penalty (in addition to scoreGap)", + "example" : [ + -4 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--scoreGapATAC", + "description" : "AT/AC and GT/AT junction penalty (in addition to scoreGap)", + "example" : [ + -8 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--scoreGenomicLengthLog2scale", + "description" : "extra score logarithmically scaled with genomic length of the alignment: scoreGenomicLengthLog2scale*log2(genomicLength)", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--scoreDelOpen", + "description" : "deletion open penalty", + "example" : [ + -2 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--scoreDelBase", + "description" : "deletion extension penalty per base (in addition to scoreDelOpen)", + "example" : [ + -2 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--scoreInsOpen", + "description" : "insertion open penalty", + "example" : [ + -2 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--scoreInsBase", + "description" : "insertion extension penalty per base (in addition to scoreInsOpen)", + "example" : [ + -2 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--scoreStitchSJshift", + "description" : "maximum score reduction while searching for SJ boundaries in the stitching step", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Alignments and Seeding", + "arguments" : [ + { + "type" : "integer", + "name" : "--seedSearchStartLmax", + "description" : "defines the search start point through the read - the read is split into pieces no longer than this value", + "example" : [ + 50 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--seedSearchStartLmaxOverLread", + "description" : "seedSearchStartLmax normalized to read length (sum of mates' lengths for paired-end reads)", + "example" : [ + 1.0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--seedSearchLmax", + "description" : "defines the maximum length of the seeds, if =0 seed length is not limited", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--seedMultimapNmax", + "description" : "only pieces that map fewer than this value are utilized in the stitching procedure", + "example" : [ + 10000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--seedPerReadNmax", + "description" : "max number of seeds per read", + "example" : [ + 1000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--seedPerWindowNmax", + "description" : "max number of seeds per window", + "example" : [ + 50 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--seedNoneLociPerWindow", + "description" : "max number of one seed loci per window", + "example" : [ + 10 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--seedSplitMin", + "description" : "min length of the seed sequences split by Ns or mate gap", + "example" : [ + 12 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--seedMapMin", + "description" : "min length of seeds to be mapped", + "example" : [ + 5 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--alignIntronMin", + "description" : "minimum intron size, genomic gap is considered intron if its length>=alignIntronMin, otherwise it is considered Deletion", + "example" : [ + 21 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--alignIntronMax", + "description" : "maximum intron size, if 0, max intron size will be determined by (2^winBinNbits)*winAnchorDistNbins", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--alignMatesGapMax", + "description" : "maximum gap between two mates, if 0, max intron gap will be determined by (2^winBinNbits)*winAnchorDistNbins", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--alignSJoverhangMin", + "description" : "minimum overhang (i.e. block size) for spliced alignments", + "example" : [ + 5 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--alignSJstitchMismatchNmax", + "description" : "maximum number of mismatches for stitching of the splice junctions (-1: no limit).\n\n(1) non-canonical motifs, (2) GT/AG and CT/AC motif, (3) GC/AG and CT/GC motif, (4) AT/AC and GT/AT motif.", + "example" : [ + 0, + -1, + 0, + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--alignSJDBoverhangMin", + "description" : "minimum overhang (i.e. block size) for annotated (sjdb) spliced alignments", + "example" : [ + 3 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--alignSplicedMateMapLmin", + "description" : "minimum mapped length for a read mate that is spliced", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--alignSplicedMateMapLminOverLmate", + "description" : "alignSplicedMateMapLmin normalized to mate length", + "example" : [ + 0.66 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--alignWindowsPerReadNmax", + "description" : "max number of windows per read", + "example" : [ + 10000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--alignTranscriptsPerWindowNmax", + "description" : "max number of transcripts per window", + "example" : [ + 100 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--alignTranscriptsPerReadNmax", + "description" : "max number of different alignments per read to consider", + "example" : [ + 10000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--alignEndsType", + "description" : "type of read ends alignment\n\n- Local ... standard local alignment with soft-clipping allowed\n- EndToEnd ... force end-to-end read alignment, do not soft-clip\n- Extend5pOfRead1 ... fully extend only the 5p of the read1, all other ends: local alignment\n- Extend5pOfReads12 ... fully extend only the 5p of the both read1 and read2, all other ends: local alignment", + "example" : [ + "Local" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--alignEndsProtrude", + "description" : "allow protrusion of alignment ends, i.e. start (end) of the +strand mate downstream of the start (end) of the -strand mate\n\n1st word: int: maximum number of protrusion bases allowed\n2nd word: string:\n- ConcordantPair ... report alignments with non-zero protrusion as concordant pairs\n- DiscordantPair ... report alignments with non-zero protrusion as discordant pairs", + "example" : [ + "0 ConcordantPair" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--alignSoftClipAtReferenceEnds", + "description" : "allow the soft-clipping of the alignments past the end of the chromosomes\n\n- Yes ... allow\n- No ... prohibit, useful for compatibility with Cufflinks", + "example" : [ + "Yes" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--alignInsertionFlush", + "description" : "how to flush ambiguous insertion positions\n\n- None ... insertions are not flushed\n- Right ... insertions are flushed to the right", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Paired-End reads", + "arguments" : [ + { + "type" : "integer", + "name" : "--peOverlapNbasesMin", + "description" : "minimum number of overlapping bases to trigger mates merging and realignment. Specify >0 value to switch on the \\"merginf of overlapping mates\\" algorithm.", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--peOverlapMMp", + "description" : "maximum proportion of mismatched bases in the overlap area", + "example" : [ + 0.01 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Windows, Anchors, Binning", + "arguments" : [ + { + "type" : "integer", + "name" : "--winAnchorMultimapNmax", + "description" : "max number of loci anchors are allowed to map to", + "example" : [ + 50 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--winBinNbits", + "description" : "=log2(winBin), where winBin is the size of the bin for the windows/clustering, each window will occupy an integer number of bins.", + "example" : [ + 16 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--winAnchorDistNbins", + "description" : "max number of bins between two anchors that allows aggregation of anchors into one window", + "example" : [ + 9 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--winFlankNbins", + "description" : "log2(winFlank), where win Flank is the size of the left and right flanking regions for each window", + "example" : [ + 4 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "double", + "name" : "--winReadCoverageRelativeMin", + "description" : "minimum relative coverage of the read sequence by the seeds in a window, for STARlong algorithm only.", + "example" : [ + 0.5 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--winReadCoverageBasesMin", + "description" : "minimum number of bases covered by the seeds in a window , for STARlong algorithm only.", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Chimeric Alignments", + "arguments" : [ + { + "type" : "string", + "name" : "--chimOutType", + "description" : "type of chimeric output\n\n- Junctions ... Chimeric.out.junction\n- SeparateSAMold ... output old SAM into separate Chimeric.out.sam file\n- WithinBAM ... output into main aligned BAM files (Aligned.*.bam)\n- WithinBAM HardClip ... (defaul''' + '''t) hard-clipping in the CIGAR for supplemental chimeric alignments (default if no 2nd word is present)\n- WithinBAM SoftClip ... soft-clipping in the CIGAR for supplemental chimeric alignments", + "example" : [ + "Junctions" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimSegmentMin", + "description" : "minimum length of chimeric segment length, if ==0, no chimeric output", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimScoreMin", + "description" : "minimum total (summed) score of the chimeric segments", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimScoreDropMax", + "description" : "max drop (difference) of chimeric score (the sum of scores of all chimeric segments) from the read length", + "example" : [ + 20 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimScoreSeparation", + "description" : "minimum difference (separation) between the best chimeric score and the next one", + "example" : [ + 10 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimScoreJunctionNonGTAG", + "description" : "penalty for a non-GT/AG chimeric junction", + "example" : [ + -1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimJunctionOverhangMin", + "description" : "minimum overhang for a chimeric junction", + "example" : [ + 20 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimSegmentReadGapMax", + "description" : "maximum gap in the read sequence between chimeric segments", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--chimFilter", + "description" : "different filters for chimeric alignments\n\n- None ... no filtering\n- banGenomicN ... Ns are not allowed in the genome sequence around the chimeric junction", + "example" : [ + "banGenomicN" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimMainSegmentMultNmax", + "description" : "maximum number of multi-alignments for the main chimeric segment. =1 will prohibit multimapping main segments.", + "example" : [ + 10 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimMultimapNmax", + "description" : "maximum number of chimeric multi-alignments\n\n- 0 ... use the old scheme for chimeric detection which only considered unique alignments", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimMultimapScoreRange", + "description" : "the score range for multi-mapping chimeras below the best chimeric score. Only works with --chimMultimapNmax > 1", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimNonchimScoreDropMin", + "description" : "to trigger chimeric detection, the drop in the best non-chimeric alignment score with respect to the read length has to be greater than this value", + "example" : [ + 20 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--chimOutJunctionFormat", + "description" : "formatting type for the Chimeric.out.junction file\n\n- 0 ... no comment lines/headers\n- 1 ... comment lines at the end of the file: command line and Nreads: total, unique/multi-mapping", + "example" : [ + 0 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Quantification of Annotations", + "arguments" : [ + { + "type" : "string", + "name" : "--quantMode", + "description" : "types of quantification requested\n\n- - ... none\n- TranscriptomeSAM ... output SAM/BAM alignments to transcriptome into a separate file\n- GeneCounts ... count reads per gene", + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--quantTranscriptomeBAMcompression", + "description" : "-2 to 10 transcriptome BAM compression level\n\n- -2 ... no BAM output\n- -1 ... default compression (6?)\n- 0 ... no compression\n- 10 ... maximum compression", + "example" : [ + 1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--quantTranscriptomeSAMoutput", + "description" : "alignment filtering for TranscriptomeSAM output\n\n- BanSingleEnd_BanIndels_ExtendSoftclip ... prohibit indels and single-end alignments, extend softclips - compatible with RSEM\n- BanSingleEnd ... prohibit single-end alignments, allow indels and softclips\n- BanSingleEnd_ExtendSoftclip ... prohibit single-end alignments, extend softclips, allow indels", + "example" : [ + "BanSingleEnd_BanIndels_ExtendSoftclip" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "2-pass Mapping", + "arguments" : [ + { + "type" : "string", + "name" : "--twopassMode", + "description" : "2-pass mapping mode.\n\n- None ... 1-pass mapping\n- Basic ... basic 2-pass mapping, with all 1st pass junctions inserted into the genome indices on the fly", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--twopass1readsN", + "description" : "number of reads to process for the 1st step. Use very large number (or default -1) to map all reads in the first step.", + "example" : [ + -1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "WASP parameters", + "arguments" : [ + { + "type" : "string", + "name" : "--waspOutputMode", + "description" : "WASP allele-specific output type. This is re-implementation of the original WASP mappability filtering by Bryce van de Geijn, Graham McVicker, Yoav Gilad & Jonathan K Pritchard. Please cite the original WASP paper: Nature Methods 12, 1061-1063 (2015), https://www.nature.com/articles/nmeth.3582 .\n\n- SAMtag ... add WASP tags to the alignments that pass WASP filtering", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Inputs", + "arguments" : [ + { + "type" : "file", + "name" : "--input", + "alternatives" : [ + "--readFilesIn" + ], + "description" : "The single-end or paired-end R1 FastQ files to be processed.", + "example" : [ + "mysample_S1_L001_R1_001.fastq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--input_r2", + "description" : "The paired-end R2 FastQ files to be processed. Only required if --input is a paired-end R1 file.", + "example" : [ + "mysample_S1_L001_R2_001.fastq.gz" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Outputs", + "arguments" : [ + { + "type" : "file", + "name" : "--aligned_reads", + "description" : "The output file containing the aligned reads.", + "example" : [ + "aligned_reads.bam" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--reads_per_gene", + "description" : "The output file containing the number of reads per gene.", + "example" : [ + "reads_per_gene.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--unmapped", + "description" : "The output file containing the unmapped reads.", + "example" : [ + "unmapped.fastq" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--unmapped_r2", + "description" : "The output file containing the unmapped R2 reads.", + "example" : [ + "unmapped_r2.fastq" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--chimeric_junctions", + "description" : "The output file containing the chimeric junctions.", + "example" : [ + "chimeric_junctions.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--log", + "description" : "The output file containing the log of the alignment process.", + "example" : [ + "log.txt" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--splice_junctions", + "description" : "The output file containing the splice junctions.", + "example" : [ + "splice_junctions.tsv" + ], + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "python_script", + "path" : "script.py", + "is_executable" : true + } + ], + "description" : "Aligns reads to a reference genome using STAR.\n", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "align", + "fasta", + "genome" + ], + "license" : "MIT", + "references" : { + "doi" : [ + "10.1093/bioinformatics/bts635" + ] + }, + "links" : { + "repository" : "https://github.com/alexdobin/STAR", + "documentation" : "https://github.com/alexdobin/STAR/blob/master/doc/STARmanual.pdf" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "python:3.12-slim", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "apt", + "packages" : [ + "procps", + "gzip", + "bzip2" + ], + "interactive" : false + }, + { + "type" : "docker", + "run" : [ + "apt-get update && \\\\\n apt-get install -y --no-install-recommends ${PACKAGES} && \\\\\n cd /tmp && \\\\\n wget --no-check-certificate https://github.com/alexdobin/STAR/archive/refs/tags/${STAR_VERSION}.zip && \\\\\n unzip ${STAR_VERSION}.zip && \\\\\n cd STAR-${STAR_VERSION}/source && \\\\\n make STARstatic CXXFLAGS_SIMD=-std=c++11 && \\\\\n cp STAR /usr/local/bin && \\\\\n cd / && \\\\\n rm -rf /tmp/STAR-${STAR_VERSION} /tmp/${STAR_VERSION}.zip && \\\\\n apt-get --purge autoremove -y ${PACKAGES} && \\\\\n apt-get clean\n" + ], + "env" : [ + "STAR_VERSION 2.7.11b", + "PACKAGES gcc g++ make wget zlib1g-dev unzip xxd" + ] + }, + { + "type" : "docker", + "run" : [ + "STAR --version | sed 's#\\\\(.*\\\\)#star: \\"\\\\1\\"#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/star/star_align_reads/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/star/star_align_reads", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +import tempfile +import subprocess +import shutil +from pathlib import Path + +## VIASH START +# The following code has been auto-generated by Viash. +par = { + 'runRNGseed': $( if [ ! -z ${VIASH_PAR_RUNRNGSEED+x} ]; then echo "int(r'${VIASH_PAR_RUNRNGSEED//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'genomeDir': $( if [ ! -z ${VIASH_PAR_GENOMEDIR+x} ]; then echo "r'${VIASH_PAR_GENOMEDIR//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'genomeLoad': $( if [ ! -z ${VIASH_PAR_GENOMELOAD+x} ]; then echo "r'${VIASH_PAR_GENOMELOAD//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'genomeFastaFiles': $( if [ ! -z ${VIASH_PAR_GENOMEFASTAFILES+x} ]; then echo "r'${VIASH_PAR_GENOMEFASTAFILES//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'genomeFileSizes': $( if [ ! -z ${VIASH_PAR_GENOMEFILESIZES+x} ]; then echo "list(map(int, r'${VIASH_PAR_GENOMEFILESIZES//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'genomeTransformOutput': $( if [ ! -z ${VIASH_PAR_GENOMETRANSFORMOUTPUT+x} ]; then echo "r'${VIASH_PAR_GENOMETRANSFORMOUTPUT//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'genomeChrSetMitochondrial': $( if [ ! -z ${VIASH_PAR_GENOMECHRSETMITOCHONDRIAL+x} ]; then echo "r'${VIASH_PAR_GENOMECHRSETMITOCHONDRIAL//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'sjdbFileChrStartEnd': $( if [ ! -z ${VIASH_PAR_SJDBFILECHRSTARTEND+x} ]; then echo "r'${VIASH_PAR_SJDBFILECHRSTARTEND//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'sjdbGTFfile': $( if [ ! -z ${VIASH_PAR_SJDBGTFFILE+x} ]; then echo "r'${VIASH_PAR_SJDBGTFFILE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'sjdbGTFchrPrefix': $( if [ ! -z ${VIASH_PAR_SJDBGTFCHRPREFIX+x} ]; then echo "r'${VIASH_PAR_SJDBGTFCHRPREFIX//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'sjdbGTFfeatureExon': $( if [ ! -z ${VIASH_PAR_SJDBGTFFEATUREEXON+x} ]; then echo "r'${VIASH_PAR_SJDBGTFFEATUREEXON//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'sjdbGTFtagExonParentTranscript': $( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT+x} ]; then echo "r'${VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'sjdbGTFtagExonParentGene': $( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENE+x} ]; then echo "r'${VIASH_PAR_SJDBGTFTAGEXONPARENTGENE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'sjdbGTFtagExonParentGeneName': $( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME+x} ]; then echo "r'${VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'sjdbGTFtagExonParentGeneType': $( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE+x} ]; then echo "r'${VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'sjdbOverhang': $( if [ ! -z ${VIASH_PAR_SJDBOVERHANG+x} ]; then echo "int(r'${VIASH_PAR_SJDBOVERHANG//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'sjdbScore': $( if [ ! -z ${VIASH_PAR_SJDBSCORE+x} ]; then echo "int(r'${VIASH_PAR_SJDBSCORE//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'sjdbInsertSave': $( if [ ! -z ${VIASH_PAR_SJDBINSERTSAVE+x} ]; then echo "r'${VIASH_PAR_SJDBINSERTSAVE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'varVCFfile': $( if [ ! -z ${VIASH_PAR_VARVCFFILE+x} ]; then echo "r'${VIASH_PAR_VARVCFFILE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'readFilesType': $( if [ ! -z ${VIASH_PAR_READFILESTYPE+x} ]; then echo "r'${VIASH_PAR_READFILESTYPE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'readFilesSAMattrKeep': $( if [ ! -z ${VIASH_PAR_READFILESSAMATTRKEEP+x} ]; then echo "r'${VIASH_PAR_READFILESSAMATTRKEEP//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'readFilesManifest': $( if [ ! -z ${VIASH_PAR_READFILESMANIFEST+x} ]; then echo "r'${VIASH_PAR_READFILESMANIFEST//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'readFilesPrefix': $( if [ ! -z ${VIASH_PAR_READFILESPREFIX+x} ]; then echo "r'${VIASH_PAR_READFILESPREFIX//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'readFilesCommand': $( if [ ! -z ${VIASH_PAR_READFILESCOMMAND+x} ]; then echo "r'${VIASH_PAR_READFILESCOMMAND//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'readMapNumber': $( if [ ! -z ${VIASH_PAR_READMAPNUMBER+x} ]; then echo "int(r'${VIASH_PAR_READMAPNUMBER//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'readMatesLengthsIn': $( if [ ! -z ${VIASH_PAR_READMATESLENGTHSIN+x} ]; then echo "r'${VIASH_PAR_READMATESLENGTHSIN//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'readNameSeparator': $( if [ ! -z ${VIASH_PAR_READNAMESEPARATOR+x} ]; then echo "r'${VIASH_PAR_READNAMESEPARATOR//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'readQualityScoreBase': $( if [ ! -z ${VIASH_PAR_READQUALITYSCOREBASE+x} ]; then echo "int(r'${VIASH_PAR_READQUALITYSCOREBASE//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'clipAdapterType': $( if [ ! -z ${VIASH_PAR_CLIPADAPTERTYPE+x} ]; then echo "r'${VIASH_PAR_CLIPADAPTERTYPE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'clip3pNbases': $( if [ ! -z ${VIASH_PAR_CLIP3PNBASES+x} ]; then echo "list(map(int, r'${VIASH_PAR_CLIP3PNBASES//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'clip3pAdapterSeq': $( if [ ! -z ${VIASH_PAR_CLIP3PADAPTERSEQ+x} ]; then echo "r'${VIASH_PAR_CLIP3PADAPTERSEQ//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'clip3pAdapterMMp': $( if [ ! -z ${VIASH_PAR_CLIP3PADAPTERMMP+x} ]; then echo "list(map(float, r'${VIASH_PAR_CLIP3PADAPTERMMP//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'clip3pAfterAdapterNbases': $( if [ ! -z ${VIASH_PAR_CLIP3PAFTERADAPTERNBASES+x} ]; then echo "list(map(int, r'${VIASH_PAR_CLIP3PAFTERADAPTERNBASES//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'clip5pNbases': $( if [ ! -z ${VIASH_PAR_CLIP5PNBASES+x} ]; then echo "list(map(int, r'${VIASH_PAR_CLIP5PNBASES//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'limitGenomeGenerateRAM': $( if [ ! -z ${VIASH_PAR_LIMITGENOMEGENERATERAM+x} ]; then echo "int(r'${VIASH_PAR_LIMITGENOMEGENERATERAM//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'limitIObufferSize': $( if [ ! -z ${VIASH_PAR_LIMITIOBUFFERSIZE+x} ]; then echo "list(map(int, r'${VIASH_PAR_LIMITIOBUFFERSIZE//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'limitOutSAMoneReadBytes': $( if [ ! -z ${VIASH_PAR_LIMITOUTSAMONEREADBYTES+x} ]; then echo "int(r'${VIASH_PAR_LIMITOUTSAMONEREADBYTES//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'limitOutSJoneRead': $( if [ ! -z ${VIASH_PAR_LIMITOUTSJONEREAD+x} ]; then echo "int(r'${VIASH_PAR_LIMITOUTSJONEREAD//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'limitOutSJcollapsed': $( if [ ! -z ${VIASH_PAR_LIMITOUTSJCOLLAPSED+x} ]; then echo "int(r'${VIASH_PAR_LIMITOUTSJCOLLAPSED//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'limitBAMsortRAM': $( if [ ! -z ${VIASH_PAR_LIMITBAMSORTRAM+x} ]; then echo "int(r'${VIASH_PAR_LIMITBAMSORTRAM//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'limitSjdbInsertNsj': $( if [ ! -z ${VIASH_PAR_LIMITSJDBINSERTNSJ+x} ]; then echo "int(r'${VIASH_PAR_LIMITSJDBINSERTNSJ//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'limitNreadsSoft': $( if [ ! -z ${VIASH_PAR_LIMITNREADSSOFT+x} ]; then echo "int(r'${VIASH_PAR_LIMITNREADSSOFT//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outTmpKeep': $( if [ ! -z ${VIASH_PAR_OUTTMPKEEP+x} ]; then echo "r'${VIASH_PAR_OUTTMPKEEP//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outStd': $( if [ ! -z ${VIASH_PAR_OUTSTD+x} ]; then echo "r'${VIASH_PAR_OUTSTD//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outReadsUnmapped': $( if [ ! -z ${VIASH_PAR_OUTREADSUNMAPPED+x} ]; then echo "r'${VIASH_PAR_OUTREADSUNMAPPED//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outQSconversionAdd': $( if [ ! -z ${VIASH_PAR_OUTQSCONVERSIONADD+x} ]; then echo "int(r'${VIASH_PAR_OUTQSCONVERSIONADD//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outMultimapperOrder': $( if [ ! -z ${VIASH_PAR_OUTMULTIMAPPERORDER+x} ]; then echo "r'${VIASH_PAR_OUTMULTIMAPPERORDER//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outSAMtype': $( if [ ! -z ${VIASH_PAR_OUTSAMTYPE+x} ]; then echo "r'${VIASH_PAR_OUTSAMTYPE//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'outSAMmode': $( if [ ! -z ${VIASH_PAR_OUTSAMMODE+x} ]; then echo "r'${VIASH_PAR_OUTSAMMODE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outSAMstrandField': $( if [ ! -z ${VIASH_PAR_OUTSAMSTRANDFIELD+x} ]; then echo "r'${VIASH_PAR_OUTSAMSTRANDFIELD//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outSAMattributes': $( if [ ! -z ${VIASH_PAR_OUTSAMATTRIBUTES+x} ]; then echo "r'${VIASH_PAR_OUTSAMATTRIBUTES//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'outSAMattrIHstart': $( if [ ! -z ${VIASH_PAR_OUTSAMATTRIHSTART+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMATTRIHSTART//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outSAMunmapped': $( if [ ! -z ${VIASH_PAR_OUTSAMUNMAPPED+x} ]; then echo "r'${VIASH_PAR_OUTSAMUNMAPPED//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'outSAMorder': $( if [ ! -z ${VIASH_PAR_OUTSAMORDER+x} ]; then echo "r'${VIASH_PAR_OUTSAMORDER//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outSAMprimaryFlag': $( if [ ! -z ${VIASH_PAR_OUTSAMPRIMARYFLAG+x} ]; then echo "r'${VIASH_PAR_OUTSAMPRIMARYFLAG//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outSAMreadID': $( if [ ! -z ${VIASH_PAR_OUTSAMREADID+x} ]; then echo "r'${VIASH_PAR_OUTSAMREADID//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outSAMmapqUnique': $( if [ ! -z ${VIASH_PAR_OUTSAMMAPQUNIQUE+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMMAPQUNIQUE//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outSAMflagOR': $( if [ ! -z ${VIASH_PAR_OUTSAMFLAGOR+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMFLAGOR//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outSAMflagAND': $( if [ ! -z ${VIASH_PAR_OUTSAMFLAGAND+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMFLAGAND//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outSAMattrRGline': $( if [ ! -z ${VIASH_PAR_OUTSAMATTRRGLINE+x} ]; then echo "r'${VIASH_PAR_OUTSAMATTRRGLINE//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'outSAMheaderHD': $( if [ ! -z ${VIASH_PAR_OUTSAMHEADERHD+x} ]; then echo "r'${VIASH_PAR_OUTSAMHEADERHD//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'outSAMheaderPG': $( if [ ! -z ${VIASH_PAR_OUTSAMHEADERPG+x} ]; then echo "r'${VIASH_PAR_OUTSAMHEADERPG//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'outSAMheaderCommentFile': $( if [ ! -z ${VIASH_PAR_OUTSAMHEADERCOMMENTFILE+x} ]; then echo "r'${VIASH_PAR_OUTSAMHEADERCOMMENTFILE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outSAMfilter': $( if [ ! -z ${VIASH_PAR_OUTSAMFILTER+x} ]; then echo "r'${VIASH_PAR_OUTSAMFILTER//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'outSAMmultNmax': $( if [ ! -z ${VIASH_PAR_OUTSAMMULTNMAX+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMMULTNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outSAMtlen': $( if [ ! -z ${VIASH_PAR_OUTSAMTLEN+x} ]; then echo "int(r'${VIASH_PAR_OUTSAMTLEN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outBAMcompression': $( if [ ! -z ${VIASH_PAR_OUTBAMCOMPRESSION+x} ]; then echo "int(r'${VIASH_PAR_OUTBAMCOMPRESSION//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outBAMsortingThreadN': $( if [ ! -z ${VIASH_PAR_OUTBAMSORTINGTHREADN+x} ]; then echo "int(r'${VIASH_PAR_OUTBAMSORTINGTHREADN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outBAMsortingBinsN': $( if [ ! -z ${VIASH_PAR_OUTBAMSORTINGBINSN+x} ]; then echo "int(r'${VIASH_PAR_OUTBAMSORTINGBINSN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'bamRemoveDuplicatesType': $( if [ ! -z ${VIASH_PAR_BAMREMOVEDUPLICATESTYPE+x} ]; then echo "r'${VIASH_PAR_BAMREMOVEDUPLICATESTYPE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'bamRemoveDuplicatesMate2basesN': $( if [ ! -z ${VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN+x} ]; then echo "int(r'${VIASH_PAR_BAMREMOVEDUPLICATESMATE2BASESN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outWigType': $( if [ ! -z ${VIASH_PAR_OUTWIGTYPE+x} ]; then echo "r'${VIASH_PAR_OUTWIGTYPE//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'outWigStrand': $( if [ ! -z ${VIASH_PAR_OUTWIGSTRAND+x} ]; then echo "r'${VIASH_PAR_OUTWIGSTRAND//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outWigReferencesPrefix': $( if [ ! -z ${VIASH_PAR_OUTWIGREFERENCESPREFIX+x} ]; then echo "r'${VIASH_PAR_OUTWIGREFERENCESPREFIX//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outWigNorm': $( if [ ! -z ${VIASH_PAR_OUTWIGNORM+x} ]; then echo "r'${VIASH_PAR_OUTWIGNORM//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outFilterType': $( if [ ! -z ${VIASH_PAR_OUTFILTERTYPE+x} ]; then echo "r'${VIASH_PAR_OUTFILTERTYPE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outFilterMultimapScoreRange': $( if [ ! -z ${VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE+x} ]; then echo "int(r'${VIASH_PAR_OUTFILTERMULTIMAPSCORERANGE//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outFilterMultimapNmax': $( if [ ! -z ${VIASH_PAR_OUTFILTERMULTIMAPNMAX+x} ]; then echo "int(r'${VIASH_PAR_OUTFILTERMULTIMAPNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outFilterMismatchNmax': $( if [ ! -z ${VIASH_PAR_OUTFILTERMISMATCHNMAX+x} ]; then echo "int(r'${VIASH_PAR_OUTFILTERMISMATCHNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outFilterMismatchNoverLmax': $( if [ ! -z ${VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX+x} ]; then echo "float(r'${VIASH_PAR_OUTFILTERMISMATCHNOVERLMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outFilterMismatchNoverReadLmax': $( if [ ! -z ${VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX+x} ]; then echo "float(r'${VIASH_PAR_OUTFILTERMISMATCHNOVERREADLMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outFilterScoreMin': $( if [ ! -z ${VIASH_PAR_OUTFILTERSCOREMIN+x} ]; then echo "int(r'${VIASH_PAR_OUTFILTERSCOREMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outFilterScoreMinOverLread': $( if [ ! -z ${VIASH_PAR_OUTFILTERSCOREMINOVERLREAD+x} ]; then echo "float(r'${VIASH_PAR_OUTFILTERSCOREMINOVERLREAD//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outFilterMatchNmin': $( if [ ! -z ${VIASH_PAR_OUTFILTERMATCHNMIN+x} ]; then echo "int(r'${VIASH_PAR_OUTFILTERMATCHNMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outFilterMatchNminOverLread': $( if [ ! -z ${VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD+x} ]; then echo "float(r'${VIASH_PAR_OUTFILTERMATCHNMINOVERLREAD//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'outFilterIntronMotifs': $( if [ ! -z ${VIASH_PAR_OUTFILTERINTRONMOTIFS+x} ]; then echo "r'${VIASH_PAR_OUTFILTERINTRONMOTIFS//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outFilterIntronStrands': $( if [ ! -z ${VIASH_PAR_OUTFILTERINTRONSTRANDS+x} ]; then echo "r'${VIASH_PAR_OUTFILTERINTRONSTRANDS//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outSJtype': $( if [ ! -z ${VIASH_PAR_OUTSJTYPE+x} ]; then echo "r'${VIASH_PAR_OUTSJTYPE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outSJfilterReads': $( if [ ! -z ${VIASH_PAR_OUTSJFILTERREADS+x} ]; then echo "r'${VIASH_PAR_OUTSJFILTERREADS//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'outSJfilterOverhangMin': $( if [ ! -z ${VIASH_PAR_OUTSJFILTEROVERHANGMIN+x} ]; then echo "list(map(int, r'${VIASH_PAR_OUTSJFILTEROVERHANGMIN//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'outSJfilterCountUniqueMin': $( if [ ! -z ${VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN+x} ]; then echo "list(map(int, r'${VIASH_PAR_OUTSJFILTERCOUNTUNIQUEMIN//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'outSJfilterCountTotalMin': $( if [ ! -z ${VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN+x} ]; then echo "list(map(int, r'${VIASH_PAR_OUTSJFILTERCOUNTTOTALMIN//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'outSJfilterDistToOtherSJmin': $( if [ ! -z ${VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN+x} ]; then echo "list(map(int, r'${VIASH_PAR_OUTSJFILTERDISTTOOTHERSJMIN//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'outSJfilterIntronMaxVsReadN': $( if [ ! -z ${VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN+x} ]; then echo "list(map(int, r'${VIASH_PAR_OUTSJFILTERINTRONMAXVSREADN//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'scoreGap': $( if [ ! -z ${VIASH_PAR_SCOREGAP+x} ]; then echo "int(r'${VIASH_PAR_SCOREGAP//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'scoreGapNoncan': $( if [ ! -z ${VIASH_PAR_SCOREGAPNONCAN+x} ]; then echo "int(r'${VIASH_PAR_SCOREGAPNONCAN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'scoreGapGCAG': $( if [ ! -z ${VIASH_PAR_SCOREGAPGCAG+x} ]; then echo "int(r'${VIASH_PAR_SCOREGAPGCAG//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'scoreGapATAC': $( if [ ! -z ${VIASH_PAR_SCOREGAPATAC+x} ]; then echo "int(r'${VIASH_PAR_SCOREGAPATAC//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'scoreGenomicLengthLog2scale': $( if [ ! -z ${VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE+x} ]; then echo "int(r'${VIASH_PAR_SCOREGENOMICLENGTHLOG2SCALE//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'scoreDelOpen': $( if [ ! -z ${VIASH_PAR_SCOREDELOPEN+x} ]; then echo "int(r'${VIASH_PAR_SCOREDELOPEN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'scoreDelBase': $( if [ ! -z ${VIASH_PAR_SCOREDELBASE+x} ]; then echo "int(r'${VIASH_PAR_SCOREDELBASE//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'scoreInsOpen': $( if [ ! -z ${VIASH_PAR_SCOREINSOPEN+x} ]; then echo "int(r'${VIASH_PAR_SCOREINSOPEN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'scoreInsBase': $( if [ ! -z ${VIASH_PAR_SCOREINSBASE+x} ]; then echo "int(r'${VIASH_PAR_SCOREINSBASE//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'scoreStitchSJshift': $( if [ ! -z ${VIASH_PAR_SCORESTITCHSJSHIFT+x} ]; then echo "int(r'${VIASH_PAR_SCORESTITCHSJSHIFT//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'seedSearchStartLmax': $( if [ ! -z ${VIASH_PAR_SEEDSEARCHSTARTLMAX+x} ]; then echo "int(r'${VIASH_PAR_SEEDSEARCHSTARTLMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'seedSearchStartLmaxOverLread': $( if [ ! -z ${VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD+x} ]; then echo "float(r'${VIASH_PAR_SEEDSEARCHSTARTLMAXOVERLREAD//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'seedSearchLmax': $( if [ ! -z ${VIASH_PAR_SEEDSEARCHLMAX+x} ]; then echo "int(r'${VIASH_PAR_SEEDSEARCHLMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'seedMultimapNmax': $( if [ ! -z ${VIASH_PAR_SEEDMULTIMAPNMAX+x} ]; then echo "int(r'${VIASH_PAR_SEEDMULTIMAPNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'seedPerReadNmax': $( if [ ! -z ${VIASH_PAR_SEEDPERREADNMAX+x} ]; then echo "int(r'${VIASH_PAR_SEEDPERREADNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'seedPerWindowNmax': $( if [ ! -z ${VIASH_PAR_SEEDPERWINDOWNMAX+x} ]; then echo "int(r'${VIASH_PAR_SEEDPERWINDOWNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'seedNoneLociPerWindow': $( if [ ! -z ${VIASH_PAR_SEEDNONELOCIPERWINDOW+x} ]; then echo "int(r'${VIASH_PAR_SEEDNONELOCIPERWINDOW//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'seedSplitMin': $( if [ ! -z ${VIASH_PAR_SEEDSPLITMIN+x} ]; then echo "int(r'${VIASH_PAR_SEEDSPLITMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'seedMapMin': $( if [ ! -z ${VIASH_PAR_SEEDMAPMIN+x} ]; then echo "int(r'${VIASH_PAR_SEEDMAPMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'alignIntronMin': $( if [ ! -z ${VIASH_PAR_ALIGNINTRONMIN+x} ]; then echo "int(r'${VIASH_PAR_ALIGNINTRONMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'alignIntronMax': $( if [ ! -z ${VIASH_PAR_ALIGNINTRONMAX+x} ]; then echo "int(r'${VIASH_PAR_ALIGNINTRONMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'alignMatesGapMax': $( if [ ! -z ${VIASH_PAR_ALIGNMATESGAPMAX+x} ]; then echo "int(r'${VIASH_PAR_ALIGNMATESGAPMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'alignSJoverhangMin': $( if [ ! -z ${VIASH_PAR_ALIGNSJOVERHANGMIN+x} ]; then echo "int(r'${VIASH_PAR_ALIGNSJOVERHANGMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'alignSJstitchMismatchNmax': $( if [ ! -z ${VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX+x} ]; then echo "list(map(int, r'${VIASH_PAR_ALIGNSJSTITCHMISMATCHNMAX//\\'/\\'\\"\\'\\"r\\'}'.split(';')))"; else echo None; fi ), + 'alignSJDBoverhangMin': $( if [ ! -z ${VIASH_PAR_ALIGNSJDBOVERHANGMIN+x} ]; then echo "int(r'${VIASH_PAR_ALIGNSJDBOVERHANGMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'alignSplicedMateMapLmin': $( if [ ! -z ${VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN+x} ]; then echo "int(r'${VIASH_PAR_ALIGNSPLICEDMATEMAPLMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'alignSplicedMateMapLminOverLmate': $( if [ ! -z ${VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE+x} ]; then echo "float(r'${VIASH_PAR_ALIGNSPLICEDMATEMAPLMINOVERLMATE//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'alignWindowsPerReadNmax': $( if [ ! -z ${VIASH_PAR_ALIGNWINDOWSPERREADNMAX+x} ]; then echo "int(r'${VIASH_PAR_ALIGNWINDOWSPERREADNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'alignTranscriptsPerWindowNmax': $( if [ ! -z ${VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX+x} ]; then echo "int(r'${VIASH_PAR_ALIGNTRANSCRIPTSPERWINDOWNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'alignTranscriptsPerReadNmax': $( if [ ! -z ${VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX+x} ]; then echo "int(r'${VIASH_PAR_ALIGNTRANSCRIPTSPERREADNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'alignEndsType': $( if [ ! -z ${VIASH_PAR_ALIGNENDSTYPE+x} ]; then echo "r'${VIASH_PAR_ALIGNENDSTYPE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'alignEndsProtrude': $( if [ ! -z ${VIASH_PAR_ALIGNENDSPROTRUDE+x} ]; then echo "r'${VIASH_PAR_ALIGNENDSPROTRUDE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'alignSoftClipAtReferenceEnds': $( if [ ! -z ${VIASH_PAR_ALIGNSOFTCLIPATREFERENCEENDS+x} ]; then echo "r'${VIASH_PAR_ALIGNSOFTCLIPATREFERENCEENDS//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'alignInsertionFlush': $( if [ ! -z ${VIASH_PAR_ALIGNINSERTIONFLUSH+x} ]; then echo "r'${VIASH_PAR_ALIGNINSERTIONFLUSH//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'peOverlapNbasesMin': $( if [ ! -z ${VIASH_PAR_PEOVERLAPNBASESMIN+x} ]; then echo "int(r'${VIASH_PAR_PEOVERLAPNBASESMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'peOverlapMMp': $( if [ ! -z ${VIASH_PAR_PEOVERLAPMMP+x} ]; then echo "float(r'${VIASH_PAR_PEOVERLAPMMP//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'winAnchorMultimapNmax': $( if [ ! -z ${VIASH_PAR_WINANCHORMULTIMAPNMAX+x} ]; then echo "int(r'${VIASH_PAR_WINANCHORMULTIMAPNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'winBinNbits': $( if [ ! -z ${VIASH_PAR_WINBINNBITS+x} ]; then echo "int(r'${VIASH_PAR_WINBINNBITS//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'winAnchorDistNbins': $( if [ ! -z ${VIASH_PAR_WINANCHORDISTNBINS+x} ]; then echo "int(r'${VIASH_PAR_WINANCHORDISTNBINS//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'winFlankNbins': $( if [ ! -z ${VIASH_PAR_WINFLANKNBINS+x} ]; then echo "int(r'${VIASH_PAR_WINFLANKNBINS//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'winReadCoverageRelativeMin': $( if [ ! -z ${VIASH_PAR_WINREADCOVERAGERELATIVEMIN+x} ]; then echo "float(r'${VIASH_PAR_WINREADCOVERAGERELATIVEMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'winReadCoverageBasesMin': $( if [ ! -z ${VIASH_PAR_WINREADCOVERAGEBASESMIN+x} ]; then echo "int(r'${VIASH_PAR_WINREADCOVERAGEBASESMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimOutType': $( if [ ! -z ${VIASH_PAR_CHIMOUTTYPE+x} ]; then echo "r'${VIASH_PAR_CHIMOUTTYPE//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'chimSegmentMin': $( if [ ! -z ${VIASH_PAR_CHIMSEGMENTMIN+x} ]; then echo "int(r'${VIASH_PAR_CHIMSEGMENTMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimScoreMin': $( if [ ! -z ${VIASH_PAR_CHIMSCOREMIN+x} ]; then echo "int(r'${VIASH_PAR_CHIMSCOREMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimScoreDropMax': $( if [ ! -z ${VIASH_PAR_CHIMSCOREDROPMAX+x} ]; then echo "int(r'${VIASH_PAR_CHIMSCOREDROPMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimScoreSeparation': $( if [ ! -z ${VIASH_PAR_CHIMSCORESEPARATION+x} ]; then echo "int(r'${VIASH_PAR_CHIMSCORESEPARATION//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimScoreJunctionNonGTAG': $( if [ ! -z ${VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG+x} ]; then echo "int(r'${VIASH_PAR_CHIMSCOREJUNCTIONNONGTAG//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimJunctionOverhangMin': $( if [ ! -z ${VIASH_PAR_CHIMJUNCTIONOVERHANGMIN+x} ]; then echo "int(r'${VIASH_PAR_CHIMJUNCTIONOVERHANGMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimSegmentReadGapMax': $( if [ ! -z ${VIASH_PAR_CHIMSEGMENTREADGAPMAX+x} ]; then echo "int(r'${VIASH_PAR_CHIMSEGMENTREADGAPMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimFilter': $( if [ ! -z ${VIASH_PAR_CHIMFILTER+x} ]; then echo "r'${VIASH_PAR_CHIMFILTER//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'chimMainSegmentMultNmax': $( if [ ! -z ${VIASH_PAR_CHIMMAINSEGMENTMULTNMAX+x} ]; then echo "int(r'${VIASH_PAR_CHIMMAINSEGMENTMULTNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimMultimapNmax': $( if [ ! -z ${VIASH_PAR_CHIMMULTIMAPNMAX+x} ]; then echo "int(r'${VIASH_PAR_CHIMMULTIMAPNMAX//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimMultimapScoreRange': $( if [ ! -z ${VIASH_PAR_CHIMMULTIMAPSCORERANGE+x} ]; then echo "int(r'${VIASH_PAR_CHIMMULTIMAPSCORERANGE//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimNonchimScoreDropMin': $( if [ ! -z ${VIASH_PAR_CHIMNONCHIMSCOREDROPMIN+x} ]; then echo "int(r'${VIASH_PAR_CHIMNONCHIMSCOREDROPMIN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'chimOutJunctionFormat': $( if [ ! -z ${VIASH_PAR_CHIMOUTJUNCTIONFORMAT+x} ]; then echo "int(r'${VIASH_PAR_CHIMOUTJUNCTIONFORMAT//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'quantMode': $( if [ ! -z ${VIASH_PAR_QUANTMODE+x} ]; then echo "r'${VIASH_PAR_QUANTMODE//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'quantTranscriptomeBAMcompression': $( if [ ! -z ${VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION+x} ]; then echo "int(r'${VIASH_PAR_QUANTTRANSCRIPTOMEBAMCOMPRESSION//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'quantTranscriptomeSAMoutput': $( if [ ! -z ${VIASH_PAR_QUANTTRANSCRIPTOMESAMOUTPUT+x} ]; then echo "r'${VIASH_PAR_QUANTTRANSCRIPTOMESAMOUTPUT//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'twopassMode': $( if [ ! -z ${VIASH_PAR_TWOPASSMODE+x} ]; then echo "r'${VIASH_PAR_TWOPASSMODE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'twopass1readsN': $( if [ ! -z ${VIASH_PAR_TWOPASS1READSN+x} ]; then echo "int(r'${VIASH_PAR_TWOPASS1READSN//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'waspOutputMode': $( if [ ! -z ${VIASH_PAR_WASPOUTPUTMODE+x} ]; then echo "r'${VIASH_PAR_WASPOUTPUTMODE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'input': $( if [ ! -z ${VIASH_PAR_INPUT+x} ]; then echo "r'${VIASH_PAR_INPUT//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'input_r2': $( if [ ! -z ${VIASH_PAR_INPUT_R2+x} ]; then echo "r'${VIASH_PAR_INPUT_R2//\\'/\\'\\"\\'\\"r\\'}'.split(';')"; else echo None; fi ), + 'aligned_reads': $( if [ ! -z ${VIASH_PAR_ALIGNED_READS+x} ]; then echo "r'${VIASH_PAR_ALIGNED_READS//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'reads_per_gene': $( if [ ! -z ${VIASH_PAR_READS_PER_GENE+x} ]; then echo "r'${VIASH_PAR_READS_PER_GENE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'unmapped': $( if [ ! -z ${VIASH_PAR_UNMAPPED+x} ]; then echo "r'${VIASH_PAR_UNMAPPED//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'unmapped_r2': $( if [ ! -z ${VIASH_PAR_UNMAPPED_R2+x} ]; then echo "r'${VIASH_PAR_UNMAPPED_R2//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'chimeric_junctions': $( if [ ! -z ${VIASH_PAR_CHIMERIC_JUNCTIONS+x} ]; then echo "r'${VIASH_PAR_CHIMERIC_JUNCTIONS//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'log': $( if [ ! -z ${VIASH_PAR_LOG+x} ]; then echo "r'${VIASH_PAR_LOG//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'splice_junctions': $( if [ ! -z ${VIASH_PAR_SPLICE_JUNCTIONS+x} ]; then echo "r'${VIASH_PAR_SPLICE_JUNCTIONS//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ) +} +meta = { + 'name': $( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "r'${VIASH_META_NAME//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'functionality_name': $( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "r'${VIASH_META_FUNCTIONALITY_NAME//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'resources_dir': $( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "r'${VIASH_META_RESOURCES_DIR//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'executable': $( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "r'${VIASH_META_EXECUTABLE//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'config': $( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "r'${VIASH_META_CONFIG//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'temp_dir': $( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "r'${VIASH_META_TEMP_DIR//\\'/\\'\\"\\'\\"r\\'}'"; else echo None; fi ), + 'cpus': $( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "int(r'${VIASH_META_CPUS//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'memory_b': $( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "int(r'${VIASH_META_MEMORY_B//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'memory_kb': $( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "int(r'${VIASH_META_MEMORY_KB//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'memory_mb': $( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "int(r'${VIASH_META_MEMORY_MB//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'memory_gb': $( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "int(r'${VIASH_META_MEMORY_GB//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'memory_tb': $( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "int(r'${VIASH_META_MEMORY_TB//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'memory_pb': $( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "int(r'${VIASH_META_MEMORY_PB//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'memory_kib': $( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "int(r'${VIASH_META_MEMORY_KIB//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'memory_mib': $( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "int(r'${VIASH_META_MEMORY_MIB//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'memory_gib': $( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "int(r'${VIASH_META_MEMORY_GIB//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'memory_tib': $( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "int(r'${VIASH_META_MEMORY_TIB//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ), + 'memory_pib': $( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "int(r'${VIASH_META_MEMORY_PIB//\\'/\\'\\"\\'\\"r\\'}')"; else echo None; fi ) +} +dep = { + +} + +## VIASH END + +################################################## +# check and process SE / PE R1 input files +input_r1 = par["input"] +readFilesIn = ",".join(par["input"]) +par["input"] = None + +# check and process PE R2 input files +input_r2 = par["input_r2"] +if input_r2 is not None: + if len(input_r1) != len(input_r2): + raise ValueError("The number of R1 and R2 files do not match.") + readFilesIn = [readFilesIn, ",".join(par["input_r2"])] + par["input_r2"] = None + +# store readFilesIn +par["readFilesIn"] = readFilesIn + +################################################## + +# determine readFilesCommand +if input_r1[0].endswith(".gz"): + print(">> Input files are gzipped, setting readFilesCommand to zcat", flush=True) + par["readFilesCommand"] = "zcat" +elif input_r1[0].endswith(".bz2"): + print(">> Input files are bzipped, setting readFilesCommand to bzcat", flush=True) + par["readFilesCommand"] = "bzcat" + +################################################## +# store output paths +expected_outputs = { + "aligned_reads": ["Aligned.out.sam", "Aligned.out.bam"], + "reads_per_gene": "ReadsPerGene.out.tab", + "chimeric_junctions": "Chimeric.out.junction", + "log": "Log.final.out", + "splice_junctions": "SJ.out.tab", + "unmapped": "Unmapped.out.mate1", + "unmapped_r2": "Unmapped.out.mate2" +} +output_paths = {name: par[name] for name in expected_outputs.keys()} +for name in expected_outputs.keys(): + par[name] = None + +################################################## +# process other args +par["runMode"] = "alignReads" + +if "cpus" in meta and meta["cpus"]: + par["runThreadN"] = meta["cpus"] + +################################################## +# run STAR and move output to final destination +with tempfile.TemporaryDirectory(prefix="star-", dir=meta["temp_dir"], ignore_cleanup_errors=True) as temp_dir: + print(">> Constructing command", flush=True) + + # set output paths + temp_dir = Path(temp_dir) + par["outTmpDir"] = temp_dir / "tempdir" + out_dir = temp_dir / "out" + par["outFileNamePrefix"] = f"{out_dir}/" # star needs this slash + + # construct command + cmd_args = [ "STAR" ] + for name, value in par.items(): + if value is not None: + val_to_add = value if isinstance(value, list) else [value] + cmd_args.extend([f"--{name}"] + [str(x) for x in val_to_add]) + print("", flush=True) + + # run command + print(">> Running STAR with command:", flush=True) + print(f"+ {' '.join(cmd_args)}", end="\\\\n\\\\n", flush=True) + subprocess.run( + cmd_args, + check=True + ) + print(">> STAR finished successfully", end="\\\\n\\\\n", flush=True) + + # move output to final destination + print(">> Moving output to final destination", flush=True) + for name, paths in expected_outputs.items(): + for expected_path in [paths] if isinstance(paths, str) else paths: + expected_full_path = out_dir / expected_path + if output_paths[name] and expected_full_path.is_file(): + print(f">> Moving {expected_path} to {output_paths[name]}", flush=True) + shutil.move(expected_full_path, output_paths[name]) +VIASHMAIN +python -B "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/star/star_align_reads", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/star/star_align_reads/nextflow.config b/target/nextflow/star/star_align_reads/nextflow.config new file mode 100644 index 00000000..4619e6b7 --- /dev/null +++ b/target/nextflow/star/star_align_reads/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'star/star_align_reads' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Aligns reads to a reference genome using STAR.\n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/star/star_align_reads/nextflow_schema.json b/target/nextflow/star/star_align_reads/nextflow_schema.json new file mode 100644 index 00000000..6c1dc148 --- /dev/null +++ b/target/nextflow/star/star_align_reads/nextflow_schema.json @@ -0,0 +1,171 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "star_align_reads", +"description": "Aligns reads to a reference genome using STAR.\n", +"type": "object", +"definitions": { + + + + "inputs" : { + "title": "Inputs", + "type": "object", + "description": "No description", + "properties": { + + + "input": { + "type": + "string", + "description": "Type: List of `file`, required, example: `mysample_S1_L001_R1_001.fastq.gz`, multiple_sep: `\":\"`. The single-end or paired-end R1 FastQ files to be processed", + "help_text": "Type: List of `file`, required, example: `mysample_S1_L001_R1_001.fastq.gz`, multiple_sep: `\":\"`. The single-end or paired-end R1 FastQ files to be processed." + + } + + + , + "input_r2": { + "type": + "string", + "description": "Type: List of `file`, example: `mysample_S1_L001_R2_001.fastq.gz`, multiple_sep: `\":\"`. The paired-end R2 FastQ files to be processed", + "help_text": "Type: List of `file`, example: `mysample_S1_L001_R2_001.fastq.gz`, multiple_sep: `\":\"`. The paired-end R2 FastQ files to be processed. Only required if --input is a paired-end R1 file." + + } + + +} +}, + + + "outputs" : { + "title": "Outputs", + "type": "object", + "description": "No description", + "properties": { + + + "aligned_reads": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.aligned_reads.bam`, example: `aligned_reads.bam`. The output file containing the aligned reads", + "help_text": "Type: `file`, required, default: `$id.$key.aligned_reads.bam`, example: `aligned_reads.bam`. The output file containing the aligned reads." + , + "default": "$id.$key.aligned_reads.bam" + } + + + , + "reads_per_gene": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.reads_per_gene.tsv`, example: `reads_per_gene.tsv`. The output file containing the number of reads per gene", + "help_text": "Type: `file`, default: `$id.$key.reads_per_gene.tsv`, example: `reads_per_gene.tsv`. The output file containing the number of reads per gene." + , + "default": "$id.$key.reads_per_gene.tsv" + } + + + , + "unmapped": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.unmapped.fastq`, example: `unmapped.fastq`. The output file containing the unmapped reads", + "help_text": "Type: `file`, default: `$id.$key.unmapped.fastq`, example: `unmapped.fastq`. The output file containing the unmapped reads." + , + "default": "$id.$key.unmapped.fastq" + } + + + , + "unmapped_r2": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.unmapped_r2.fastq`, example: `unmapped_r2.fastq`. The output file containing the unmapped R2 reads", + "help_text": "Type: `file`, default: `$id.$key.unmapped_r2.fastq`, example: `unmapped_r2.fastq`. The output file containing the unmapped R2 reads." + , + "default": "$id.$key.unmapped_r2.fastq" + } + + + , + "chimeric_junctions": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.chimeric_junctions.tsv`, example: `chimeric_junctions.tsv`. The output file containing the chimeric junctions", + "help_text": "Type: `file`, default: `$id.$key.chimeric_junctions.tsv`, example: `chimeric_junctions.tsv`. The output file containing the chimeric junctions." + , + "default": "$id.$key.chimeric_junctions.tsv" + } + + + , + "log": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.log.txt`, example: `log.txt`. The output file containing the log of the alignment process", + "help_text": "Type: `file`, default: `$id.$key.log.txt`, example: `log.txt`. The output file containing the log of the alignment process." + , + "default": "$id.$key.log.txt" + } + + + , + "splice_junctions": { + "type": + "string", + "description": "Type: `file`, default: `$id.$key.splice_junctions.tsv`, example: `splice_junctions.tsv`. The output file containing the splice junctions", + "help_text": "Type: `file`, default: `$id.$key.splice_junctions.tsv`, example: `splice_junctions.tsv`. The output file containing the splice junctions." + , + "default": "$id.$key.splice_junctions.tsv" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/inputs" + }, + + { + "$ref": "#/definitions/outputs" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +} diff --git a/target/nextflow/star/star_genome_generate/.config.vsh.yaml b/target/nextflow/star/star_genome_generate/.config.vsh.yaml new file mode 100644 index 00000000..a174da7b --- /dev/null +++ b/target/nextflow/star/star_genome_generate/.config.vsh.yaml @@ -0,0 +1,345 @@ +name: "star_genome_generate" +namespace: "star" +version: "main" +argument_groups: +- name: "Input" + arguments: + - type: "file" + name: "--genomeFastaFiles" + description: "Path(s) to the fasta files with the genome sequences, separated\ + \ by spaces. These files should be plain text FASTA files, they *cannot* be\ + \ zipped.\n" + info: null + must_exist: true + create_parent: true + required: true + direction: "input" + multiple: true + multiple_sep: ";" + - type: "file" + name: "--sjdbGTFfile" + description: "Path to the GTF file with annotations" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--sjdbOverhang" + description: "Length of the donor/acceptor sequence on each side of the junctions,\ + \ ideally = (mate_length - 1)" + info: null + example: + - 100 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFchrPrefix" + description: "Prefix for chromosome names in a GTF file (e.g. 'chr' for using\ + \ ENSMEBL annotations with UCSC genomes)" + info: null + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFfeatureExon" + description: "Feature type in GTF file to be used as exons for building transcripts" + info: null + example: + - "exon" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentTranscript" + description: "GTF attribute name for parent transcript ID (default \"transcript_id\"\ + \ works for GTF files)" + info: null + example: + - "transcript_id" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGene" + description: "GTF attribute name for parent gene ID (default \"gene_id\" works\ + \ for GTF files)" + info: null + example: + - "gene_id" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGeneName" + description: "GTF attribute name for parent gene name" + info: null + example: + - "gene_name" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "string" + name: "--sjdbGTFtagExonParentGeneType" + description: "GTF attribute name for parent gene type" + info: null + example: + - "gene_type" + - "gene_biotype" + required: false + direction: "input" + multiple: true + multiple_sep: ";" + - type: "long" + name: "--limitGenomeGenerateRAM" + description: "Maximum available RAM (bytes) for genome generation" + info: null + example: + - 31000000000 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--genomeSAindexNbases" + description: "Length (bases) of the SA pre-indexing string. Typically between\ + \ 10 and 15. Longer strings will use much more memory, but allow faster searches.\ + \ For small genomes, this parameter must be scaled down to min(14, log2(GenomeLength)/2\ + \ - 1)." + info: null + example: + - 14 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--genomeChrBinNbits" + description: "Defined as log2(chrBin), where chrBin is the size of the bins for\ + \ genome storage. Each chromosome will occupy an integer number of bins. For\ + \ a genome with large number of contigs, it is recommended to scale this parameter\ + \ as min(18, log2[max(GenomeLength/NumberOfReferences,ReadLength)])." + info: null + example: + - 18 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--genomeSAsparseD" + description: "Suffux array sparsity, i.e. distance between indices. Use bigger\ + \ numbers to decrease needed RAM at the cost of mapping speed reduction." + info: null + example: + - 1 + required: false + min: 0 + direction: "input" + multiple: false + multiple_sep: ";" + - type: "integer" + name: "--genomeSuffixLengthMax" + description: "Maximum length of the suffixes, has to be longer than read length.\ + \ Use -1 for infinite length." + info: null + example: + - -1 + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "string" + name: "--genomeTransformType" + description: "Type of genome transformation\n None ... no transformation\n\ + \ Haploid ... replace reference alleles with alternative alleles from VCF\ + \ file (e.g. consensus allele)\n Diploid ... create two haplotypes for each\ + \ chromosome listed in VCF file, for genotypes 1|2, assumes perfect phasing\ + \ (e.g. personal genome)\n" + info: null + example: + - "None" + required: false + direction: "input" + multiple: false + multiple_sep: ";" + - type: "file" + name: "--genomeTransformVCF" + description: "path to VCF file for genome transformation" + info: null + must_exist: true + create_parent: true + required: false + direction: "input" + multiple: false + multiple_sep: ";" +- name: "Output" + arguments: + - type: "file" + name: "--index" + description: "STAR index directory." + info: null + default: + - "STAR_index" + must_exist: true + create_parent: true + required: true + direction: "output" + multiple: false + multiple_sep: ";" +resources: +- type: "bash_script" + path: "script.sh" + is_executable: true +description: "Create index for STAR\n" +test_resources: +- type: "bash_script" + path: "test.sh" + is_executable: true +info: null +status: "enabled" +requirements: + commands: + - "ps" +keywords: +- "genome" +- "index" +- "align" +license: "MIT" +references: + doi: + - "10.1093/bioinformatics/bts635" +links: + repository: "https://github.com/alexdobin/STAR" + documentation: "https://github.com/alexdobin/STAR/blob/master/doc/STARmanual.pdf" +runners: +- type: "executable" + id: "executable" + docker_setup_strategy: "ifneedbepullelsecachedbuild" +- type: "nextflow" + id: "nextflow" + directives: + tag: "$id" + auto: + simplifyInput: true + simplifyOutput: false + transcript: false + publish: false + config: + labels: + mem1gb: "memory = 1000000000.B" + mem2gb: "memory = 2000000000.B" + mem5gb: "memory = 5000000000.B" + mem10gb: "memory = 10000000000.B" + mem20gb: "memory = 20000000000.B" + mem50gb: "memory = 50000000000.B" + mem100gb: "memory = 100000000000.B" + mem200gb: "memory = 200000000000.B" + mem500gb: "memory = 500000000000.B" + mem1tb: "memory = 1000000000000.B" + mem2tb: "memory = 2000000000000.B" + mem5tb: "memory = 5000000000000.B" + mem10tb: "memory = 10000000000000.B" + mem20tb: "memory = 20000000000000.B" + mem50tb: "memory = 50000000000000.B" + mem100tb: "memory = 100000000000000.B" + mem200tb: "memory = 200000000000000.B" + mem500tb: "memory = 500000000000000.B" + mem1gib: "memory = 1073741824.B" + mem2gib: "memory = 2147483648.B" + mem4gib: "memory = 4294967296.B" + mem8gib: "memory = 8589934592.B" + mem16gib: "memory = 17179869184.B" + mem32gib: "memory = 34359738368.B" + mem64gib: "memory = 68719476736.B" + mem128gib: "memory = 137438953472.B" + mem256gib: "memory = 274877906944.B" + mem512gib: "memory = 549755813888.B" + mem1tib: "memory = 1099511627776.B" + mem2tib: "memory = 2199023255552.B" + mem4tib: "memory = 4398046511104.B" + mem8tib: "memory = 8796093022208.B" + mem16tib: "memory = 17592186044416.B" + mem32tib: "memory = 35184372088832.B" + mem64tib: "memory = 70368744177664.B" + mem128tib: "memory = 140737488355328.B" + mem256tib: "memory = 281474976710656.B" + mem512tib: "memory = 562949953421312.B" + cpu1: "cpus = 1" + cpu2: "cpus = 2" + cpu5: "cpus = 5" + cpu10: "cpus = 10" + cpu20: "cpus = 20" + cpu50: "cpus = 50" + cpu100: "cpus = 100" + cpu200: "cpus = 200" + cpu500: "cpus = 500" + cpu1000: "cpus = 1000" + debug: false + container: "docker" +engines: +- type: "docker" + id: "docker" + image: "ubuntu:22.04" + target_registry: "images.viash-hub.com" + target_tag: "main" + namespace_separator: "/" + setup: + - type: "docker" + run: + - "apt-get update && \\\n apt-get install -y --no-install-recommends ${PACKAGES}\ + \ && \\\n cd /tmp && \\\n wget --no-check-certificate https://github.com/alexdobin/STAR/archive/refs/tags/${STAR_VERSION}.zip\ + \ && \\\n unzip ${STAR_VERSION}.zip && \\\n cd STAR-${STAR_VERSION}/source\ + \ && \\\n make STARstatic CXXFLAGS_SIMD=-std=c++11 && \\\n cp STAR /usr/local/bin\ + \ && \\\n cd / && \\\n rm -rf /tmp/STAR-${STAR_VERSION} /tmp/${STAR_VERSION}.zip\ + \ && \\\n apt-get --purge autoremove -y ${PACKAGES} && \\\n apt-get clean\n" + env: + - "STAR_VERSION 2.7.11b" + - "PACKAGES gcc g++ make wget zlib1g-dev unzip xxd" + - type: "docker" + run: + - "STAR --version | sed 's#\\(.*\\)#star: \"\\1\"#' > /var/software_versions.txt\n" + entrypoint: [] + cmd: null +- type: "native" + id: "native" +build_info: + config: "src/star/star_genome_generate/config.vsh.yaml" + runner: "nextflow" + engine: "docker|native" + output: "target/nextflow/star/star_genome_generate" + executable: "target/nextflow/star/star_genome_generate/main.nf" + viash_version: "0.9.0-RC6" + git_commit: "d0c648fb7eefe067f5b5b3d402a204354bb37198" + git_remote: "https://github.com/viash-hub/biobox" +package_config: + name: "biobox" + version: "main" + description: "A collection of bioinformatics tools for working with sequence data.\n" + info: null + viash_version: "0.9.0-RC6" + source: "src" + target: "target" + config_mods: + - ".requirements.commands := ['ps']\n" + - ".engines += { type: \"native\" }" + - ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'" + - ".engines[.type == 'docker'].target_tag := 'main'" + keywords: + - "bioinformatics" + - "modules" + - "sequencing" + license: "MIT" + organization: "vsh" + links: + repository: "https://github.com/viash-hub/biobox" + issue_tracker: "https://github.com/viash-hub/biobox/issues" diff --git a/target/nextflow/star/star_genome_generate/main.nf b/target/nextflow/star/star_genome_generate/main.nf new file mode 100644 index 00000000..95b263e3 --- /dev/null +++ b/target/nextflow/star/star_genome_generate/main.nf @@ -0,0 +1,3727 @@ +// star_genome_generate main +// +// This wrapper script is auto-generated by viash 0.9.0-RC6 and is thus a +// derivative work thereof. This software comes with ABSOLUTELY NO WARRANTY from +// Data Intuitive. +// +// The component may contain files which fall under a different license. The +// authors of this component should specify the license in the header of such +// files, or include a separate license file detailing the licenses of all included +// files. + +//////////////////////////// +// VDSL3 helper functions // +//////////////////////////// + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_checkArgumentType.nf' +class UnexpectedArgumentTypeException extends Exception { + String errorIdentifier + String stage + String plainName + String expectedClass + String foundClass + + // ${key ? " in module '$key'" : ""}${id ? " id '$id'" : ""} + UnexpectedArgumentTypeException(String errorIdentifier, String stage, String plainName, String expectedClass, String foundClass) { + super("Error${errorIdentifier ? " $errorIdentifier" : ""}:${stage ? " $stage" : "" } argument '${plainName}' has the wrong type. " + + "Expected type: ${expectedClass}. Found type: ${foundClass}") + this.errorIdentifier = errorIdentifier + this.stage = stage + this.plainName = plainName + this.expectedClass = expectedClass + this.foundClass = foundClass + } +} + +/** + * Checks if the given value is of the expected type. If not, an exception is thrown. + * + * @param stage The stage of the argument (input or output) + * @param par The parameter definition + * @param value The value to check + * @param errorIdentifier The identifier to use in the error message + * @return The value, if it is of the expected type + * @throws UnexpectedArgumentTypeException If the value is not of the expected type +*/ +def _checkArgumentType(String stage, Map par, Object value, String errorIdentifier) { + // expectedClass will only be != null if value is not of the expected type + def expectedClass = null + def foundClass = null + + // todo: split if need be + + if (!par.required && value == null) { + expectedClass = null + } else if (par.multiple) { + if (value !instanceof Collection) { + value = [value] + } + + // split strings + value = value.collectMany{ val -> + if (val instanceof String) { + // collect() to ensure that the result is a List and not simply an array + val.split(par.multiple_sep).collect() + } else { + [val] + } + } + + // process globs + if (par.type == "file" && par.direction == "input") { + value = value.collect{ it instanceof String ? file(it, hidden: true) : it }.flatten() + } + + // check types of elements in list + try { + value = value.collect { listVal -> + _checkArgumentType(stage, par + [multiple: false], listVal, errorIdentifier) + } + } catch (UnexpectedArgumentTypeException e) { + expectedClass = "List[${e.expectedClass}]" + foundClass = "List[${e.foundClass}]" + } + } else if (par.type == "string") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else if (par.type == "integer") { + // cast to integer if need be + if (value instanceof String) { + try { + value = value.toInteger() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigInteger) { + value = value.intValue() + } + expectedClass = value instanceof Integer ? null : "Integer" + } else if (par.type == "long") { + // cast to long if need be + if (value instanceof String) { + try { + value = value.toLong() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof Integer) { + value = value.toLong() + } + expectedClass = value instanceof Long ? null : "Long" + } else if (par.type == "double") { + // cast to double if need be + if (value instanceof String) { + try { + value = value.toDouble() + } catch (NumberFormatException e) { + // do nothing + } + } + if (value instanceof java.math.BigDecimal) { + value = value.doubleValue() + } + if (value instanceof Float) { + value = value.toDouble() + } + expectedClass = value instanceof Double ? null : "Double" + } else if (par.type == "boolean" | par.type == "boolean_true" | par.type == "boolean_false") { + // cast to boolean if need be + if (value instanceof String) { + def valueLower = value.toLowerCase() + if (valueLower == "true") { + value = true + } else if (valueLower == "false") { + value = false + } + } + expectedClass = value instanceof Boolean ? null : "Boolean" + } else if (par.type == "file" && (par.direction == "input" || stage == "output")) { + // cast to path if need be + if (value instanceof String) { + value = file(value, hidden: true) + } + if (value instanceof File) { + value = value.toPath() + } + expectedClass = value instanceof Path ? null : "Path" + } else if (par.type == "file" && stage == "input" && par.direction == "output") { + // cast to string if need be + if (value instanceof GString) { + value = value.toString() + } + expectedClass = value instanceof String ? null : "String" + } else { + // didn't find a match for par.type + expectedClass = par.type + } + + if (expectedClass != null) { + if (foundClass == null) { + foundClass = value.getClass().getName() + } + throw new UnexpectedArgumentTypeException(errorIdentifier, stage, par.plainName, expectedClass, foundClass) + } + + return value +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processInputValues.nf' +Map _processInputValues(Map inputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.required) { + assert inputs.containsKey(arg.plainName) && inputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required input argument '${arg.plainName}' is missing" + } + } + + inputs = inputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid input argument" + + value = _checkArgumentType("input", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return inputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/arguments/_processOutputValues.nf' +Map _processOutputValues(Map outputs, Map config, String id, String key) { + if (!workflow.stubRun) { + config.allArguments.each { arg -> + if (arg.direction == "output" && arg.required) { + assert outputs.containsKey(arg.plainName) && outputs.get(arg.plainName) != null : + "Error in module '${key}' id '${id}': required output argument '${arg.plainName}' is missing" + } + } + + outputs = outputs.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && it.direction == "output" } + assert par != null : "Error in module '${key}' id '${id}': '${name}' is not a valid output argument" + + value = _checkArgumentType("output", par, value, "in module '$key' id '$id'") + + [ name, value ] + } + } + return outputs +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/IDChecker.nf' +class IDChecker { + final def items = [] as Set + + @groovy.transform.WithWriteLock + boolean observe(String item) { + if (items.contains(item)) { + return false + } else { + items << item + return true + } + } + + @groovy.transform.WithReadLock + boolean contains(String item) { + return items.contains(item) + } + + @groovy.transform.WithReadLock + Set getItems() { + return items.clone() + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_checkUniqueIds.nf' + +/** + * Check if the ids are unique across parameter sets + * + * @param parameterSets a list of parameter sets. + */ +private void _checkUniqueIds(List>> parameterSets) { + def ppIds = parameterSets.collect{it[0]} + assert ppIds.size() == ppIds.unique().size() : "All argument sets should have unique ids. Detected ids: $ppIds" +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_getChild.nf' + +// helper functions for reading params from file // +def _getChild(parent, child) { + if (child.contains("://") || java.nio.file.Paths.get(child).isAbsolute()) { + child + } else { + def parentAbsolute = java.nio.file.Paths.get(parent).toAbsolutePath().toString() + parentAbsolute.replaceAll('/[^/]*$', "/") + child + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_parseParamList.nf' +/** + * Figure out the param list format based on the file extension + * + * @param param_list A String containing the path to the parameter list file. + * + * @return A String containing the format of the parameter list file. + */ +def _paramListGuessFormat(param_list) { + if (param_list !instanceof String) { + "asis" + } else if (param_list.endsWith(".csv")) { + "csv" + } else if (param_list.endsWith(".json") || param_list.endsWith(".jsn")) { + "json" + } else if (param_list.endsWith(".yaml") || param_list.endsWith(".yml")) { + "yaml" + } else { + "yaml_blob" + } +} + + +/** + * Read the param list + * + * @param param_list One of the following: + * - A String containing the path to the parameter list file (csv, json or yaml), + * - A yaml blob of a list of maps (yaml_blob), + * - Or a groovy list of maps (asis). + * @param config A Map of the Viash configuration. + * + * @return A List of Maps containing the parameters. + */ +def _parseParamList(param_list, Map config) { + // first determine format by extension + def paramListFormat = _paramListGuessFormat(param_list) + + def paramListPath = (paramListFormat != "asis" && paramListFormat != "yaml_blob") ? + file(param_list, hidden: true) : + null + + // get the correct parser function for the detected params_list format + def paramSets = [] + if (paramListFormat == "asis") { + paramSets = param_list + } else if (paramListFormat == "yaml_blob") { + paramSets = readYamlBlob(param_list) + } else if (paramListFormat == "yaml") { + paramSets = readYaml(paramListPath) + } else if (paramListFormat == "json") { + paramSets = readJson(paramListPath) + } else if (paramListFormat == "csv") { + paramSets = readCsv(paramListPath) + } else { + error "Format of provided --param_list not recognised.\n" + + "Found: '$paramListFormat'.\n" + + "Expected: a csv file, a json file, a yaml file,\n" + + "a yaml blob or a groovy list of maps." + } + + // data checks + assert paramSets instanceof List: "--param_list should contain a list of maps" + for (value in paramSets) { + assert value instanceof Map: "--param_list should contain a list of maps" + } + + // id is argument + def idIsArgument = config.allArguments.any{it.plainName == "id"} + + // Reformat from List to List> by adding the ID as first element of a Tuple2 + paramSets = paramSets.collect({ data -> + def id = data.id + if (!idIsArgument) { + data = data.findAll{k, v -> k != "id"} + } + [id, data] + }) + + // Split parameters with 'multiple: true' + paramSets = paramSets.collect({ id, data -> + data = _splitParams(data, config) + [id, data] + }) + + // The paths of input files inside a param_list file may have been specified relatively to the + // location of the param_list file. These paths must be made absolute. + if (paramListPath) { + paramSets = paramSets.collect({ id, data -> + def new_data = data.collectEntries{ parName, parValue -> + def par = config.allArguments.find{it.plainName == parName} + if (par && par.type == "file" && par.direction == "input") { + if (parValue instanceof Collection) { + parValue = parValue.collectMany{path -> + def x = _resolveSiblingIfNotAbsolute(path, paramListPath) + x instanceof Collection ? x : [x] + } + } else { + parValue = _resolveSiblingIfNotAbsolute(parValue, paramListPath) + } + } + [parName, parValue] + } + [id, new_data] + }) + } + + return paramSets +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/_splitParams.nf' +/** + * Split parameters for arguments that accept multiple values using their separator + * + * @param paramList A Map containing parameters to split. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A Map of parameters where the parameter values have been split into a list using + * their seperator. + */ +Map _splitParams(Map parValues, Map config){ + def parsedParamValues = parValues.collectEntries { parName, parValue -> + def parameterSettings = config.allArguments.find({it.plainName == parName}) + + if (!parameterSettings) { + // if argument is not found, do not alter + return [parName, parValue] + } + if (parameterSettings.multiple) { // Check if parameter can accept multiple values + if (parValue instanceof Collection) { + parValue = parValue.collect{it instanceof String ? it.split(parameterSettings.multiple_sep) : it } + } else if (parValue instanceof String) { + parValue = parValue.split(parameterSettings.multiple_sep) + } else if (parValue == null) { + parValue = [] + } else { + parValue = [ parValue ] + } + parValue = parValue.flatten() + } + // For all parameters check if multiple values are only passed for + // arguments that allow it. Quietly simplify lists of length 1. + if (!parameterSettings.multiple && parValue instanceof Collection) { + assert parValue.size() == 1 : + "Error: argument ${parName} has too many values.\n" + + " Expected amount: 1. Found: ${parValue.size()}" + parValue = parValue[0] + } + [parName, parValue] + } + return parsedParamValues +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/channelFromParams.nf' +/** + * Parse nextflow parameters based on settings defined in a viash config. + * Return a list of parameter sets, each parameter set corresponding to + * an event in a nextflow channel. The output from this function can be used + * with Channel.fromList to create a nextflow channel with Vdsl3 formatted + * events. + * + * This function performs: + * - A filtering of the params which can be found in the config file. + * - Process the params_list argument which allows a user to to initialise + * a Vsdl3 channel with multiple parameter sets. Possible formats are + * csv, json, yaml, or simply a yaml_blob. A csv should have column names + * which correspond to the different arguments of this pipeline. A json or a yaml + * file should be a list of maps, each of which has keys corresponding to the + * arguments of the pipeline. A yaml blob can also be passed directly as a parameter. + * When passing a csv, json or yaml, relative path names are relativized to the + * location of the parameter file. + * - Combine the parameter sets into a vdsl3 Channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A list of parameters with the first element of the event being + * the event ID and the second element containing a map of the parsed parameters. + */ + +private List>> _paramsToParamSets(Map params, Map config){ + // todo: fetch key from run args + def key_ = config.name + + /* parse regular parameters (not in param_list) */ + /*************************************************/ + def globalParams = config.allArguments + .findAll { params.containsKey(it.plainName) } + .collectEntries { [ it.plainName, params[it.plainName] ] } + def globalID = params.get("id", null) + + /* process params_list arguments */ + /*********************************/ + def paramList = params.containsKey("param_list") && params.param_list != null ? + params.param_list : [] + // if (paramList instanceof String) { + // paramList = [paramList] + // } + // def paramSets = paramList.collectMany{ _parseParamList(it, config) } + // TODO: be able to process param_list when it is a list of strings + def paramSets = _parseParamList(paramList, config) + if (paramSets.isEmpty()) { + paramSets = [[null, [:]]] + } + + /* combine arguments into channel */ + /**********************************/ + def processedParams = paramSets.indexed().collect{ index, tup -> + // Process ID + def id = tup[0] ?: globalID + + if (workflow.stubRun && !id) { + // if stub run, explicitly add an id if missing + id = "stub${index}" + } + assert id != null: "Each parameter set should have at least an 'id'" + + // Process params + def parValues = globalParams + tup[1] + // // Remove parameters which are null, if the default is also null + // parValues = parValues.collectEntries{paramName, paramValue -> + // parameterSettings = config.functionality.allArguments.find({it.plainName == paramName}) + // if ( paramValue != null || parameterSettings.get("default", null) != null ) { + // [paramName, paramValue] + // } + // } + parValues = parValues.collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + assert par != null : "Error in module '${key_}' id '${id}': '${name}' is not a valid input argument" + + if (par == null) { + return [:] + } + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + + [ name, value ] + } + + [id, parValues] + } + + // Check if ids (first element of each list) is unique + _checkUniqueIds(processedParams) + return processedParams +} + +/** + * Parse nextflow parameters based on settings defined in a viash config + * and return a nextflow channel. + * + * @param params Input parameters. Can optionaly contain a 'param_list' key that + * provides a list of arguments that can be split up into multiple events + * in the output channel possible formats of param_lists are: a csv file, + * json file, a yaml file or a yaml blob. Each parameters set (event) must + * have a unique ID. + * @param config A Map of the Viash configuration. This Map can be generated from the config file + * using the readConfig() function. + * + * @return A nextflow Channel with events. Events are formatted as a tuple that contains + * first contains the ID of the event and as second element holds a parameter map. + * + * + */ +def channelFromParams(Map params, Map config) { + def processedParams = _paramsToParamSets(params, config) + return Channel.fromList(processedParams) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/checkUniqueIds.nf' +def checkUniqueIds(Map args) { + def stopOnError = args.stopOnError == null ? args.stopOnError : true + + def idChecker = new IDChecker() + + return filter { tup -> + if (!idChecker.observe(tup[0])) { + if (stopOnError) { + error "Duplicate id: ${tup[0]}" + } else { + log.warn "Duplicate id: ${tup[0]}, removing duplicate entry" + return false + } + } + return true + } +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/preprocessInputs.nf' +// This helper file will be deprecated soon +preprocessInputsDeprecationWarningPrinted = false + +def preprocessInputsDeprecationWarning() { + if (!preprocessInputsDeprecationWarningPrinted) { + preprocessInputsDeprecationWarningPrinted = true + System.err.println("Warning: preprocessInputs() is deprecated and will be removed in Viash 0.9.0.") + } +} + +/** + * Generate a nextflow Workflow that allows processing a channel of + * Vdsl3 formatted events and apply a Viash config to them: + * - Gather default parameters from the Viash config and make + * sure that they are correctly formatted (see applyConfig method). + * - Format the input parameters (also using the applyConfig method). + * - Apply the default parameter to the input parameters. + * - Do some assertions: + * ~ Check if the event IDs in the channel are unique. + * + * The events in the channel are formatted as tuples, with the + * first element of the tuples being a unique id of the parameter set, + * and the second element containg the the parameters themselves. + * Optional extra elements of the tuples will be passed to the output as is. + * + * @param args A map that must contain a 'config' key that points + * to a parsed config (see readConfig()). Optionally, a + * 'key' key can be provided which can be used to create a unique + * name for the workflow process. + * + * @return A workflow that allows processing a channel of Vdsl3 formatted events + * and apply a Viash config to them. + */ +def preprocessInputs(Map args) { + preprocessInputsDeprecationWarning() + + def config = args.config + assert config instanceof Map : + "Error in preprocessInputs: config must be a map. " + + "Expected class: Map. Found: config.getClass() is ${config.getClass()}" + def key_ = args.key ?: config.name + + // Get different parameter types (used throughout this function) + def defaultArgs = config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + map { tup -> + def id = tup[0] + def data = tup[1] + def passthrough = tup.drop(2) + + def new_data = (defaultArgs + data).collectEntries { name, value -> + def par = config.allArguments.find { it.plainName == name && (it.direction == "input" || it.type == "file") } + + if (par != null) { + value = _checkArgumentType("input", par, value, "in module '$key_' id '$id'") + } + + [ name, value ] + } + + [ id, new_data ] + passthrough + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runComponents.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component config. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component config. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component config. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component config. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runComponents(Map args) { + log.warn("runComponents is deprecated, use runEach instead") + assert args.components: "runComponents should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runComponents" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runComponentsWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def comp_config = comp_.config + + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_config) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + // def new_id = id_(tup[0], tup[1], comp_config) + def new_id = tup[0] + if (id_ instanceof String) { + new_id = id_ + } else if (id_ instanceof Closure) { + new_id = id_(new_id, tup[1], comp_config) + } + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_config) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_config) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runComponentsWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/runEach.nf' +/** + * Run a list of components on a stream of data. + * + * @param components: list of Viash VDSL3 modules to run + * @param fromState: a closure, a map or a list of keys to extract from the input data. + * If a closure, it will be called with the id, the data and the component itself. + * @param toState: a closure, a map or a list of keys to extract from the output data + * If a closure, it will be called with the id, the output data, the old state and the component itself. + * @param filter: filter function to apply to the input. + * It will be called with the id, the data and the component itself. + * @param id: id to use for the output data + * If a closure, it will be called with the id, the data and the component itself. + * @param auto: auto options to pass to the components + * + * @return: a workflow that runs the components + **/ +def runEach(Map args) { + assert args.components: "runEach should be passed a list of components to run" + + def components_ = args.components + if (components_ !instanceof List) { + components_ = [ components_ ] + } + assert components_.size() > 0: "pass at least one component to runEach" + + def fromState_ = args.fromState + def toState_ = args.toState + def filter_ = args.filter + def id_ = args.id + + workflow runEachWf { + take: input_ch + main: + + // generate one channel per method + out_chs = components_.collect{ comp_ -> + def filter_ch = filter_ + ? input_ch | filter{tup -> + filter_(tup[0], tup[1], comp_) + } + : input_ch + def id_ch = id_ + ? filter_ch | map{tup -> + def new_id = id_ + if (new_id instanceof Closure) { + new_id = new_id(tup[0], tup[1], comp_) + } + assert new_id instanceof String : "Error in runEach: id should be a String or a Closure that returns a String. Expected: id instanceof String. Found: ${new_id.getClass()}" + [new_id] + tup.drop(1) + } + : filter_ch + def data_ch = id_ch | map{tup -> + def new_data = tup[1] + if (fromState_ instanceof Map) { + new_data = fromState_.collectEntries{ key0, key1 -> + [key0, new_data[key1]] + } + } else if (fromState_ instanceof List) { + new_data = fromState_.collectEntries{ key -> + [key, new_data[key]] + } + } else if (fromState_ instanceof Closure) { + new_data = fromState_(tup[0], new_data, comp_) + } + tup.take(1) + [new_data] + tup.drop(1) + } + def out_ch = data_ch + | comp_.run( + auto: (args.auto ?: [:]) + [simplifyInput: false, simplifyOutput: false] + ) + def post_ch = toState_ + ? out_ch | map{tup -> + def output = tup[1] + def old_state = tup[2] + def new_state = null + if (toState_ instanceof Map) { + new_state = old_state + toState_.collectEntries{ key0, key1 -> + [key0, output[key1]] + } + } else if (toState_ instanceof List) { + new_state = old_state + toState_.collectEntries{ key -> + [key, output[key]] + } + } else if (toState_ instanceof Closure) { + new_state = toState_(tup[0], output, old_state, comp_) + } + [tup[0], new_state] + tup.drop(3) + } + : out_ch + + post_ch + } + + // mix all results + output_ch = + (out_chs.size == 1) + ? out_chs[0] + : out_chs[0].mix(*out_chs.drop(1)) + + emit: output_ch + } + + return runEachWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/channel/safeJoin.nf' +/** + * Join sourceChannel to targetChannel + * + * This function joins the sourceChannel to the targetChannel. + * However, each id in the targetChannel must be present in the + * sourceChannel. If _meta.join_id exists in the targetChannel, that is + * used as an id instead. If the id doesn't match any id in the sourceChannel, + * an error is thrown. + */ + +def safeJoin(targetChannel, sourceChannel, key) { + def sourceIDs = new IDChecker() + + def sourceCheck = sourceChannel + | map { tup -> + sourceIDs.observe(tup[0]) + tup + } + def targetCheck = targetChannel + | map { tup -> + def id = tup[0] + + if (!sourceIDs.contains(id)) { + error ( + "Error in module '${key}' when merging output with original state.\n" + + " Reason: output with id '${id}' could not be joined with source channel.\n" + + " If the IDs in the output channel differ from the input channel,\n" + + " please set `tup[1]._meta.join_id to the original ID.\n" + + " Original IDs in input channel: ['${sourceIDs.getItems().join("', '")}'].\n" + + " Unexpected ID in the output channel: '${id}'.\n" + + " Example input event: [\"id\", [input: file(...)]],\n" + + " Example output event: [\"newid\", [output: file(...), _meta: [join_id: \"id\"]]]" + ) + } + // TODO: add link to our documentation on how to fix this + + tup + } + + sourceCheck.cross(targetChannel) + | map{ left, right -> + right + left.drop(1) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/_processArgument.nf' +def _processArgument(arg) { + arg.multiple = arg.multiple != null ? arg.multiple : false + arg.required = arg.required != null ? arg.required : false + arg.direction = arg.direction != null ? arg.direction : "input" + arg.multiple_sep = arg.multiple_sep != null ? arg.multiple_sep : ";" + arg.plainName = arg.name.replaceAll("^-*", "") + + if (arg.type == "file") { + arg.must_exist = arg.must_exist != null ? arg.must_exist : true + arg.create_parent = arg.create_parent != null ? arg.create_parent : true + } + + // add default values to output files which haven't already got a default + if (arg.type == "file" && arg.direction == "output" && arg.default == null) { + def mult = arg.multiple ? "_*" : "" + def extSearch = "" + if (arg.default != null) { + extSearch = arg.default + } else if (arg.example != null) { + extSearch = arg.example + } + if (extSearch instanceof List) { + extSearch = extSearch[0] + } + def extSearchResult = extSearch.find("\\.[^\\.]+\$") + def ext = extSearchResult != null ? extSearchResult : "" + arg.default = "\$id.\$key.${arg.plainName}${mult}${ext}" + if (arg.multiple) { + arg.default = [arg.default] + } + } + + if (!arg.multiple) { + if (arg.default != null && arg.default instanceof List) { + arg.default = arg.default[0] + } + if (arg.example != null && arg.example instanceof List) { + arg.example = arg.example[0] + } + } + + if (arg.type == "boolean_true") { + arg.default = false + } + if (arg.type == "boolean_false") { + arg.default = true + } + + arg +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/addGlobalParams.nf' +def addGlobalArguments(config) { + def localConfig = [ + "argument_groups": [ + [ + "name": "Nextflow input-output arguments", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "arguments" : [ + [ + 'name': '--publish_dir', + 'required': true, + 'type': 'string', + 'description': 'Path to an output directory.', + 'example': 'output/', + 'multiple': false + ], + [ + 'name': '--param_list', + 'required': false, + 'type': 'string', + 'description': '''Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob. + | + |* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ ['id': 'foo', 'input': 'foo.txt'], ['id': 'bar', 'input': 'bar.txt'] ]`. + |* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`. + |* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]`. + |* A yaml blob can also be passed directly as a string. Example: `--param_list "[ {'id': 'foo', 'input': 'foo.txt'}, {'id': 'bar', 'input': 'bar.txt'} ]"`. + | + |When passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.'''.stripMargin(), + 'example': 'my_params.yaml', + 'multiple': false, + 'hidden': true + ] + // TODO: allow multiple: true in param_list? + // TODO: allow to specify a --param_list_regex to filter the param_list? + // TODO: allow to specify a --param_list_from_state to remap entries in the param_list? + ] + ] + ] + ] + + return processConfig(_mergeMap(config, localConfig)) +} + +def _mergeMap(Map lhs, Map rhs) { + return rhs.inject(lhs.clone()) { map, entry -> + if (map[entry.key] instanceof Map && entry.value instanceof Map) { + map[entry.key] = _mergeMap(map[entry.key], entry.value) + } else if (map[entry.key] instanceof Collection && entry.value instanceof Collection) { + map[entry.key] += entry.value + } else { + map[entry.key] = entry.value + } + return map + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/generateHelp.nf' +def _generateArgumentHelp(param) { + // alternatives are not supported + // def names = param.alternatives ::: List(param.name) + + def unnamedProps = [ + ["required parameter", param.required], + ["multiple values allowed", param.multiple], + ["output", param.direction.toLowerCase() == "output"], + ["file must exist", param.type == "file" && param.must_exist] + ].findAll{it[1]}.collect{it[0]} + + def dflt = null + if (param.default != null) { + if (param.default instanceof List) { + dflt = param.default.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + dflt = param.default.toString() + } + } + def example = null + if (param.example != null) { + if (param.example instanceof List) { + example = param.example.join(param.multiple_sep != null ? param.multiple_sep : ", ") + } else { + example = param.example.toString() + } + } + def min = param.min?.toString() + def max = param.max?.toString() + + def escapeChoice = { choice -> + def s1 = choice.replaceAll("\\n", "\\\\n") + def s2 = s1.replaceAll("\"", """\\\"""") + s2.contains(",") || s2 != choice ? "\"" + s2 + "\"" : s2 + } + def choices = param.choices == null ? + null : + "[ " + param.choices.collect{escapeChoice(it.toString())}.join(", ") + " ]" + + def namedPropsStr = [ + ["type", ([param.type] + unnamedProps).join(", ")], + ["default", dflt], + ["example", example], + ["choices", choices], + ["min", min], + ["max", max] + ] + .findAll{it[1]} + .collect{"\n " + it[0] + ": " + it[1].replaceAll("\n", "\\n")} + .join("") + + def descStr = param.description == null ? + "" : + _paragraphWrap("\n" + param.description.trim(), 80 - 8).join("\n ") + + "\n --" + param.plainName + + namedPropsStr + + descStr +} + +// Based on Helper.generateHelp() in Helper.scala +def _generateHelp(config) { + def fun = config + + // PART 1: NAME AND VERSION + def nameStr = fun.name + + (fun.version == null ? "" : " " + fun.version) + + // PART 2: DESCRIPTION + def descrStr = fun.description == null ? + "" : + "\n\n" + _paragraphWrap(fun.description.trim(), 80).join("\n") + + // PART 3: Usage + def usageStr = fun.usage == null ? + "" : + "\n\nUsage:\n" + fun.usage.trim() + + // PART 4: Options + def argGroupStrs = fun.allArgumentGroups.collect{argGroup -> + def name = argGroup.name + def descriptionStr = argGroup.description == null ? + "" : + "\n " + _paragraphWrap(argGroup.description.trim(), 80-4).join("\n ") + "\n" + def arguments = argGroup.arguments.collect{arg -> + arg instanceof String ? fun.allArguments.find{it.plainName == arg} : arg + }.findAll{it != null} + def argumentStrs = arguments.collect{param -> _generateArgumentHelp(param)} + + "\n\n$name:" + + descriptionStr + + argumentStrs.join("\n") + } + + // FINAL: combine + def out = nameStr + + descrStr + + usageStr + + argGroupStrs.join("") + + return out +} + +// based on Format._paragraphWrap +def _paragraphWrap(str, maxLength) { + def outLines = [] + str.split("\n").each{par -> + def words = par.split("\\s").toList() + + def word = null + def line = words.pop() + while(!words.isEmpty()) { + word = words.pop() + if (line.length() + word.length() + 1 <= maxLength) { + line = line + " " + word + } else { + outLines.add(line) + line = word + } + } + if (words.isEmpty()) { + outLines.add(line) + } + } + return outLines +} + +def helpMessage(config) { + if (params.containsKey("help") && params.help) { + def mergedConfig = addGlobalArguments(config) + def helpStr = _generateHelp(mergedConfig) + println(helpStr) + exit 0 + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/processConfig.nf' +def processConfig(config) { + // set defaults for arguments + config.arguments = + (config.arguments ?: []).collect{_processArgument(it)} + + // set defaults for argument_group arguments + config.argument_groups = + (config.argument_groups ?: []).collect{grp -> + grp.arguments = (grp.arguments ?: []).collect{_processArgument(it)} + grp + } + + // create combined arguments list + config.allArguments = + config.arguments + + config.argument_groups.collectMany{it.arguments} + + // add missing argument groups (based on Functionality::allArgumentGroups()) + def argGroups = config.argument_groups + if (argGroups.any{it.name.toLowerCase() == "arguments"}) { + argGroups = argGroups.collect{ grp -> + if (grp.name.toLowerCase() == "arguments") { + grp = grp + [ + arguments: grp.arguments + config.arguments + ] + } + grp + } + } else { + argGroups = argGroups + [ + name: "Arguments", + arguments: config.arguments + ] + } + config.allArgumentGroups = argGroups + + config +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/config/readConfig.nf' + +def readConfig(file) { + def config = readYaml(file ?: moduleDir.resolve("config.vsh.yaml")) + processConfig(config) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_resolveSiblingIfNotAbsolute.nf' +/** + * Resolve a path relative to the current file. + * + * @param str The path to resolve, as a String. + * @param parentPath The path to resolve relative to, as a Path. + * + * @return The path that may have been resovled, as a Path. + */ +def _resolveSiblingIfNotAbsolute(str, parentPath) { + if (str !instanceof String) { + return str + } + if (!_stringIsAbsolutePath(str)) { + return parentPath.resolveSibling(str) + } else { + return file(str, hidden: true) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/_stringIsAbsolutePath.nf' +/** + * Check whether a path as a string is absolute. + * + * In the past, we tried using `file(., relative: true).isAbsolute()`, + * but the 'relative' option was added in 22.10.0. + * + * @param path The path to check, as a String. + * + * @return Whether the path is absolute, as a boolean. + */ +def _stringIsAbsolutePath(path) { + def _resolve_URL_PROTOCOL = ~/^([a-zA-Z][a-zA-Z0-9]*:)?\\/.+/ + + assert path instanceof String + return _resolve_URL_PROTOCOL.matcher(path).matches() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/collectTraces.nf' +class CustomTraceObserver implements nextflow.trace.TraceObserver { + List traces + + CustomTraceObserver(List traces) { + this.traces = traces + } + + @Override + void onProcessComplete(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } + + @Override + void onProcessCached(nextflow.processor.TaskHandler handler, nextflow.trace.TraceRecord trace) { + def trace2 = trace.store.clone() + trace2.script = null + traces.add(trace2) + } +} + +def collectTraces() { + def traces = Collections.synchronizedList([]) + + // add custom trace observer which stores traces in the traces object + session.observers.add(new CustomTraceObserver(traces)) + + traces +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/deepClone.nf' +/** + * Performs a deep clone of the given object. + * @param x an object + */ +def deepClone(x) { + iterateMap(x, {it instanceof Cloneable ? it.clone() : it}) +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getPublishDir.nf' +def getPublishDir() { + return params.containsKey("publish_dir") ? params.publish_dir : + params.containsKey("publishDir") ? params.publishDir : + null +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/getRootDir.nf' + +// Recurse upwards until we find a '.build.yaml' file +def _findBuildYamlFile(pathPossiblySymlink) { + def path = pathPossiblySymlink.toRealPath() + def child = path.resolve(".build.yaml") + if (java.nio.file.Files.isDirectory(path) && java.nio.file.Files.exists(child)) { + return child + } else { + def parent = path.getParent() + if (parent == null) { + return null + } else { + return _findBuildYamlFile(parent) + } + } +} + +// get the root of the target folder +def getRootDir() { + def dir = _findBuildYamlFile(meta.resources_dir) + assert dir != null: "Could not find .build.yaml in the folder structure" + dir.getParent() +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/iterateMap.nf' +/** + * Recursively apply a function over the leaves of an object. + * @param obj The object to iterate over. + * @param fun The function to apply to each value. + * @return The object with the function applied to each value. + */ +def iterateMap(obj, fun) { + if (obj instanceof List && obj !instanceof String) { + return obj.collect{item -> + iterateMap(item, fun) + } + } else if (obj instanceof Map) { + return obj.collectEntries{key, item -> + [key.toString(), iterateMap(item, fun)] + } + } else { + return fun(obj) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/functions/niceView.nf' +/** + * A view for printing the event of each channel as a YAML blob. + * This is useful for debugging. + */ +def niceView() { + workflow niceViewWf { + take: input + main: + output = input + | view{toYamlBlob(it)} + emit: output + } + return niceViewWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readCsv.nf' + +def readCsv(file_path) { + def output = [] + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + + // todo: allow escaped quotes in string + // todo: allow single quotes? + def splitRegex = java.util.regex.Pattern.compile(''',(?=(?:[^"]*"[^"]*")*[^"]*$)''') + def removeQuote = java.util.regex.Pattern.compile('''"(.*)"''') + + def br = java.nio.file.Files.newBufferedReader(inputFile) + + def row = -1 + def header = null + while (br.ready() && header == null) { + def line = br.readLine() + row++ + if (!line.startsWith("#")) { + header = splitRegex.split(line, -1).collect{field -> + m = removeQuote.matcher(field) + m.find() ? m.replaceFirst('$1') : field + } + } + } + assert header != null: "CSV file should contain a header" + + while (br.ready()) { + def line = br.readLine() + row++ + if (line == null) { + br.close() + break + } + + if (!line.startsWith("#")) { + def predata = splitRegex.split(line, -1) + def data = predata.collect{field -> + if (field == "") { + return null + } + def m = removeQuote.matcher(field) + if (m.find()) { + return m.replaceFirst('$1') + } else { + return field + } + } + assert header.size() == data.size(): "Row $row should contain the same number as fields as the header" + + def dataMap = [header, data].transpose().collectEntries().findAll{it.value != null} + output.add(dataMap) + } + } + + output +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJson.nf' +def readJson(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parse(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readJsonBlob.nf' +def readJsonBlob(str) { + def jsonSlurper = new groovy.json.JsonSlurper() + jsonSlurper.parseText(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readTaggedYaml.nf' +// Custom constructor to modify how certain objects are parsed from YAML +class CustomConstructor extends org.yaml.snakeyaml.constructor.Constructor { + Path root + + class ConstructPath extends org.yaml.snakeyaml.constructor.AbstractConstruct { + public Object construct(org.yaml.snakeyaml.nodes.Node node) { + String filename = (String) constructScalar(node); + if (root != null) { + return root.resolve(filename); + } + return java.nio.file.Paths.get(filename); + } + } + + CustomConstructor(org.yaml.snakeyaml.LoaderOptions options, Path root) { + super(options) + this.root = root + // Handling !file tag and parse it back to a File type + this.yamlConstructors.put(new org.yaml.snakeyaml.nodes.Tag("!file"), new ConstructPath()) + } +} + +def readTaggedYaml(Path path) { + def options = new org.yaml.snakeyaml.LoaderOptions() + def constructor = new CustomConstructor(options, path.getParent()) + def yaml = new org.yaml.snakeyaml.Yaml(constructor) + return yaml.load(path.text) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYaml.nf' +def readYaml(file_path) { + def inputFile = file_path !instanceof Path ? file(file_path, hidden: true) : file_path + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(inputFile) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/readYamlBlob.nf' +def readYamlBlob(str) { + def yamlSlurper = new org.yaml.snakeyaml.Yaml() + yamlSlurper.load(str) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toJsonBlob.nf' +String toJsonBlob(data) { + return groovy.json.JsonOutput.toJson(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toTaggedYamlBlob.nf' +// Custom representer to modify how certain objects are represented in YAML +class CustomRepresenter extends org.yaml.snakeyaml.representer.Representer { + Path relativizer + + class RepresentPath implements org.yaml.snakeyaml.representer.Represent { + public String getFileName(Object obj) { + if (obj instanceof File) { + obj = ((File) obj).toPath(); + } + if (obj !instanceof Path) { + throw new IllegalArgumentException("Object: " + obj + " is not a Path or File"); + } + def path = (Path) obj; + + if (relativizer != null) { + return relativizer.relativize(path).toString() + } else { + return path.toString() + } + } + + public org.yaml.snakeyaml.nodes.Node representData(Object data) { + String filename = getFileName(data); + def tag = new org.yaml.snakeyaml.nodes.Tag("!file"); + return representScalar(tag, filename); + } + } + CustomRepresenter(org.yaml.snakeyaml.DumperOptions options, Path relativizer) { + super(options) + this.relativizer = relativizer + this.representers.put(sun.nio.fs.UnixPath, new RepresentPath()) + this.representers.put(Path, new RepresentPath()) + this.representers.put(File, new RepresentPath()) + } +} + +String toTaggedYamlBlob(data) { + return toRelativeTaggedYamlBlob(data, null) +} +String toRelativeTaggedYamlBlob(data, Path relativizer) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + def representer = new CustomRepresenter(options, relativizer) + def yaml = new org.yaml.snakeyaml.Yaml(representer, options) + return yaml.dump(data) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/toYamlBlob.nf' +String toYamlBlob(data) { + def options = new org.yaml.snakeyaml.DumperOptions() + options.setDefaultFlowStyle(org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK) + options.setPrettyFlow(true) + def yaml = new org.yaml.snakeyaml.Yaml(options) + def cleanData = iterateMap(data, { it instanceof Path ? it.toString() : it }) + return yaml.dump(cleanData) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeJson.nf' +void writeJson(data, file) { + assert data: "writeJson: data should not be null" + assert file: "writeJson: file should not be null" + file.write(toJsonBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/readwrite/writeYaml.nf' +void writeYaml(data, file) { + assert data: "writeYaml: data should not be null" + assert file: "writeYaml: file should not be null" + file.write(toYamlBlob(data)) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/findStates.nf' +def findStates(Map params, Map config) { + def auto_config = deepClone(config) + def auto_params = deepClone(params) + + auto_config = auto_config.clone() + // override arguments + auto_config.argument_groups = [] + auto_config.arguments = [ + [ + type: "string", + name: "--id", + description: "A dummy identifier", + required: false + ], + [ + type: "file", + name: "--input_states", + example: "/path/to/input/directory/**/state.yaml", + description: "Path to input directory containing the datasets to be integrated.", + required: true, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--filter", + example: "foo/.*/state.yaml", + description: "Regex to filter state files by path.", + required: false + ], + // to do: make this a yaml blob? + [ + type: "string", + name: "--rename_keys", + example: ["newKey1:oldKey1", "newKey2:oldKey2"], + description: "Rename keys in the detected input files. This is useful if the input files do not match the set of input arguments of the workflow.", + required: false, + multiple: true, + multiple_sep: ";" + ], + [ + type: "string", + name: "--settings", + example: '{"output_dataset": "dataset.h5ad", "k": 10}', + description: "Global arguments as a JSON glob to be passed to all components.", + required: false + ] + ] + if (!(auto_params.containsKey("id"))) { + auto_params["id"] = "auto" + } + + // run auto config through processConfig once more + auto_config = processConfig(auto_config) + + workflow findStatesWf { + helpMessage(auto_config) + + output_ch = + channelFromParams(auto_params, auto_config) + | flatMap { autoId, args -> + + def globalSettings = args.settings ? readYamlBlob(args.settings) : [:] + + // look for state files in input dir + def stateFiles = args.input_states + + // filter state files by regex + if (args.filter) { + stateFiles = stateFiles.findAll{ stateFile -> + def stateFileStr = stateFile.toString() + def matcher = stateFileStr =~ args.filter + matcher.matches()} + } + + // read in states + def states = stateFiles.collect { stateFile -> + def state_ = readTaggedYaml(stateFile) + [state_.id, state_] + } + + // construct renameMap + if (args.rename_keys) { + def renameMap = args.rename_keys.collectEntries{renameString -> + def split = renameString.split(";") + assert split.size() == 2: "Argument 'rename_keys' should be of the form 'newKey:oldKey,newKey:oldKey'" + split + } + + // rename keys in state, only let states through which have all keys + // also add global settings + states = states.collectMany{id, state -> + def newState = [:] + + for (key in renameMap.keySet()) { + def origKey = renameMap[key] + if (!(state.containsKey(origKey))) { + return [] + } + newState[key] = state[origKey] + } + + [[id, globalSettings + newState]] + } + } + + states + } + emit: + output_ch + } + + return findStatesWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/joinStates.nf' +def joinStates(Closure apply_) { + workflow joinStatesWf { + take: input_ch + main: + output_ch = input_ch + | toSortedList + | filter{ it.size() > 0 } + | map{ tups -> + def ids = tups.collect{it[0]} + def states = tups.collect{it[1]} + apply_(ids, states) + } + + emit: output_ch + } + return joinStatesWf +} +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/publishStates.nf' +def collectFiles(obj) { + if (obj instanceof java.io.File || obj instanceof Path) { + return [obj] + } else if (obj instanceof List && obj !instanceof String) { + return obj.collectMany{item -> + collectFiles(item) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectFiles(item) + } + } else { + return [] + } +} + +/** + * Recurse through a state and collect all input files and their target output filenames. + * @param obj The state to recurse through. + * @param prefix The prefix to prepend to the output filenames. + */ +def collectInputOutputPaths(obj, prefix) { + if (obj instanceof File || obj instanceof Path) { + def path = obj instanceof Path ? obj : obj.toPath() + def ext = path.getFileName().toString().find("\\.[^\\.]+\$") ?: "" + def newFilename = prefix + ext + return [[obj, newFilename]] + } else if (obj instanceof List && obj !instanceof String) { + return obj.withIndex().collectMany{item, ix -> + collectInputOutputPaths(item, prefix + "_" + ix) + } + } else if (obj instanceof Map) { + return obj.collectMany{key, item -> + collectInputOutputPaths(item, prefix + "." + key) + } + } else { + return [] + } +} + +def publishStates(Map args) { + def key_ = args.get("key") + def yamlTemplate_ = args.get("output_state", args.get("outputState", '$id.$key.state.yaml')) + + assert key_ != null : "publishStates: key must be specified" + + workflow publishStatesWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] + + // the input files and the target output filenames + def inputoutputFilenames_ = collectInputOutputPaths(state_, id_ + "." + key_).transpose() + def inputFiles_ = inputoutputFilenames_[0] + def outputFilenames_ = inputoutputFilenames_[1] + + def yamlFilename = yamlTemplate_ + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + + // TODO: do the pathnames in state_ match up with the outputFilenames_? + + // convert state to yaml blob + def yamlBlob_ = toRelativeTaggedYamlBlob([id: id_] + state_, java.nio.file.Paths.get(yamlFilename)) + + [id_, yamlBlob_, yamlFilename, inputFiles_, outputFilenames_] + } + | publishStatesProc + emit: input_ch + } + return publishStatesWf +} +process publishStatesProc { + // todo: check publishpath? + publishDir path: "${getPublishDir()}/", mode: "copy" + tag "$id" + input: + tuple val(id), val(yamlBlob), val(yamlFile), path(inputFiles, stageAs: "_inputfile?/*"), val(outputFiles) + output: + tuple val(id), path{[yamlFile] + outputFiles} + script: + def copyCommands = [ + inputFiles instanceof List ? inputFiles : [inputFiles], + outputFiles instanceof List ? outputFiles : [outputFiles] + ] + .transpose() + .collectMany{infile, outfile -> + if (infile.toString() != outfile.toString()) { + [ + "[ -d \"\$(dirname '${outfile.toString()}')\" ] || mkdir -p \"\$(dirname '${outfile.toString()}')\"", + "cp -r '${infile.toString()}' '${outfile.toString()}'" + ] + } else { + // no need to copy if infile is the same as outfile + [] + } + } + """ +mkdir -p "\$(dirname '${yamlFile}')" +echo "Storing state as yaml" +echo '${yamlBlob}' > '${yamlFile}' +echo "Copying output files to destination folder" +${copyCommands.join("\n ")} +""" +} + + +// this assumes that the state contains no other values other than those specified in the config +def publishStatesByConfig(Map args) { + def config = args.get("config") + assert config != null : "publishStatesByConfig: config must be specified" + + def key_ = args.get("key", config.name) + assert key_ != null : "publishStatesByConfig: key must be specified" + + workflow publishStatesSimpleWf { + take: input_ch + main: + input_ch + | map { tup -> + def id_ = tup[0] + def state_ = tup[1] // e.g. [output: new File("myoutput.h5ad"), k: 10] + def origState_ = tup[2] // e.g. [output: '$id.$key.foo.h5ad'] + + // TODO: allow overriding the state.yaml template + // TODO TODO: if auto.publish == "state", add output_state as an argument + def yamlTemplate = params.containsKey("output_state") ? params.output_state : '$id.$key.state.yaml' + def yamlFilename = yamlTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + def yamlDir = java.nio.file.Paths.get(yamlFilename).getParent() + + // the processed state is a list of [key, value, inputPath, outputFilename] tuples, where + // - key is a String + // - value is any object that can be serialized to a Yaml (so a String/Integer/Long/Double/Boolean, a List, a Map, or a Path) + // - inputPath is a List[Path] + // - outputFilename is a List[String] + // - (key, value) are the tuples that will be saved to the state.yaml file + // - (inputPath, outputFilename) are the files that will be copied from src to dest (relative to the state.yaml) + def processedState = + config.allArguments + .findAll { it.direction == "output" } + .collectMany { par -> + def plainName_ = par.plainName + // if the state does not contain the key, it's an + // optional argument for which the component did + // not generate any output + if (!state_.containsKey(plainName_)) { + return [] + } + def value = state_[plainName_] + // if the parameter is not a file, it should be stored + // in the state as-is, but is not something that needs + // to be copied from the source path to the dest path + if (par.type != "file") { + return [[key: plainName_, value: value, inputPath: [], outputFilename: []]] + } + // if the orig state does not contain this filename, + // it's an optional argument for which the user specified + // that it should not be returned as a state + if (!origState_.containsKey(plainName_)) { + return [] + } + def filenameTemplate = origState_[plainName_] + // if the pararameter is multiple: true, fetch the template + if (par.multiple && filenameTemplate instanceof List) { + filenameTemplate = filenameTemplate[0] + } + // instantiate the template + def filename = filenameTemplate + .replaceAll('\\$id', id_) + .replaceAll('\\$key', key_) + if (par.multiple) { + // if the parameter is multiple: true, the filename + // should contain a wildcard '*' that is replaced with + // the index of the file + assert filename.contains("*") : "Module '${key_}' id '${id_}': Multiple output files specified, but no wildcard '*' in the filename: ${filename}" + def outputPerFile = value.withIndex().collect{ val, ix -> + def filename_ix = filename.replace("*", ix.toString()) + def value_ = java.nio.file.Paths.get(filename_ix) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = val instanceof File ? val.toPath() : val + [value: value_, inputPath: inputPath, outputFilename: filename_ix] + } + def transposedOutputs = ["value", "inputPath", "outputFilename"].collectEntries{ key -> + [key, outputPerFile.collect{dic -> dic[key]}] + } + return [[key: plainName_] + transposedOutputs] + } else { + def value_ = java.nio.file.Paths.get(filename) + // if id contains a slash + if (yamlDir != null) { + value_ = yamlDir.relativize(value_) + } + def inputPath = value instanceof File ? value.toPath() : value + return [[key: plainName_, value: value_, inputPath: [inputPath], outputFilename: [filename]]] + } + } + + def updatedState_ = processedState.collectEntries{[it.key, it.value]} + def inputPaths = processedState.collectMany{it.inputPath} + def outputFilenames = processedState.collectMany{it.outputFilename} + + // convert state to yaml blob + def yamlBlob_ = toTaggedYamlBlob([id: id_] + updatedState_) + + [id_, yamlBlob_, yamlFilename, inputPaths, outputFilenames] + } + | publishStatesProc + emit: input_ch + } + return publishStatesSimpleWf +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/states/setState.nf' +def setState(fun) { + assert fun instanceof Closure || fun instanceof Map || fun instanceof List : + "Error in setState: Expected process argument to be a Closure, a Map, or a List. Found: class ${fun.getClass()}" + + // if fun is a List, convert to map + if (fun instanceof List) { + // check whether fun is a list[string] + assert fun.every{it instanceof CharSequence} : "Error in setState: argument is a List, but not all elements are Strings" + fun = fun.collectEntries{[it, it]} + } + + // if fun is a map, convert to closure + if (fun instanceof Map) { + // check whether fun is a map[string, string] + assert fun.values().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all values are Strings" + assert fun.keySet().every{it instanceof CharSequence} : "Error in setState: argument is a Map, but not all keys are Strings" + def funMap = fun.clone() + // turn the map into a closure to be used later on + fun = { id_, state_ -> + assert state_ instanceof Map : "Error in setState: the state is not a Map" + funMap.collectMany{newkey, origkey -> + if (state_.containsKey(origkey)) { + [[newkey, state_[origkey]]] + } else { + [] + } + }.collectEntries() + } + } + + map { tup -> + def id = tup[0] + def state = tup[1] + def unfilteredState = fun(id, state) + def newState = unfilteredState.findAll{key, val -> val != null} + [id, newState] + tup.drop(2) + } +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processAuto.nf' +// TODO: unit test processAuto +def processAuto(Map auto) { + // remove null values + auto = auto.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = ["simplifyInput", "simplifyOutput", "transcript", "publish"] + def unexpectedKeys = auto.keySet() - expectedKeys + assert unexpectedKeys.isEmpty(), "unexpected keys in auto: '${unexpectedKeys.join("', '")}'" + + // check auto.simplifyInput + assert auto.simplifyInput instanceof Boolean, "auto.simplifyInput must be a boolean" + + // check auto.simplifyOutput + assert auto.simplifyOutput instanceof Boolean, "auto.simplifyOutput must be a boolean" + + // check auto.transcript + assert auto.transcript instanceof Boolean, "auto.transcript must be a boolean" + + // check auto.publish + assert auto.publish instanceof Boolean || auto.publish == "state", "auto.publish must be a boolean or 'state'" + + return auto.subMap(expectedKeys) +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processDirectives.nf' +def assertMapKeys(map, expectedKeys, requiredKeys, mapName) { + assert map instanceof Map : "Expected argument '$mapName' to be a Map. Found: class ${map.getClass()}" + map.forEach { key, val -> + assert key in expectedKeys : "Unexpected key '$key' in ${mapName ? mapName + " " : ""}map" + } + requiredKeys.forEach { requiredKey -> + assert map.containsKey(requiredKey) : "Missing required key '$key' in ${mapName ? mapName + " " : ""}map" + } +} + +// TODO: unit test processDirectives +def processDirectives(Map drctv) { + // remove null values + drctv = drctv.findAll{k, v -> v != null} + + // check for unexpected keys + def expectedKeys = [ + "accelerator", "afterScript", "beforeScript", "cache", "conda", "container", "containerOptions", "cpus", "disk", "echo", "errorStrategy", "executor", "machineType", "maxErrors", "maxForks", "maxRetries", "memory", "module", "penv", "pod", "publishDir", "queue", "label", "scratch", "storeDir", "stageInMode", "stageOutMode", "tag", "time" + ] + def unexpectedKeys = drctv.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Unexpected keys in process directive: '${unexpectedKeys.join("', '")}'" + + /* DIRECTIVE accelerator + accepted examples: + - [ limit: 4, type: "nvidia-tesla-k80" ] + */ + if (drctv.containsKey("accelerator")) { + assertMapKeys(drctv["accelerator"], ["type", "limit", "request", "runtime"], [], "accelerator") + } + + /* DIRECTIVE afterScript + accepted examples: + - "source /cluster/bin/cleanup" + */ + if (drctv.containsKey("afterScript")) { + assert drctv["afterScript"] instanceof CharSequence + } + + /* DIRECTIVE beforeScript + accepted examples: + - "source /cluster/bin/setup" + */ + if (drctv.containsKey("beforeScript")) { + assert drctv["beforeScript"] instanceof CharSequence + } + + /* DIRECTIVE cache + accepted examples: + - true + - false + - "deep" + - "lenient" + */ + if (drctv.containsKey("cache")) { + assert drctv["cache"] instanceof CharSequence || drctv["cache"] instanceof Boolean + if (drctv["cache"] instanceof CharSequence) { + assert drctv["cache"] in ["deep", "lenient"] : "Unexpected value for cache" + } + } + + /* DIRECTIVE conda + accepted examples: + - "bwa=0.7.15" + - "bwa=0.7.15 fastqc=0.11.5" + - ["bwa=0.7.15", "fastqc=0.11.5"] + */ + if (drctv.containsKey("conda")) { + if (drctv["conda"] instanceof List) { + drctv["conda"] = drctv["conda"].join(" ") + } + assert drctv["conda"] instanceof CharSequence + } + + /* DIRECTIVE container + accepted examples: + - "foo/bar:tag" + - [ registry: "reg", image: "im", tag: "ta" ] + is transformed to "reg/im:ta" + - [ image: "im" ] + is transformed to "im:latest" + */ + if (drctv.containsKey("container")) { + assert drctv["container"] instanceof Map || drctv["container"] instanceof CharSequence + if (drctv["container"] instanceof Map) { + def m = drctv["container"] + assertMapKeys(m, [ "registry", "image", "tag" ], ["image"], "container") + def part1 = + System.getenv('OVERRIDE_CONTAINER_REGISTRY') ? System.getenv('OVERRIDE_CONTAINER_REGISTRY') + "/" : + params.containsKey("override_container_registry") ? params["override_container_registry"] + "/" : // todo: remove? + m.registry ? m.registry + "/" : + "" + def part2 = m.image + def part3 = m.tag ? ":" + m.tag : ":latest" + drctv["container"] = part1 + part2 + part3 + } + } + + /* DIRECTIVE containerOptions + accepted examples: + - "--foo bar" + - ["--foo bar", "-f b"] + */ + if (drctv.containsKey("containerOptions")) { + if (drctv["containerOptions"] instanceof List) { + drctv["containerOptions"] = drctv["containerOptions"].join(" ") + } + assert drctv["containerOptions"] instanceof CharSequence + } + + /* DIRECTIVE cpus + accepted examples: + - 1 + - 10 + */ + if (drctv.containsKey("cpus")) { + assert drctv["cpus"] instanceof Integer + } + + /* DIRECTIVE disk + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("disk")) { + assert drctv["disk"] instanceof CharSequence + // assert drctv["disk"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE echo + accepted examples: + - true + - false + */ + if (drctv.containsKey("echo")) { + assert drctv["echo"] instanceof Boolean + } + + /* DIRECTIVE errorStrategy + accepted examples: + - "terminate" + - "finish" + */ + if (drctv.containsKey("errorStrategy")) { + assert drctv["errorStrategy"] instanceof CharSequence + assert drctv["errorStrategy"] in ["terminate", "finish", "ignore", "retry"] : "Unexpected value for errorStrategy" + } + + /* DIRECTIVE executor + accepted examples: + - "local" + - "sge" + */ + if (drctv.containsKey("executor")) { + assert drctv["executor"] instanceof CharSequence + assert drctv["executor"] in ["local", "sge", "uge", "lsf", "slurm", "pbs", "pbspro", "moab", "condor", "nqsii", "ignite", "k8s", "awsbatch", "google-pipelines"] : "Unexpected value for executor" + } + + /* DIRECTIVE machineType + accepted examples: + - "n1-highmem-8" + */ + if (drctv.containsKey("machineType")) { + assert drctv["machineType"] instanceof CharSequence + } + + /* DIRECTIVE maxErrors + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxErrors")) { + assert drctv["maxErrors"] instanceof Integer + } + + /* DIRECTIVE maxForks + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxForks")) { + assert drctv["maxForks"] instanceof Integer + } + + /* DIRECTIVE maxRetries + accepted examples: + - 1 + - 3 + */ + if (drctv.containsKey("maxRetries")) { + assert drctv["maxRetries"] instanceof Integer + } + + /* DIRECTIVE memory + accepted examples: + - "1 GB" + - "2TB" + - "3.2KB" + - "10.B" + */ + if (drctv.containsKey("memory")) { + assert drctv["memory"] instanceof CharSequence + // assert drctv["memory"].matches("[0-9]+(\\.[0-9]*)? *[KMGTPEZY]?B") + // ^ does not allow closures + } + + /* DIRECTIVE module + accepted examples: + - "ncbi-blast/2.2.27" + - "ncbi-blast/2.2.27:t_coffee/10.0" + - ["ncbi-blast/2.2.27", "t_coffee/10.0"] + */ + if (drctv.containsKey("module")) { + if (drctv["module"] instanceof List) { + drctv["module"] = drctv["module"].join(":") + } + assert drctv["module"] instanceof CharSequence + } + + /* DIRECTIVE penv + accepted examples: + - "smp" + */ + if (drctv.containsKey("penv")) { + assert drctv["penv"] instanceof CharSequence + } + + /* DIRECTIVE pod + accepted examples: + - [ label: "key", value: "val" ] + - [ annotation: "key", value: "val" ] + - [ env: "key", value: "val" ] + - [ [label: "l", value: "v"], [env: "e", value: "v"]] + */ + if (drctv.containsKey("pod")) { + if (drctv["pod"] instanceof Map) { + drctv["pod"] = [ drctv["pod"] ] + } + assert drctv["pod"] instanceof List + drctv["pod"].forEach { pod -> + assert pod instanceof Map + // TODO: should more checks be added? + // See https://www.nextflow.io/docs/latest/process.html?highlight=directives#pod + // e.g. does it contain 'label' and 'value', or 'annotation' and 'value', or ...? + } + } + + /* DIRECTIVE publishDir + accepted examples: + - [] + - [ [ path: "foo", enabled: true ], [ path: "bar", enabled: false ] ] + - "/path/to/dir" + is transformed to [[ path: "/path/to/dir" ]] + - [ path: "/path/to/dir", mode: "cache" ] + is transformed to [[ path: "/path/to/dir", mode: "cache" ]] + */ + // TODO: should we also look at params["publishDir"]? + if (drctv.containsKey("publishDir")) { + def pblsh = drctv["publishDir"] + + // check different options + assert pblsh instanceof List || pblsh instanceof Map || pblsh instanceof CharSequence + + // turn into list if not already so + // for some reason, 'if (!pblsh instanceof List) pblsh = [ pblsh ]' doesn't work. + pblsh = pblsh instanceof List ? pblsh : [ pblsh ] + + // check elements of publishDir + pblsh = pblsh.collect{ elem -> + // turn into map if not already so + elem = elem instanceof CharSequence ? [ path: elem ] : elem + + // check types and keys + assert elem instanceof Map : "Expected publish argument '$elem' to be a String or a Map. Found: class ${elem.getClass()}" + assertMapKeys(elem, [ "path", "mode", "overwrite", "pattern", "saveAs", "enabled" ], ["path"], "publishDir") + + // check elements in map + assert elem.containsKey("path") + assert elem["path"] instanceof CharSequence + if (elem.containsKey("mode")) { + assert elem["mode"] instanceof CharSequence + assert elem["mode"] in [ "symlink", "rellink", "link", "copy", "copyNoFollow", "move" ] + } + if (elem.containsKey("overwrite")) { + assert elem["overwrite"] instanceof Boolean + } + if (elem.containsKey("pattern")) { + assert elem["pattern"] instanceof CharSequence + } + if (elem.containsKey("saveAs")) { + assert elem["saveAs"] instanceof CharSequence //: "saveAs as a Closure is currently not supported. Surround your closure with single quotes to get the desired effect. Example: '\{ foo \}'" + } + if (elem.containsKey("enabled")) { + assert elem["enabled"] instanceof Boolean + } + + // return final result + elem + } + // store final directive + drctv["publishDir"] = pblsh + } + + /* DIRECTIVE queue + accepted examples: + - "long" + - "short,long" + - ["short", "long"] + */ + if (drctv.containsKey("queue")) { + if (drctv["queue"] instanceof List) { + drctv["queue"] = drctv["queue"].join(",") + } + assert drctv["queue"] instanceof CharSequence + } + + /* DIRECTIVE label + accepted examples: + - "big_mem" + - "big_cpu" + - ["big_mem", "big_cpu"] + */ + if (drctv.containsKey("label")) { + if (drctv["label"] instanceof CharSequence) { + drctv["label"] = [ drctv["label"] ] + } + assert drctv["label"] instanceof List + drctv["label"].forEach { label -> + assert label instanceof CharSequence + // assert label.matches("[a-zA-Z0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?") + // ^ does not allow closures + } + } + + /* DIRECTIVE scratch + accepted examples: + - true + - "/path/to/scratch" + - '$MY_PATH_TO_SCRATCH' + - "ram-disk" + */ + if (drctv.containsKey("scratch")) { + assert drctv["scratch"] == true || drctv["scratch"] instanceof CharSequence + } + + /* DIRECTIVE storeDir + accepted examples: + - "/path/to/storeDir" + */ + if (drctv.containsKey("storeDir")) { + assert drctv["storeDir"] instanceof CharSequence + } + + /* DIRECTIVE stageInMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageInMode")) { + assert drctv["stageInMode"] instanceof CharSequence + assert drctv["stageInMode"] in ["copy", "link", "symlink", "rellink"] + } + + /* DIRECTIVE stageOutMode + accepted examples: + - "copy" + - "link" + */ + if (drctv.containsKey("stageOutMode")) { + assert drctv["stageOutMode"] instanceof CharSequence + assert drctv["stageOutMode"] in ["copy", "move", "rsync"] + } + + /* DIRECTIVE tag + accepted examples: + - "foo" + - '$id' + */ + if (drctv.containsKey("tag")) { + assert drctv["tag"] instanceof CharSequence + } + + /* DIRECTIVE time + accepted examples: + - "1h" + - "2days" + - "1day 6hours 3minutes 30seconds" + */ + if (drctv.containsKey("time")) { + assert drctv["time"] instanceof CharSequence + // todo: validation regex? + } + + return drctv +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/processWorkflowArgs.nf' +def processWorkflowArgs(Map args, Map defaultWfArgs, Map meta) { + // override defaults with args + def workflowArgs = defaultWfArgs + args + + // check whether 'key' exists + assert workflowArgs.containsKey("key") : "Error in module '${meta.config.name}': key is a required argument" + + // if 'key' is a closure, apply it to the original key + if (workflowArgs["key"] instanceof Closure) { + workflowArgs["key"] = workflowArgs["key"](meta.config.name) + } + def key = workflowArgs["key"] + assert key instanceof CharSequence : "Expected process argument 'key' to be a String. Found: class ${key.getClass()}" + assert key ==~ /^[a-zA-Z_]\w*$/ : "Error in module '$key': Expected process argument 'key' to consist of only letters, digits or underscores. Found: ${key}" + + // check for any unexpected keys + def expectedKeys = ["key", "directives", "auto", "map", "mapId", "mapData", "mapPassthrough", "filter", "runIf", "fromState", "toState", "args", "renameKeys", "debug"] + def unexpectedKeys = workflowArgs.keySet() - expectedKeys + assert unexpectedKeys.isEmpty() : "Error in module '$key': unexpected arguments to the '.run()' function: '${unexpectedKeys.join("', '")}'" + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("directives") : "Error in module '$key': directives is a required argument" + assert workflowArgs["directives"] instanceof Map : "Error in module '$key': Expected process argument 'directives' to be a Map. Found: class ${workflowArgs['directives'].getClass()}" + workflowArgs["directives"] = processDirectives(defaultWfArgs.directives + workflowArgs["directives"]) + + // check whether directives exists and apply defaults + assert workflowArgs.containsKey("auto") : "Error in module '$key': auto is a required argument" + assert workflowArgs["auto"] instanceof Map : "Error in module '$key': Expected process argument 'auto' to be a Map. Found: class ${workflowArgs['auto'].getClass()}" + workflowArgs["auto"] = processAuto(defaultWfArgs.auto + workflowArgs["auto"]) + + // auto define publish, if so desired + if (workflowArgs.auto.publish == true && (workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : [:]).isEmpty()) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.publish is true, params.publish_dir needs to be defined.\n" + + // " Example: params.publish_dir = \"./output/\"" + def publishDir = getPublishDir() + + if (publishDir != null) { + workflowArgs.directives.publishDir = [[ + path: publishDir, + saveAs: "{ it.startsWith('.') ? null : it }", // don't publish hidden files, by default + mode: "copy" + ]] + } + } + + // auto define transcript, if so desired + if (workflowArgs.auto.transcript == true) { + // can't assert at this level thanks to the no_publish profile + // assert params.containsKey("transcriptsDir") || params.containsKey("transcripts_dir") || params.containsKey("publishDir") || params.containsKey("publish_dir") : + // "Error in module '${workflowArgs['key']}': if auto.transcript is true, either params.transcripts_dir or params.publish_dir needs to be defined.\n" + + // " Example: params.transcripts_dir = \"./transcripts/\"" + def transcriptsDir = + params.containsKey("transcripts_dir") ? params.transcripts_dir : + params.containsKey("transcriptsDir") ? params.transcriptsDir : + params.containsKey("publish_dir") ? params.publish_dir + "/_transcripts" : + params.containsKey("publishDir") ? params.publishDir + "/_transcripts" : + null + if (transcriptsDir != null) { + def timestamp = nextflow.Nextflow.getSession().getWorkflowMetadata().start.format('yyyy-MM-dd_HH-mm-ss') + def transcriptsPublishDir = [ + path: "$transcriptsDir/$timestamp/\${task.process.replaceAll(':', '-')}/\${id}/", + saveAs: "{ it.startsWith('.') ? it.replaceAll('^.', '') : null }", + mode: "copy" + ] + def publishDirs = workflowArgs.directives.publishDir != null ? workflowArgs.directives.publishDir : null ? workflowArgs.directives.publishDir : [] + workflowArgs.directives.publishDir = publishDirs + transcriptsPublishDir + } + } + + // if this is a stubrun, remove certain directives? + if (workflow.stubRun) { + workflowArgs.directives.keySet().removeAll(["publishDir", "cpus", "memory", "label"]) + } + + for (nam in ["map", "mapId", "mapData", "mapPassthrough", "filter", "runIf"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam]) { + assert workflowArgs[nam] instanceof Closure : "Error in module '$key': Expected process argument '$nam' to be null or a Closure. Found: class ${workflowArgs[nam].getClass()}" + } + } + + // TODO: should functions like 'map', 'mapId', 'mapData', 'mapPassthrough' be deprecated as well? + for (nam in ["map", "mapData", "mapPassthrough", "renameKeys"]) { + if (workflowArgs.containsKey(nam) && workflowArgs[nam] != null) { + log.warn "module '$key': workflow argument '$nam' is deprecated and will be removed in Viash 0.9.0. Please use 'fromState' and 'toState' instead." + } + } + + // check fromState + workflowArgs["fromState"] = _processFromState(workflowArgs.get("fromState"), key, meta.config) + + // check toState + workflowArgs["toState"] = _processToState(workflowArgs.get("toState"), key, meta.config) + + // return output + return workflowArgs +} + +def _processFromState(fromState, key_, config_) { + assert fromState == null || fromState instanceof Closure || fromState instanceof Map || fromState instanceof List : + "Error in module '$key_': Expected process argument 'fromState' to be null, a Closure, a Map, or a List. Found: class ${fromState.getClass()}" + if (fromState == null) { + return null + } + + // if fromState is a List, convert to map + if (fromState instanceof List) { + // check whether fromstate is a list[string] + assert fromState.every{it instanceof CharSequence} : "Error in module '$key_': fromState is a List, but not all elements are Strings" + fromState = fromState.collectEntries{[it, it]} + } + + // if fromState is a map, convert to closure + if (fromState instanceof Map) { + // check whether fromstate is a map[string, string] + assert fromState.values().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all values are Strings" + assert fromState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': fromState is a Map, but not all keys are Strings" + def fromStateMap = fromState.clone() + def requiredInputNames = meta.config.allArguments.findAll{it.required && it.direction == "Input"}.collect{it.plainName} + // turn the map into a closure to be used later on + fromState = { it -> + def state = it[1] + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def data = fromStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (state.containsKey(origkey)) { + [[newkey, state[origkey]]] + } else if (!requiredInputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': fromState key '$origkey' not found in current state") + } + }.collectEntries() + data + } + } + + return fromState +} + +def _processToState(toState, key_, config_) { + if (toState == null) { + toState = { tup -> tup[1] } + } + + // toState should be a closure, map[string, string], or list[string] + assert toState instanceof Closure || toState instanceof Map || toState instanceof List : + "Error in module '$key_': Expected process argument 'toState' to be a Closure, a Map, or a List. Found: class ${toState.getClass()}" + + // if toState is a List, convert to map + if (toState instanceof List) { + // check whether toState is a list[string] + assert toState.every{it instanceof CharSequence} : "Error in module '$key_': toState is a List, but not all elements are Strings" + toState = toState.collectEntries{[it, it]} + } + + // if toState is a map, convert to closure + if (toState instanceof Map) { + // check whether toState is a map[string, string] + assert toState.values().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all values are Strings" + assert toState.keySet().every{it instanceof CharSequence} : "Error in module '$key_': toState is a Map, but not all keys are Strings" + def toStateMap = toState.clone() + def requiredOutputNames = config_.allArguments.findAll{it.required && it.direction == "Output"}.collect{it.plainName} + // turn the map into a closure to be used later on + toState = { it -> + def output = it[1] + def state = it[2] + assert output instanceof Map : "Error in module '$key_': the output is not a Map" + assert state instanceof Map : "Error in module '$key_': the state is not a Map" + def extraEntries = toStateMap.collectMany{newkey, origkey -> + // check whether newkey corresponds to a required argument + if (output.containsKey(origkey)) { + [[newkey, output[origkey]]] + } else if (!requiredOutputNames.contains(origkey)) { + [] + } else { + throw new Exception("Error in module '$key_': toState key '$origkey' not found in current output") + } + }.collectEntries() + state + extraEntries + } + } + + return toState +} + +// helper file: 'src/main/resources/io/viash/runners/nextflow/workflowFactory/workflowFactory.nf' +def _debug(workflowArgs, debugKey) { + if (workflowArgs.debug) { + view { "process '${workflowArgs.key}' $debugKey tuple: $it" } + } else { + map { it } + } +} + +// depends on: innerWorkflowFactory +def workflowFactory(Map args, Map defaultWfArgs, Map meta) { + def workflowArgs = processWorkflowArgs(args, defaultWfArgs, meta) + def key_ = workflowArgs["key"] + + workflow workflowInstance { + take: input_ + + main: + def chModified = input_ + | checkUniqueIds([:]) + | _debug(workflowArgs, "input") + | map { tuple -> + tuple = deepClone(tuple) + + if (workflowArgs.map) { + tuple = workflowArgs.map(tuple) + } + if (workflowArgs.mapId) { + tuple[0] = workflowArgs.mapId(tuple[0]) + } + if (workflowArgs.mapData) { + tuple[1] = workflowArgs.mapData(tuple[1]) + } + if (workflowArgs.mapPassthrough) { + tuple = tuple.take(2) + workflowArgs.mapPassthrough(tuple.drop(2)) + } + + // check tuple + assert tuple instanceof List : + "Error in module '${key_}': element in channel should be a tuple [id, data, ...otherargs...]\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: List. Found: tuple.getClass() is ${tuple.getClass()}" + assert tuple.size() >= 2 : + "Error in module '${key_}': expected length of tuple in input channel to be two or greater.\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: tuple.size() == ${tuple.size()}" + + // check id field + if (tuple[0] instanceof GString) { + tuple[0] = tuple[0].toString() + } + assert tuple[0] instanceof CharSequence : + "Error in module '${key_}': first element of tuple in channel should be a String\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Found: ${tuple[0]}" + + // match file to input file + if (workflowArgs.auto.simplifyInput && (tuple[1] instanceof Path || tuple[1] instanceof List)) { + def inputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + + assert inputFiles.size() == 1 : + "Error in module '${key_}' id '${tuple[0]}'.\n" + + " Anonymous file inputs are only allowed when the process has exactly one file input.\n" + + " Expected: inputFiles.size() == 1. Found: inputFiles.size() is ${inputFiles.size()}" + + tuple[1] = [[ inputFiles[0].plainName, tuple[1] ]].collectEntries() + } + + // check data field + assert tuple[1] instanceof Map : + "Error in module '${key_}' id '${tuple[0]}': second element of tuple in channel should be a Map\n" + + " Example: [\"id\", [input: file('foo.txt'), arg: 10]].\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // rename keys of data field in tuple + if (workflowArgs.renameKeys) { + assert workflowArgs.renameKeys instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class: Map. Found: renameKeys.getClass() is ${workflowArgs.renameKeys.getClass()}" + assert tuple[1] instanceof Map : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Expected class: Map. Found: tuple[1].getClass() is ${tuple[1].getClass()}" + + // TODO: allow renameKeys to be a function? + workflowArgs.renameKeys.each { newKey, oldKey -> + assert newKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of newKey: String. Found: newKey.getClass() is ${newKey.getClass()}" + assert oldKey instanceof CharSequence : + "Error renaming data keys in module '${key_}' id '${tuple[0]}'.\n" + + " Example: renameKeys: ['new_key': 'old_key'].\n" + + " Expected class of oldKey: String. Found: oldKey.getClass() is ${oldKey.getClass()}" + assert tuple[1].containsKey(oldKey) : + "Error renaming data keys in module '${key}' id '${tuple[0]}'.\n" + + " Key '$oldKey' is missing in the data map. tuple[1].keySet() is '${tuple[1].keySet()}'" + tuple[1].put(newKey, tuple[1][oldKey]) + } + tuple[1].keySet().removeAll(workflowArgs.renameKeys.collect{ newKey, oldKey -> oldKey }) + } + tuple + } + + def chModifiedFiltered = workflowArgs.filter ? + chModified | filter{workflowArgs.filter(it)} : + chModified + + def chRun = null + def chPassthrough = null + if (workflowArgs.runIf) { + def runIfBranch = chModifiedFiltered.branch{ tup -> + run: workflowArgs.runIf(tup[0], tup[1]) + passthrough: true + } + chRun = runIfBranch.run + chPassthrough = runIfBranch.passthrough + } else { + chRun = chModifiedFiltered + chPassthrough = Channel.empty() + } + + def chArgs = workflowArgs.fromState ? + chRun | map{ + def new_data = workflowArgs.fromState(it.take(2)) + [it[0], new_data] + } : + chRun | map {tup -> tup.take(2)} + + // fill in defaults + def chArgsWithDefaults = chArgs + | map { tuple -> + def id_ = tuple[0] + def data_ = tuple[1] + + // TODO: could move fromState to here + + // fetch default params from functionality + def defaultArgs = meta.config.allArguments + .findAll { it.containsKey("default") } + .collectEntries { [ it.plainName, it.default ] } + + // fetch overrides in params + def paramArgs = meta.config.allArguments + .findAll { par -> + def argKey = key_ + "__" + par.plainName + params.containsKey(argKey) + } + .collectEntries { [ it.plainName, params[key_ + "__" + it.plainName] ] } + + // fetch overrides in data + def dataArgs = meta.config.allArguments + .findAll { data_.containsKey(it.plainName) } + .collectEntries { [ it.plainName, data_[it.plainName] ] } + + // combine params + def combinedArgs = defaultArgs + paramArgs + workflowArgs.args + dataArgs + + // remove arguments with explicit null values + combinedArgs + .removeAll{_, val -> val == null || val == "viash_no_value" || val == "force_null"} + + combinedArgs = _processInputValues(combinedArgs, meta.config, id_, key_) + + [id_, combinedArgs] + tuple.drop(2) + } + + // TODO: move some of the _meta.join_id wrangling to the safeJoin() function. + def chInitialOutput = chArgsWithDefaults + | _debug(workflowArgs, "processed") + // run workflow + | innerWorkflowFactory(workflowArgs) + // check output tuple + | map { id_, output_ -> + + // see if output map contains metadata + def meta_ = + output_ instanceof Map && output_.containsKey("_meta") ? + output_["_meta"] : + [:] + def join_id = meta_.join_id ?: id_ + + // remove metadata + output_ = output_.findAll{k, v -> k != "_meta"} + + // check value types + output_ = _processOutputValues(output_, meta.config, id_, key_) + + // simplify output if need be + if (workflowArgs.auto.simplifyOutput && output_.size() == 1) { + output_ = output_.values()[0] + } + + [join_id, id_, output_] + } + // | view{"chInitialOutput: ${it.take(3)}"} + + // join the output [prev_id, new_id, output] with the previous state [prev_id, state, ...] + def chNewState = safeJoin(chInitialOutput, chModifiedFiltered, key_) + // input tuple format: [join_id, id, output, prev_state, ...] + // output tuple format: [join_id, id, new_state, ...] + | map{ tup -> + def new_state = workflowArgs.toState(tup.drop(1).take(3)) + tup.take(2) + [new_state] + tup.drop(4) + } + + if (workflowArgs.auto.publish == "state") { + def chPublish = chNewState + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [join_id, id, new_state] + | map{ tup -> + tup.take(3) + } + + safeJoin(chPublish, chArgsWithDefaults, key_) + // input tuple format: [join_id, id, new_state, orig_state, ...] + // output tuple format: [id, new_state, orig_state] + | map { tup -> + tup.drop(1).take(3) + } + | publishStatesByConfig(key: key_, config: meta.config) + } + + // remove join_id and meta + chReturn = chNewState + | map { tup -> + // input tuple format: [join_id, id, new_state, ...] + // output tuple format: [id, new_state, ...] + tup.drop(1) + } + | _debug(workflowArgs, "output") + | concat(chPassthrough) + + emit: chReturn + } + + def wf = workflowInstance.cloneWithName(key_) + + // add factory function + wf.metaClass.run = { runArgs -> + workflowFactory(runArgs, workflowArgs, meta) + } + // add config to module for later introspection + wf.metaClass.config = meta.config + + return wf +} + +nextflow.enable.dsl=2 + +// START COMPONENT-SPECIFIC CODE + +// create meta object +meta = [ + "resources_dir": moduleDir.toRealPath().normalize(), + "config": processConfig(readJsonBlob('''{ + "name" : "star_genome_generate", + "namespace" : "star", + "version" : "main", + "argument_groups" : [ + { + "name" : "Input", + "arguments" : [ + { + "type" : "file", + "name" : "--genomeFastaFiles", + "description" : "Path(s) to the fasta files with the genome sequences, separated by spaces. These files should be plain text FASTA files, they *cannot* be zipped.\n", + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--sjdbGTFfile", + "description" : "Path to the GTF file with annotations", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--sjdbOverhang", + "description" : "Length of the donor/acceptor sequence on each side of the junctions, ideally = (mate_length - 1)", + "example" : [ + 100 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFchrPrefix", + "description" : "Prefix for chromosome names in a GTF file (e.g. 'chr' for using ENSMEBL annotations with UCSC genomes)", + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFfeatureExon", + "description" : "Feature type in GTF file to be used as exons for building transcripts", + "example" : [ + "exon" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFtagExonParentTranscript", + "description" : "GTF attribute name for parent transcript ID (default \\"transcript_id\\" works for GTF files)", + "example" : [ + "transcript_id" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFtagExonParentGene", + "description" : "GTF attribute name for parent gene ID (default \\"gene_id\\" works for GTF files)", + "example" : [ + "gene_id" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFtagExonParentGeneName", + "description" : "GTF attribute name for parent gene name", + "example" : [ + "gene_name" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--sjdbGTFtagExonParentGeneType", + "description" : "GTF attribute name for parent gene type", + "example" : [ + "gene_type", + "gene_biotype" + ], + "required" : false, + "direction" : "input", + "multiple" : true, + "multiple_sep" : ";" + }, + { + "type" : "long", + "name" : "--limitGenomeGenerateRAM", + "description" : "Maximum available RAM (bytes) for genome generation", + "example" : [ + 31000000000 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--genomeSAindexNbases", + "description" : "Length (bases) of the SA pre-indexing string. Typically between 10 and 15. Longer strings will use much more memory, but allow faster searches. For small genomes, this parameter must be scaled down to min(14, log2(GenomeLength)/2 - 1).", + "example" : [ + 14 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--genomeChrBinNbits", + "description" : "Defined as log2(chrBin), where chrBin is the size of the bins for genome storage. Each chromosome will occupy an integer number of bins. For a genome with large number of contigs, it is recommended to scale this parameter as min(18, log2[max(GenomeLength/NumberOfReferences,ReadLength)]).", + "example" : [ + 18 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--genomeSAsparseD", + "description" : "Suffux array sparsity, i.e. distance between indices. Use bigger numbers to decrease needed RAM at the cost of mapping speed reduction.", + "example" : [ + 1 + ], + "required" : false, + "min" : 0, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "integer", + "name" : "--genomeSuffixLengthMax", + "description" : "Maximum length of the suffixes, has to be longer than read length. Use -1 for infinite length.", + "example" : [ + -1 + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "string", + "name" : "--genomeTransformType", + "description" : "Type of genome transformation\n None ... no transformation\n Haploid ... replace reference alleles with alternative alleles from VCF file (e.g. consensus allele)\n Diploid ... create two haplotypes for each chromosome listed in VCF file, for genotypes 1|2, assumes perfect phasing (e.g. personal genome)\n", + "example" : [ + "None" + ], + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + }, + { + "type" : "file", + "name" : "--genomeTransformVCF", + "description" : "path to VCF file for genome transformation", + "must_exist" : true, + "create_parent" : true, + "required" : false, + "direction" : "input", + "multiple" : false, + "multiple_sep" : ";" + } + ] + }, + { + "name" : "Output", + "arguments" : [ + { + "type" : "file", + "name" : "--index", + "description" : "STAR index directory.", + "default" : [ + "STAR_index" + ], + "must_exist" : true, + "create_parent" : true, + "required" : true, + "direction" : "output", + "multiple" : false, + "multiple_sep" : ";" + } + ] + } + ], + "resources" : [ + { + "type" : "bash_script", + "path" : "script.sh", + "is_executable" : true + } + ], + "description" : "Create index for STAR\n", + "test_resources" : [ + { + "type" : "bash_script", + "path" : "test.sh", + "is_executable" : true + } + ], + "status" : "enabled", + "requirements" : { + "commands" : [ + "ps" + ] + }, + "keywords" : [ + "genome", + "index", + "align" + ], + "license" : "MIT", + "references" : { + "doi" : [ + "10.1093/bioinformatics/bts635" + ] + }, + "links" : { + "repository" : "https://github.com/alexdobin/STAR", + "documentation" : "https://github.com/alexdobin/STAR/blob/master/doc/STARmanual.pdf" + }, + "runners" : [ + { + "type" : "executable", + "id" : "executable", + "docker_setup_strategy" : "ifneedbepullelsecachedbuild" + }, + { + "type" : "nextflow", + "id" : "nextflow", + "directives" : { + "tag" : "$id" + }, + "auto" : { + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false + }, + "config" : { + "labels" : { + "mem1gb" : "memory = 1000000000.B", + "mem2gb" : "memory = 2000000000.B", + "mem5gb" : "memory = 5000000000.B", + "mem10gb" : "memory = 10000000000.B", + "mem20gb" : "memory = 20000000000.B", + "mem50gb" : "memory = 50000000000.B", + "mem100gb" : "memory = 100000000000.B", + "mem200gb" : "memory = 200000000000.B", + "mem500gb" : "memory = 500000000000.B", + "mem1tb" : "memory = 1000000000000.B", + "mem2tb" : "memory = 2000000000000.B", + "mem5tb" : "memory = 5000000000000.B", + "mem10tb" : "memory = 10000000000000.B", + "mem20tb" : "memory = 20000000000000.B", + "mem50tb" : "memory = 50000000000000.B", + "mem100tb" : "memory = 100000000000000.B", + "mem200tb" : "memory = 200000000000000.B", + "mem500tb" : "memory = 500000000000000.B", + "mem1gib" : "memory = 1073741824.B", + "mem2gib" : "memory = 2147483648.B", + "mem4gib" : "memory = 4294967296.B", + "mem8gib" : "memory = 8589934592.B", + "mem16gib" : "memory = 17179869184.B", + "mem32gib" : "memory = 34359738368.B", + "mem64gib" : "memory = 68719476736.B", + "mem128gib" : "memory = 137438953472.B", + "mem256gib" : "memory = 274877906944.B", + "mem512gib" : "memory = 549755813888.B", + "mem1tib" : "memory = 1099511627776.B", + "mem2tib" : "memory = 2199023255552.B", + "mem4tib" : "memory = 4398046511104.B", + "mem8tib" : "memory = 8796093022208.B", + "mem16tib" : "memory = 17592186044416.B", + "mem32tib" : "memory = 35184372088832.B", + "mem64tib" : "memory = 70368744177664.B", + "mem128tib" : "memory = 140737488355328.B", + "mem256tib" : "memory = 281474976710656.B", + "mem512tib" : "memory = 562949953421312.B", + "cpu1" : "cpus = 1", + "cpu2" : "cpus = 2", + "cpu5" : "cpus = 5", + "cpu10" : "cpus = 10", + "cpu20" : "cpus = 20", + "cpu50" : "cpus = 50", + "cpu100" : "cpus = 100", + "cpu200" : "cpus = 200", + "cpu500" : "cpus = 500", + "cpu1000" : "cpus = 1000" + } + }, + "debug" : false, + "container" : "docker" + } + ], + "engines" : [ + { + "type" : "docker", + "id" : "docker", + "image" : "ubuntu:22.04", + "target_registry" : "images.viash-hub.com", + "target_tag" : "main", + "namespace_separator" : "/", + "setup" : [ + { + "type" : "docker", + "run" : [ + "apt-get update && \\\\\n apt-get install -y --no-install-recommends ${PACKAGES} && \\\\\n cd /tmp && \\\\\n wget --no-check-certificate https://github.com/alexdobin/STAR/archive/refs/tags/${STAR_VERSION}.zip && \\\\\n unzip ${STAR_VERSION}.zip && \\\\\n cd STAR-${STAR_VERSION}/source && \\\\\n make STARstatic CXXFLAGS_SIMD=-std=c++11 && \\\\\n cp STAR /usr/local/bin && \\\\\n cd / && \\\\\n rm -rf /tmp/STAR-${STAR_VERSION} /tmp/${STAR_VERSION}.zip && \\\\\n apt-get --purge autoremove -y ${PACKAGES} && \\\\\n apt-get clean\n" + ], + "env" : [ + "STAR_VERSION 2.7.11b", + "PACKAGES gcc g++ make wget zlib1g-dev unzip xxd" + ] + }, + { + "type" : "docker", + "run" : [ + "STAR --version | sed 's#\\\\(.*\\\\)#star: \\"\\\\1\\"#' > /var/software_versions.txt\n" + ] + } + ] + }, + { + "type" : "native", + "id" : "native" + } + ], + "build_info" : { + "config" : "/workdir/root/repo/src/star/star_genome_generate/config.vsh.yaml", + "runner" : "nextflow", + "engine" : "docker|native", + "output" : "target/nextflow/star/star_genome_generate", + "viash_version" : "0.9.0-RC6", + "git_commit" : "d0c648fb7eefe067f5b5b3d402a204354bb37198", + "git_remote" : "https://github.com/viash-hub/biobox" + }, + "package_config" : { + "name" : "biobox", + "version" : "main", + "description" : "A collection of bioinformatics tools for working with sequence data.\n", + "viash_version" : "0.9.0-RC6", + "source" : "src", + "target" : "target", + "config_mods" : [ + ".requirements.commands := ['ps']\n", + ".engines += { type: \\"native\\" }", + ".engines[.type == 'docker'].target_registry := 'images.viash-hub.com'", + ".engines[.type == 'docker'].target_tag := 'main'" + ], + "keywords" : [ + "bioinformatics", + "modules", + "sequencing" + ], + "license" : "MIT", + "organization" : "vsh", + "links" : { + "repository" : "https://github.com/viash-hub/biobox", + "issue_tracker" : "https://github.com/viash-hub/biobox/issues" + } + } +}''')) +] + +// resolve dependencies dependencies (if any) + + +// inner workflow +// inner workflow hook +def innerWorkflowFactory(args) { + def rawScript = '''set -e +tempscript=".viash_script.sh" +cat > "$tempscript" << VIASHMAIN +#!/bin/bash + +set -e + +## VIASH START +# The following code has been auto-generated by Viash. +$( if [ ! -z ${VIASH_PAR_GENOMEFASTAFILES+x} ]; then echo "${VIASH_PAR_GENOMEFASTAFILES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_genomeFastaFiles='&'#" ; else echo "# par_genomeFastaFiles="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFFILE+x} ]; then echo "${VIASH_PAR_SJDBGTFFILE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sjdbGTFfile='&'#" ; else echo "# par_sjdbGTFfile="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBOVERHANG+x} ]; then echo "${VIASH_PAR_SJDBOVERHANG}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sjdbOverhang='&'#" ; else echo "# par_sjdbOverhang="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFCHRPREFIX+x} ]; then echo "${VIASH_PAR_SJDBGTFCHRPREFIX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sjdbGTFchrPrefix='&'#" ; else echo "# par_sjdbGTFchrPrefix="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFFEATUREEXON+x} ]; then echo "${VIASH_PAR_SJDBGTFFEATUREEXON}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sjdbGTFfeatureExon='&'#" ; else echo "# par_sjdbGTFfeatureExon="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT+x} ]; then echo "${VIASH_PAR_SJDBGTFTAGEXONPARENTTRANSCRIPT}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sjdbGTFtagExonParentTranscript='&'#" ; else echo "# par_sjdbGTFtagExonParentTranscript="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENE+x} ]; then echo "${VIASH_PAR_SJDBGTFTAGEXONPARENTGENE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sjdbGTFtagExonParentGene='&'#" ; else echo "# par_sjdbGTFtagExonParentGene="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME+x} ]; then echo "${VIASH_PAR_SJDBGTFTAGEXONPARENTGENENAME}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sjdbGTFtagExonParentGeneName='&'#" ; else echo "# par_sjdbGTFtagExonParentGeneName="; fi ) +$( if [ ! -z ${VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE+x} ]; then echo "${VIASH_PAR_SJDBGTFTAGEXONPARENTGENETYPE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_sjdbGTFtagExonParentGeneType='&'#" ; else echo "# par_sjdbGTFtagExonParentGeneType="; fi ) +$( if [ ! -z ${VIASH_PAR_LIMITGENOMEGENERATERAM+x} ]; then echo "${VIASH_PAR_LIMITGENOMEGENERATERAM}" | sed "s#'#'\\"'\\"'#g;s#.*#par_limitGenomeGenerateRAM='&'#" ; else echo "# par_limitGenomeGenerateRAM="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMESAINDEXNBASES+x} ]; then echo "${VIASH_PAR_GENOMESAINDEXNBASES}" | sed "s#'#'\\"'\\"'#g;s#.*#par_genomeSAindexNbases='&'#" ; else echo "# par_genomeSAindexNbases="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMECHRBINNBITS+x} ]; then echo "${VIASH_PAR_GENOMECHRBINNBITS}" | sed "s#'#'\\"'\\"'#g;s#.*#par_genomeChrBinNbits='&'#" ; else echo "# par_genomeChrBinNbits="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMESASPARSED+x} ]; then echo "${VIASH_PAR_GENOMESASPARSED}" | sed "s#'#'\\"'\\"'#g;s#.*#par_genomeSAsparseD='&'#" ; else echo "# par_genomeSAsparseD="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMESUFFIXLENGTHMAX+x} ]; then echo "${VIASH_PAR_GENOMESUFFIXLENGTHMAX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_genomeSuffixLengthMax='&'#" ; else echo "# par_genomeSuffixLengthMax="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMETRANSFORMTYPE+x} ]; then echo "${VIASH_PAR_GENOMETRANSFORMTYPE}" | sed "s#'#'\\"'\\"'#g;s#.*#par_genomeTransformType='&'#" ; else echo "# par_genomeTransformType="; fi ) +$( if [ ! -z ${VIASH_PAR_GENOMETRANSFORMVCF+x} ]; then echo "${VIASH_PAR_GENOMETRANSFORMVCF}" | sed "s#'#'\\"'\\"'#g;s#.*#par_genomeTransformVCF='&'#" ; else echo "# par_genomeTransformVCF="; fi ) +$( if [ ! -z ${VIASH_PAR_INDEX+x} ]; then echo "${VIASH_PAR_INDEX}" | sed "s#'#'\\"'\\"'#g;s#.*#par_index='&'#" ; else echo "# par_index="; fi ) +$( if [ ! -z ${VIASH_META_NAME+x} ]; then echo "${VIASH_META_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_name='&'#" ; else echo "# meta_name="; fi ) +$( if [ ! -z ${VIASH_META_FUNCTIONALITY_NAME+x} ]; then echo "${VIASH_META_FUNCTIONALITY_NAME}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_functionality_name='&'#" ; else echo "# meta_functionality_name="; fi ) +$( if [ ! -z ${VIASH_META_RESOURCES_DIR+x} ]; then echo "${VIASH_META_RESOURCES_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_resources_dir='&'#" ; else echo "# meta_resources_dir="; fi ) +$( if [ ! -z ${VIASH_META_EXECUTABLE+x} ]; then echo "${VIASH_META_EXECUTABLE}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_executable='&'#" ; else echo "# meta_executable="; fi ) +$( if [ ! -z ${VIASH_META_CONFIG+x} ]; then echo "${VIASH_META_CONFIG}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_config='&'#" ; else echo "# meta_config="; fi ) +$( if [ ! -z ${VIASH_META_TEMP_DIR+x} ]; then echo "${VIASH_META_TEMP_DIR}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_temp_dir='&'#" ; else echo "# meta_temp_dir="; fi ) +$( if [ ! -z ${VIASH_META_CPUS+x} ]; then echo "${VIASH_META_CPUS}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_cpus='&'#" ; else echo "# meta_cpus="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_B+x} ]; then echo "${VIASH_META_MEMORY_B}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_b='&'#" ; else echo "# meta_memory_b="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KB+x} ]; then echo "${VIASH_META_MEMORY_KB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kb='&'#" ; else echo "# meta_memory_kb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MB+x} ]; then echo "${VIASH_META_MEMORY_MB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mb='&'#" ; else echo "# meta_memory_mb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GB+x} ]; then echo "${VIASH_META_MEMORY_GB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gb='&'#" ; else echo "# meta_memory_gb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TB+x} ]; then echo "${VIASH_META_MEMORY_TB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tb='&'#" ; else echo "# meta_memory_tb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PB+x} ]; then echo "${VIASH_META_MEMORY_PB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pb='&'#" ; else echo "# meta_memory_pb="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_KIB+x} ]; then echo "${VIASH_META_MEMORY_KIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_kib='&'#" ; else echo "# meta_memory_kib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_MIB+x} ]; then echo "${VIASH_META_MEMORY_MIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_mib='&'#" ; else echo "# meta_memory_mib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_GIB+x} ]; then echo "${VIASH_META_MEMORY_GIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_gib='&'#" ; else echo "# meta_memory_gib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_TIB+x} ]; then echo "${VIASH_META_MEMORY_TIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_tib='&'#" ; else echo "# meta_memory_tib="; fi ) +$( if [ ! -z ${VIASH_META_MEMORY_PIB+x} ]; then echo "${VIASH_META_MEMORY_PIB}" | sed "s#'#'\\"'\\"'#g;s#.*#meta_memory_pib='&'#" ; else echo "# meta_memory_pib="; fi ) + +## VIASH END + +mkdir -p \\$par_index + +STAR \\\\ + --runMode genomeGenerate \\\\ + --genomeDir \\$par_index \\\\ + --genomeFastaFiles \\$par_genomeFastaFiles \\\\ + \\${meta_cpus:+--runThreadN "\\${meta_cpus}"} \\\\ + \\${par_sjdbGTFfile:+--sjdbGTFfile "\\${par_sjdbGTFfile}"} \\\\ + \\${par_sjdbOverhang:+--sjdbOverhang "\\${par_sjdbOverhang}"} \\\\ + \\${par_genomeSAindexNbases:+--genomeSAindexNbases "\\${par_genomeSAindexNbases}"} \\\\ + \\${par_sjdbGTFchrPrefix:+--sjdbGTFchrPrefix "\\${par_sjdbGTFchrPrefix}"} \\\\ + \\${par_sjdbGTFfeatureExon:+--sjdbGTFfeatureExon "\\${par_sjdbGTFfeatureExon}"} \\\\ + \\${par_sjdbGTFtagExonParentTranscript:+--sjdbGTFtagExonParentTranscript "\\${par_sjdbGTFtagExonParentTranscript}"} \\\\ + \\${par_sjdbGTFtagExonParentGene:+--sjdbGTFtagExonParentGene "\\${par_sjdbGTFtagExonParentGene}"} \\\\ + \\${par_sjdbGTFtagExonParentGeneName:+--sjdbGTFtagExonParentGeneName "\\${par_sjdbGTFtagExonParentGeneName}"} \\\\ + \\${par_sjdbGTFtagExonParentGeneType:+--sjdbGTFtagExonParentGeneType "\\${sjdbGTFtagExonParentGeneType}"} \\\\ + \\${par_limitGenomeGenerateRAM:+--limitGenomeGenerateRAM "\\${par_limitGenomeGenerateRAM}"} \\\\ + \\${par_genomeChrBinNbits:+--genomeChrBinNbits "\\${par_genomeChrBinNbits}"} \\\\ + \\${par_genomeSAsparseD:+--genomeSAsparseD "\\${par_genomeSAsparseD}"} \\\\ + \\${par_genomeSuffixLengthMax:+--genomeSuffixLengthMax "\\${par_genomeSuffixLengthMax}"} \\\\ + \\${par_genomeTransformType:+--genomeTransformType "\\${par_genomeTransformType}"} \\\\ + \\${par_genomeTransformVCF:+--genomeTransformVCF "\\${par_genomeTransformVCF}"} \\\\ +VIASHMAIN +bash "$tempscript" +''' + + return vdsl3WorkflowFactory(args, meta, rawScript) +} + + + +/** + * Generate a workflow for VDSL3 modules. + * + * This function is called by the workflowFactory() function. + * + * Input channel: [id, input_map] + * Output channel: [id, output_map] + * + * Internally, this workflow will convert the input channel + * to a format which the Nextflow module will be able to handle. + */ +def vdsl3WorkflowFactory(Map args, Map meta, String rawScript) { + def key = args["key"] + def processObj = null + + workflow processWf { + take: input_ + main: + + if (processObj == null) { + processObj = _vdsl3ProcessFactory(args, meta, rawScript) + } + + output_ = input_ + | map { tuple -> + def id = tuple[0] + def data_ = tuple[1] + + if (workflow.stubRun) { + // add id if missing + data_ = [id: 'stub'] + data_ + } + + // process input files separately + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { par -> + def val = data_.containsKey(par.plainName) ? data_[par.plainName] : [] + def inputFiles = [] + if (val == null) { + inputFiles = [] + } else if (val instanceof List) { + inputFiles = val + } else if (val instanceof Path) { + inputFiles = [ val ] + } else { + inputFiles = [] + } + if (!workflow.stubRun) { + // throw error when an input file doesn't exist + inputFiles.each{ file -> + assert file.exists() : + "Error in module '${key}' id '${id}' argument '${par.plainName}'.\n" + + " Required input file does not exist.\n" + + " Path: '$file'.\n" + + " Expected input file to exist" + } + } + inputFiles + } + + // remove input files + def argsExclInputFiles = meta.config.allArguments + .findAll { (it.type != "file" || it.direction != "input") && data_.containsKey(it.plainName) } + .collectEntries { par -> + def parName = par.plainName + def val = data_[parName] + if (par.multiple && val instanceof Collection) { + val = val.join(par.multiple_sep) + } + if (par.direction == "output" && par.type == "file") { + val = val.replaceAll('\\$id', id).replaceAll('\\$key', key) + } + [parName, val] + } + + [ id ] + inputPaths + [ argsExclInputFiles, meta.resources_dir ] + } + | processObj + | map { output -> + def outputFiles = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .indexed() + .collectEntries{ index, par -> + def out = output[index + 1] + // strip dummy '.exitcode' file from output (see nextflow-io/nextflow#2678) + if (!out instanceof List || out.size() <= 1) { + if (par.multiple) { + out = [] + } else { + assert !par.required : + "Error in module '${key}' id '${output[0]}' argument '${par.plainName}'.\n" + + " Required output file is missing" + out = null + } + } else if (out.size() == 2 && !par.multiple) { + out = out[1] + } else { + out = out.drop(1) + } + [ par.plainName, out ] + } + + // drop null outputs + outputFiles.removeAll{it.value == null} + + [ output[0], outputFiles ] + } + emit: output_ + } + + return processWf +} + +// depends on: session? +def _vdsl3ProcessFactory(Map workflowArgs, Map meta, String rawScript) { + // autodetect process key + def wfKey = workflowArgs["key"] + def procKeyPrefix = "${wfKey}_process" + def scriptMeta = nextflow.script.ScriptMeta.current() + def existing = scriptMeta.getProcessNames().findAll{it.startsWith(procKeyPrefix)} + def numbers = existing.collect{it.replace(procKeyPrefix, "0").toInteger()} + def newNumber = (numbers + [-1]).max() + 1 + + def procKey = newNumber == 0 ? procKeyPrefix : "$procKeyPrefix$newNumber" + + if (newNumber > 0) { + log.warn "Key for module '${wfKey}' is duplicated.\n", + "If you run a component multiple times in the same workflow,\n" + + "it's recommended you set a unique key for every call,\n" + + "for example: ${wfKey}.run(key: \"foo\")." + } + + // subset directives and convert to list of tuples + def drctv = workflowArgs.directives + + // TODO: unit test the two commands below + // convert publish array into tags + def valueToStr = { val -> + // ignore closures + if (val instanceof CharSequence) { + if (!val.matches('^[{].*[}]$')) { + '"' + val + '"' + } else { + val + } + } else if (val instanceof List) { + "[" + val.collect{valueToStr(it)}.join(", ") + "]" + } else if (val instanceof Map) { + "[" + val.collect{k, v -> k + ": " + valueToStr(v)}.join(", ") + "]" + } else { + val.inspect() + } + } + + // multiple entries allowed: label, publishdir + def drctvStrs = drctv.collect { key, value -> + if (key in ["label", "publishDir"]) { + value.collect{ val -> + if (val instanceof Map) { + "\n$key " + val.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else if (val == null) { + "" + } else { + "\n$key " + valueToStr(val) + } + }.join() + } else if (value instanceof Map) { + "\n$key " + value.collect{ k, v -> k + ": " + valueToStr(v) }.join(", ") + } else { + "\n$key " + valueToStr(value) + } + }.join() + + def inputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "input" } + .collect { ', path(viash_par_' + it.plainName + ', stageAs: "_viash_par/' + it.plainName + '_?/*")' } + .join() + + def outputPaths = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + // insert dummy into every output (see nextflow-io/nextflow#2678) + if (!par.multiple) { + ', path{[".exitcode", args.' + par.plainName + ']}' + } else { + ', path{[".exitcode"] + args.' + par.plainName + '}' + } + } + .join() + + // TODO: move this functionality somewhere else? + if (workflowArgs.auto.transcript) { + outputPaths = outputPaths + ', path{[".exitcode", ".command*"]}' + } else { + outputPaths = outputPaths + ', path{[".exitcode"]}' + } + + // create dirs for output files (based on BashWrapper.createParentFiles) + def createParentStr = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" && it.create_parent } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"mkdir_parent \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"] : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // construct inputFileExports + def inputFileExports = meta.config.allArguments + .findAll { it.type == "file" && it.direction.toLowerCase() == "input" } + .collect { par -> + def viash_par_contents = "(viash_par_${par.plainName} instanceof List ? viash_par_${par.plainName}.join(\"${par.multiple_sep}\") : viash_par_${par.plainName})" + "\n\${viash_par_${par.plainName}.empty ? \"\" : \"export VIASH_PAR_${par.plainName.toUpperCase()}=\\\"\" + ${viash_par_contents} + \"\\\"\"}" + } + + // NOTE: if using docker, use /tmp instead of tmpDir! + def tmpDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('VIASH_TMPDIR') ?: + System.getenv('VIASH_TEMPDIR') ?: + System.getenv('VIASH_TMP') ?: + System.getenv('TEMP') ?: + System.getenv('TMPDIR') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMP') ?: + '/tmp' + ).toAbsolutePath() + + // construct stub + def stub = meta.config.allArguments + .findAll { it.type == "file" && it.direction == "output" } + .collect { par -> + "\${ args.containsKey(\"${par.plainName}\") ? \"touch2 \\\"\" + (args[\"${par.plainName}\"] instanceof String ? args[\"${par.plainName}\"].replace(\"_*\", \"_0\") : args[\"${par.plainName}\"].join('\" \"')) + \"\\\"\" : \"\" }" + } + .join("\n") + + // escape script + def escapedScript = rawScript.replace('\\', '\\\\').replace('$', '\\$').replace('"""', '\\"\\"\\"') + + // publishdir assert + def assertStr = (workflowArgs.auto.publish == true) || workflowArgs.auto.transcript ? + """\nassert task.publishDir.size() > 0: "if auto.publish is true, params.publish_dir needs to be defined.\\n Example: --publish_dir './output/'" """ : + "" + + // generate process string + def procStr = + """nextflow.enable.dsl=2 + | + |process $procKey {$drctvStrs + |input: + | tuple val(id)$inputPaths, val(args), path(resourcesDir, stageAs: ".viash_meta_resources") + |output: + | tuple val("\$id")$outputPaths, optional: true + |stub: + |\"\"\" + |touch2() { mkdir -p "\\\$(dirname "\\\$1")" && touch "\\\$1" ; } + |$stub + |\"\"\" + |script:$assertStr + |def escapeText = { s -> s.toString().replaceAll('([`"])', '\\\\\\\\\$1') } + |def parInject = args + | .findAll{key, value -> value != null} + | .collect{key, value -> "export VIASH_PAR_\${key.toUpperCase()}=\\\"\${escapeText(value)}\\\""} + | .join("\\n") + |\"\"\" + |# meta exports + |export VIASH_META_RESOURCES_DIR="\${resourcesDir}" + |export VIASH_META_TEMP_DIR="${['docker', 'podman', 'charliecloud'].any{ it == workflow.containerEngine } ? '/tmp' : tmpDir}" + |export VIASH_META_NAME="${meta.config.name}" + |# export VIASH_META_EXECUTABLE="\\\$VIASH_META_RESOURCES_DIR/\\\$VIASH_META_NAME" + |export VIASH_META_CONFIG="\\\$VIASH_META_RESOURCES_DIR/.config.vsh.yaml" + |\${task.cpus ? "export VIASH_META_CPUS=\$task.cpus" : "" } + |\${task.memory?.bytes != null ? "export VIASH_META_MEMORY_B=\$task.memory.bytes" : "" } + |if [ ! -z \\\${VIASH_META_MEMORY_B+x} ]; then + | export VIASH_META_MEMORY_KB=\\\$(( (\\\$VIASH_META_MEMORY_B+999) / 1000 )) + | export VIASH_META_MEMORY_MB=\\\$(( (\\\$VIASH_META_MEMORY_KB+999) / 1000 )) + | export VIASH_META_MEMORY_GB=\\\$(( (\\\$VIASH_META_MEMORY_MB+999) / 1000 )) + | export VIASH_META_MEMORY_TB=\\\$(( (\\\$VIASH_META_MEMORY_GB+999) / 1000 )) + | export VIASH_META_MEMORY_PB=\\\$(( (\\\$VIASH_META_MEMORY_TB+999) / 1000 )) + | export VIASH_META_MEMORY_KIB=\\\$(( (\\\$VIASH_META_MEMORY_B+1023) / 1024 )) + | export VIASH_META_MEMORY_MIB=\\\$(( (\\\$VIASH_META_MEMORY_KIB+1023) / 1024 )) + | export VIASH_META_MEMORY_GIB=\\\$(( (\\\$VIASH_META_MEMORY_MIB+1023) / 1024 )) + | export VIASH_META_MEMORY_TIB=\\\$(( (\\\$VIASH_META_MEMORY_GIB+1023) / 1024 )) + | export VIASH_META_MEMORY_PIB=\\\$(( (\\\$VIASH_META_MEMORY_TIB+1023) / 1024 )) + |fi + | + |# meta synonyms + |export VIASH_TEMP="\\\$VIASH_META_TEMP_DIR" + |export TEMP_DIR="\\\$VIASH_META_TEMP_DIR" + | + |# create output dirs if need be + |function mkdir_parent { + | for file in "\\\$@"; do + | mkdir -p "\\\$(dirname "\\\$file")" + | done + |} + |$createParentStr + | + |# argument exports${inputFileExports.join()} + |\$parInject + | + |# process script + |${escapedScript} + |\"\"\" + |} + |""".stripMargin() + + // TODO: print on debug + // if (workflowArgs.debug == true) { + // println("######################\n$procStr\n######################") + // } + + // write process to temp file + def tempFile = java.nio.file.Files.createTempFile("viash-process-${procKey}-", ".nf") + addShutdownHook { java.nio.file.Files.deleteIfExists(tempFile) } + tempFile.text = procStr + + // create process from temp file + def binding = new nextflow.script.ScriptBinding([:]) + def session = nextflow.Nextflow.getSession() + def parser = new nextflow.script.ScriptParser(session) + .setModule(true) + .setBinding(binding) + def moduleScript = parser.runScript(tempFile) + .getScript() + + // register module in meta + def module = new nextflow.script.IncludeDef.Module(name: procKey) + scriptMeta.addModule(moduleScript, module.name, module.alias) + + // retrieve and return process from meta + return scriptMeta.getProcess(procKey) +} + +// defaults +meta["defaults"] = [ + // key to be used to trace the process and determine output names + key: null, + + // fixed arguments to be passed to script + args: [:], + + // default directives + directives: readJsonBlob('''{ + "container" : { + "registry" : "images.viash-hub.com", + "image" : "vsh/biobox/star/star_genome_generate", + "tag" : "main" + }, + "tag" : "$id" +}'''), + + // auto settings + auto: readJsonBlob('''{ + "simplifyInput" : true, + "simplifyOutput" : false, + "transcript" : false, + "publish" : false +}'''), + + // Apply a map over the incoming tuple + // Example: `{ tup -> [ tup[0], [input: tup[1].output] ] + tup.drop(2) }` + map: null, + + // Apply a map over the ID element of a tuple (i.e. the first element) + // Example: `{ id -> id + "_foo" }` + mapId: null, + + // Apply a map over the data element of a tuple (i.e. the second element) + // Example: `{ data -> [ input: data.output ] }` + mapData: null, + + // Apply a map over the passthrough elements of a tuple (i.e. the tuple excl. the first two elements) + // Example: `{ pt -> pt.drop(1) }` + mapPassthrough: null, + + // Filter the channel + // Example: `{ tup -> tup[0] == "foo" }` + filter: null, + + // Choose whether or not to run the component on the tuple if the condition is true. + // Otherwise, the tuple will be passed through. + // Example: `{ tup -> tup[0] != "skip_this" }` + runIf: null, + + // Rename keys in the data field of the tuple (i.e. the second element) + // Will likely be deprecated in favour of `fromState`. + // Example: `[ "new_key": "old_key" ]` + renameKeys: null, + + // Fetch data from the state and pass it to the module without altering the current state. + // + // `fromState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be passed to the module as is. + // - If it is a `List[String]`, the data will be the values of the state at the given keys. + // - If it is a `Map[String, String]`, the data will be the values of the state at the given keys, with the keys renamed according to the map. + // - If it is a function, the tuple (`[id, state]`) in the channel will be passed to the function, and the result will be used as the data. + // + // Example: `{ id, state -> [input: state.fastq_file] }` + // Default: `null` + fromState: null, + + // Determine how the state should be updated after the module has been run. + // + // `toState` should be `null`, `List[String]`, `Map[String, String]` or a function. + // + // - If it is `null`, the state will be replaced with the output of the module. + // - If it is a `List[String]`, the state will be updated with the values of the data at the given keys. + // - If it is a `Map[String, String]`, the state will be updated with the values of the data at the given keys, with the keys renamed according to the map. + // - If it is a function, a tuple (`[id, output, state]`) will be passed to the function, and the result will be used as the new state. + // + // Example: `{ id, output, state -> state + [counts: state.output] }` + // Default: `{ id, output, state -> output }` + toState: null, + + // Whether or not to print debug messages + // Default: `false` + debug: false +] + +// initialise default workflow +meta["workflow"] = workflowFactory([key: meta.config.name], meta.defaults, meta) + +// add workflow to environment +nextflow.script.ScriptMeta.current().addDefinition(meta.workflow) + +// anonymous workflow for running this module as a standalone +workflow { + // add id argument if it's not already in the config + // TODO: deep copy + def newConfig = deepClone(meta.config) + def newParams = deepClone(params) + + def argsContainsId = newConfig.allArguments.any{it.plainName == "id"} + if (!argsContainsId) { + def idArg = [ + 'name': '--id', + 'required': false, + 'type': 'string', + 'description': 'A unique id for every entry.', + 'multiple': false + ] + newConfig.arguments.add(0, idArg) + newConfig = processConfig(newConfig) + } + if (!newParams.containsKey("id")) { + newParams.id = "run" + } + + helpMessage(newConfig) + + channelFromParams(newParams, newConfig) + // make sure id is not in the state if id is not in the args + | map {id, state -> + if (!argsContainsId) { + [id, state.findAll{k, v -> k != "id"}] + } else { + [id, state] + } + } + | meta.workflow.run( + auto: [ publish: "state" ] + ) +} + +// END COMPONENT-SPECIFIC CODE diff --git a/target/nextflow/star/star_genome_generate/nextflow.config b/target/nextflow/star/star_genome_generate/nextflow.config new file mode 100644 index 00000000..57af2210 --- /dev/null +++ b/target/nextflow/star/star_genome_generate/nextflow.config @@ -0,0 +1,125 @@ +manifest { + name = 'star/star_genome_generate' + mainScript = 'main.nf' + nextflowVersion = '!>=20.12.1-edge' + version = 'main' + description = 'Create index for STAR\n' +} + +process.container = 'nextflow/bash:latest' + +// detect tempdir +tempDir = java.nio.file.Paths.get( + System.getenv('NXF_TEMP') ?: + System.getenv('VIASH_TEMP') ?: + System.getenv('TEMPDIR') ?: + System.getenv('TMPDIR') ?: + '/tmp' +).toAbsolutePath() + +profiles { + no_publish { + process { + withName: '.*' { + publishDir = [ + enabled: false + ] + } + } + } + mount_temp { + docker.temp = tempDir + podman.temp = tempDir + charliecloud.temp = tempDir + } + docker { + docker.enabled = true + // docker.userEmulation = true + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + singularity { + singularity.enabled = true + singularity.autoMounts = true + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + podman { + podman.enabled = true + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + } + shifter { + shifter.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + } + charliecloud { + charliecloud.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + } +} + +process{ + withLabel: mem1gb { memory = 1000000000.B } + withLabel: mem2gb { memory = 2000000000.B } + withLabel: mem5gb { memory = 5000000000.B } + withLabel: mem10gb { memory = 10000000000.B } + withLabel: mem20gb { memory = 20000000000.B } + withLabel: mem50gb { memory = 50000000000.B } + withLabel: mem100gb { memory = 100000000000.B } + withLabel: mem200gb { memory = 200000000000.B } + withLabel: mem500gb { memory = 500000000000.B } + withLabel: mem1tb { memory = 1000000000000.B } + withLabel: mem2tb { memory = 2000000000000.B } + withLabel: mem5tb { memory = 5000000000000.B } + withLabel: mem10tb { memory = 10000000000000.B } + withLabel: mem20tb { memory = 20000000000000.B } + withLabel: mem50tb { memory = 50000000000000.B } + withLabel: mem100tb { memory = 100000000000000.B } + withLabel: mem200tb { memory = 200000000000000.B } + withLabel: mem500tb { memory = 500000000000000.B } + withLabel: mem1gib { memory = 1073741824.B } + withLabel: mem2gib { memory = 2147483648.B } + withLabel: mem4gib { memory = 4294967296.B } + withLabel: mem8gib { memory = 8589934592.B } + withLabel: mem16gib { memory = 17179869184.B } + withLabel: mem32gib { memory = 34359738368.B } + withLabel: mem64gib { memory = 68719476736.B } + withLabel: mem128gib { memory = 137438953472.B } + withLabel: mem256gib { memory = 274877906944.B } + withLabel: mem512gib { memory = 549755813888.B } + withLabel: mem1tib { memory = 1099511627776.B } + withLabel: mem2tib { memory = 2199023255552.B } + withLabel: mem4tib { memory = 4398046511104.B } + withLabel: mem8tib { memory = 8796093022208.B } + withLabel: mem16tib { memory = 17592186044416.B } + withLabel: mem32tib { memory = 35184372088832.B } + withLabel: mem64tib { memory = 70368744177664.B } + withLabel: mem128tib { memory = 140737488355328.B } + withLabel: mem256tib { memory = 281474976710656.B } + withLabel: mem512tib { memory = 562949953421312.B } + withLabel: cpu1 { cpus = 1 } + withLabel: cpu2 { cpus = 2 } + withLabel: cpu5 { cpus = 5 } + withLabel: cpu10 { cpus = 10 } + withLabel: cpu20 { cpus = 20 } + withLabel: cpu50 { cpus = 50 } + withLabel: cpu100 { cpus = 100 } + withLabel: cpu200 { cpus = 200 } + withLabel: cpu500 { cpus = 500 } + withLabel: cpu1000 { cpus = 1000 } +} + + diff --git a/target/nextflow/star/star_genome_generate/nextflow_schema.json b/target/nextflow/star/star_genome_generate/nextflow_schema.json new file mode 100644 index 00000000..cef80fdf --- /dev/null +++ b/target/nextflow/star/star_genome_generate/nextflow_schema.json @@ -0,0 +1,245 @@ +{ +"$schema": "http://json-schema.org/draft-07/schema", +"title": "star_genome_generate", +"description": "Create index for STAR\n", +"type": "object", +"definitions": { + + + + "input" : { + "title": "Input", + "type": "object", + "description": "No description", + "properties": { + + + "genomeFastaFiles": { + "type": + "string", + "description": "Type: List of `file`, required, multiple_sep: `\";\"`. Path(s) to the fasta files with the genome sequences, separated by spaces", + "help_text": "Type: List of `file`, required, multiple_sep: `\";\"`. Path(s) to the fasta files with the genome sequences, separated by spaces. These files should be plain text FASTA files, they *cannot* be zipped.\n" + + } + + + , + "sjdbGTFfile": { + "type": + "string", + "description": "Type: `file`. Path to the GTF file with annotations", + "help_text": "Type: `file`. Path to the GTF file with annotations" + + } + + + , + "sjdbOverhang": { + "type": + "integer", + "description": "Type: `integer`, example: `100`. Length of the donor/acceptor sequence on each side of the junctions, ideally = (mate_length - 1)", + "help_text": "Type: `integer`, example: `100`. Length of the donor/acceptor sequence on each side of the junctions, ideally = (mate_length - 1)" + + } + + + , + "sjdbGTFchrPrefix": { + "type": + "string", + "description": "Type: `string`. Prefix for chromosome names in a GTF file (e", + "help_text": "Type: `string`. Prefix for chromosome names in a GTF file (e.g. \u0027chr\u0027 for using ENSMEBL annotations with UCSC genomes)" + + } + + + , + "sjdbGTFfeatureExon": { + "type": + "string", + "description": "Type: `string`, example: `exon`. Feature type in GTF file to be used as exons for building transcripts", + "help_text": "Type: `string`, example: `exon`. Feature type in GTF file to be used as exons for building transcripts" + + } + + + , + "sjdbGTFtagExonParentTranscript": { + "type": + "string", + "description": "Type: `string`, example: `transcript_id`. GTF attribute name for parent transcript ID (default \"transcript_id\" works for GTF files)", + "help_text": "Type: `string`, example: `transcript_id`. GTF attribute name for parent transcript ID (default \"transcript_id\" works for GTF files)" + + } + + + , + "sjdbGTFtagExonParentGene": { + "type": + "string", + "description": "Type: `string`, example: `gene_id`. GTF attribute name for parent gene ID (default \"gene_id\" works for GTF files)", + "help_text": "Type: `string`, example: `gene_id`. GTF attribute name for parent gene ID (default \"gene_id\" works for GTF files)" + + } + + + , + "sjdbGTFtagExonParentGeneName": { + "type": + "string", + "description": "Type: List of `string`, example: `gene_name`, multiple_sep: `\";\"`. GTF attribute name for parent gene name", + "help_text": "Type: List of `string`, example: `gene_name`, multiple_sep: `\";\"`. GTF attribute name for parent gene name" + + } + + + , + "sjdbGTFtagExonParentGeneType": { + "type": + "string", + "description": "Type: List of `string`, example: `gene_type;gene_biotype`, multiple_sep: `\";\"`. GTF attribute name for parent gene type", + "help_text": "Type: List of `string`, example: `gene_type;gene_biotype`, multiple_sep: `\";\"`. GTF attribute name for parent gene type" + + } + + + , + "limitGenomeGenerateRAM": { + "type": + "string", + "description": "Type: `long`, example: `31000000000`. Maximum available RAM (bytes) for genome generation", + "help_text": "Type: `long`, example: `31000000000`. Maximum available RAM (bytes) for genome generation" + + } + + + , + "genomeSAindexNbases": { + "type": + "integer", + "description": "Type: `integer`, example: `14`. Length (bases) of the SA pre-indexing string", + "help_text": "Type: `integer`, example: `14`. Length (bases) of the SA pre-indexing string. Typically between 10 and 15. Longer strings will use much more memory, but allow faster searches. For small genomes, this parameter must be scaled down to min(14, log2(GenomeLength)/2 - 1)." + + } + + + , + "genomeChrBinNbits": { + "type": + "integer", + "description": "Type: `integer`, example: `18`. Defined as log2(chrBin), where chrBin is the size of the bins for genome storage", + "help_text": "Type: `integer`, example: `18`. Defined as log2(chrBin), where chrBin is the size of the bins for genome storage. Each chromosome will occupy an integer number of bins. For a genome with large number of contigs, it is recommended to scale this parameter as min(18, log2[max(GenomeLength/NumberOfReferences,ReadLength)])." + + } + + + , + "genomeSAsparseD": { + "type": + "integer", + "description": "Type: `integer`, example: `1`. Suffux array sparsity, i", + "help_text": "Type: `integer`, example: `1`. Suffux array sparsity, i.e. distance between indices. Use bigger numbers to decrease needed RAM at the cost of mapping speed reduction." + + } + + + , + "genomeSuffixLengthMax": { + "type": + "integer", + "description": "Type: `integer`, example: `-1`. Maximum length of the suffixes, has to be longer than read length", + "help_text": "Type: `integer`, example: `-1`. Maximum length of the suffixes, has to be longer than read length. Use -1 for infinite length." + + } + + + , + "genomeTransformType": { + "type": + "string", + "description": "Type: `string`, example: `None`. Type of genome transformation\n None ", + "help_text": "Type: `string`, example: `None`. Type of genome transformation\n None ... no transformation\n Haploid ... replace reference alleles with alternative alleles from VCF file (e.g. consensus allele)\n Diploid ... create two haplotypes for each chromosome listed in VCF file, for genotypes 1|2, assumes perfect phasing (e.g. personal genome)\n" + + } + + + , + "genomeTransformVCF": { + "type": + "string", + "description": "Type: `file`. path to VCF file for genome transformation", + "help_text": "Type: `file`. path to VCF file for genome transformation" + + } + + +} +}, + + + "output" : { + "title": "Output", + "type": "object", + "description": "No description", + "properties": { + + + "index": { + "type": + "string", + "description": "Type: `file`, required, default: `$id.$key.index.index`. STAR index directory", + "help_text": "Type: `file`, required, default: `$id.$key.index.index`. STAR index directory." + , + "default": "$id.$key.index.index" + } + + +} +}, + + + "nextflow input-output arguments" : { + "title": "Nextflow input-output arguments", + "type": "object", + "description": "Input/output parameters for Nextflow itself. Please note that both publishDir and publish_dir are supported but at least one has to be configured.", + "properties": { + + + "publish_dir": { + "type": + "string", + "description": "Type: `string`, required, example: `output/`. Path to an output directory", + "help_text": "Type: `string`, required, example: `output/`. Path to an output directory." + + } + + + , + "param_list": { + "type": + "string", + "description": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel", + "help_text": "Type: `string`, example: `my_params.yaml`. Allows inputting multiple parameter sets to initialise a Nextflow channel. A `param_list` can either be a list of maps, a csv file, a json file, a yaml file, or simply a yaml blob.\n\n* A list of maps (as-is) where the keys of each map corresponds to the arguments of the pipeline. Example: in a `nextflow.config` file: `param_list: [ [\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027], [\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027] ]`.\n* A csv file should have column names which correspond to the different arguments of this pipeline. Example: `--param_list data.csv` with columns `id,input`.\n* A json or a yaml file should be a list of maps, each of which has keys corresponding to the arguments of the pipeline. Example: `--param_list data.json` with contents `[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]`.\n* A yaml blob can also be passed directly as a string. Example: `--param_list \"[ {\u0027id\u0027: \u0027foo\u0027, \u0027input\u0027: \u0027foo.txt\u0027}, {\u0027id\u0027: \u0027bar\u0027, \u0027input\u0027: \u0027bar.txt\u0027} ]\"`.\n\nWhen passing a csv, json or yaml file, relative path names are relativized to the location of the parameter file. No relativation is performed when `param_list` is a list of maps (as-is) or a yaml blob.", + "hidden": true + + } + + +} +} +}, +"allOf": [ + + { + "$ref": "#/definitions/input" + }, + + { + "$ref": "#/definitions/output" + }, + + { + "$ref": "#/definitions/nextflow input-output arguments" + } +] +}